【问题标题】:Can I increase a buttons onclick-area programmatically?我可以以编程方式增加按钮 onclick-area 吗?
【发布时间】:2010-06-01 11:42:50
【问题描述】:

有时我的 UI 中有一个按钮,它太小以至于难以点击。到目前为止,我的解决方案是在 Photoshop 中的按钮周围添加透明边框。仅仅增加按钮上的填充是行不通的,因为这也会拉伸图像。由于每次我想更改可点击的表面时打开 Photoshop 有点大惊小怪,有没有办法以编程方式做到这一点?我尝试在按钮后面放置一个框架布局并使其可点击,但是按钮不会改变触摸时的外观。当然,我也可以在 framelayout 上添加一个 ontouchlistener 来改变按钮的外观,但是如果我有几个这样的按钮,那就是相当多的代码了。

干杯,

【问题讨论】:

    标签: android button


    【解决方案1】:

    就我个人而言,我会使用 TouchDelegate。这使您可以将触摸目标和视觉视图边界作为两个不同的事物来处理。很方便...

    http://developer.android.com/reference/android/view/TouchDelegate.html

    【讨论】:

    • 感谢 GeorgeLawrence,您在 stackoverflow 上发表了一篇文章,正是我正在寻找的文章!
    • 这通常是解决触控区域问题的最佳方案。
    • 关于TouchDelegate和sn-p代码的解释:developer.android.com/training/gestures/viewgroup.html#delegate
    • TouchDelegate 是否仅适用于直接父级?考虑一下:我将按钮放在小于所需可触摸区域的 LinearLayout 中,因此我使用 LinearLayout 的父级作为 TouchDelegate 的“父级”。所以 Touchable 区域比 LinearLayout 大,但它适合最顶层的父级。 -> 那么可触摸区域仍然被裁剪,只有LinearLayout里面的部分是真正可触摸的。
    【解决方案2】:

    我刚刚找到了解决这个问题的好方法。

    1. 在按钮周围加上一个 LinearLayout,在按钮周围有填充。
    2. 在 LinearLayout 中添加与 Button 相同的 onclick。
    3. 在按钮中将duplicateParentState设置为true,当您在按钮外部但在LinearLayout内部单击时,按钮会突出显示。

      <LinearLayout
          android:layout_height="fill_parent"
          android:onClick="searchButtonClicked" 
          android:layout_centerVertical="true" 
          android:orientation="horizontal" 
          android:layout_width="wrap_content" 
          android:paddingRight="10dp" 
          android:paddingLeft="30dp">
          <Button 
              android:id="@+id/search_button"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:background="@drawable/toggle_button_selector"
              android:textColor="#fff"
              android:text="Search"
              android:focusable="true"
              android:textStyle="bold"
              android:onClick="searchButtonClicked" 
              android:layout_gravity="center_vertical" 
              android:duplicateParentState="true"/>
      </LinearLayout>
      

    【讨论】:

    • 这只适用于sortof。要使其完全工作,您需要设置按钮 setClickable(false) 否则单击按钮本身不会突出显示,因为它从此时未单击的父级获取其状态。使按钮不可点击后,无需在其上设置 onClick 侦听器。
    【解决方案3】:

    这是一个很晚的“我也是”,但是在解决这个问题和其他问题以寻找解决方案之后,我找到了一个简单、优雅的自己的解决方案。

    另一个question 抱怨他们图片的透明背景不可点击。如果这是一个问题,这似乎也可以解决。

    这是按钮:

    <ImageButton
        android:id="@+id/arrowUp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/arrow_up"
        android:background="@drawable/clear_button_background" />
    

    相关的行是最后两行。 "@drawable/arrow_up" 是在可绘制的 *.xml 文件中定义的 *.png 文件中的一些按钮状态,如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android" >
     <item android:state_pressed="true"
           android:drawable="@drawable/tri_up_blue" /> <!-- pressed -->
     <item android:state_selected="true"
           android:drawable="@drawable/tri_up_blue" /> <!-- selected -->
     <item android:state_focused="true"
           android:drawable="@drawable/tri_up_blue" /> <!-- focused -->
     <item android:drawable="@drawable/tri_up_green" /> <!-- default -->
    </selector>
    

    只是您的基本按钮。没什么特别的。而"@drawable/clear_button_background" 就是这样:

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"  
        android:shape="rectangle">
       <solid android:color="@android:color/transparent"/>
       <size android:width="20dp" android:height="30dp" />
    </shape>
    

    这里的高度和宽度是可点击区域,根据需要调整大小。您可以在单个视图中将其重复用于所需的任意数量的按钮,这与细节荒谬的 TouchDelegate 不同。没有额外的听众。它不会将任何视图或组添加到您的层次结构中,您也不会整天都在搞乱填充和边距。

    简单。优雅。

    【讨论】:

    • 如果您可以使用 ImageButton 是个好主意,但是许多按钮是基于文本的,这使得应用更加困难。
    • @greg7gkb 对于基于文本的按钮,您只需添加 padding。 (向基于图像的按钮添加内边距会拉伸图像,这就是人们寻找内边距以外的其他解决方案的原因。)
    【解决方案4】:

    我认为您的解决方案是目前最好的解决方案,如果您不想深入研究一些 android 的东西并自己拦截所有 motionEvent 和 TouchEvents,那么您还需要触发按下的视图自己按按钮等。

    只需创建一个带有可拉伸透明边框的nine patch image。通过这种方式,您可以更改图像的大小,而无需更改图像本身,并且您的按钮将增大或缩小,而实际显示的背景不会发生变化。

    【讨论】:

      【解决方案5】:

      另一个想法是制作新的透明图像并在其上放置图标,以便触摸区域更大,设计看起来更完美。 Check out the image

      谢谢。

      【讨论】:

        【解决方案6】:

        只需为布局提供填充以代替 Margin

            <ImageView
            android:id="@+id/back_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="25dip"
            android:src="@drawable/back_btn" />
        

        【讨论】:

        • 这将扩展视图的背景,这可能不是我们想要的。
        • 是的,这是正确的,但当布局中没有太多东西和按钮很少时,它会很有帮助。
        • 如果不想拉伸图片,padding是不够的,必须使用android:scaleType="centerInside"加上padding才能达到想要的效果。
        【解决方案7】:

        我所做的不是推荐的方式,很少有人会觉得它有用。

        对我来说,这是最好的解决方案,因为 TouchDelegate 并不适用于所有视图层次结构场景。

        解决方案是基于覆盖隐藏

        View class
        public boolean pointInView(float localX, float localY, float slop)
        

        代码如下:

           public boolean pointInView(float localX, float localY, float slop) {
            boolean res = ae_pointInView(localX, localY, slop, slop);
            if(!res) {
                if(viewIsClickableButToSmall(this)) {
                    //our view is clickable itself
                    float newSlopX = Math.max(slop, slop + minTouchableViewWidth - this.getWidth());
                    float newSlopY = Math.max(slop, slop + minTouchableViewHeight - this.getHeight());
                    if(ae_pointInView(localX, localY, newSlopX, newSlopY)) {
                        return true;
                    }
                }
        
                //the point is outside our view, now check for views with increased tap area that may extent beyond it
                int childCount = getChildCount();
                for(int i = 0; i < childCount;i++) {
                    View child = getChildAt(i);
                    if(child instanceof MyViewGroup) {
                        //this is our container that may also contain views with increased tap area
        
                        float[] newPoint = ae_transformPointToViewLocal(localX, localY, child);
                        if(((MyViewGroup) child).pointInView(newPoint[0], newPoint[1], slop)) {
                            return true;
                        }
                    }
                    else {
                        if(viewIsClickableButToSmall(child)) {
                            float[] newPoint = ae_transformPointToViewLocal(localX, localY, child);
                            float newSlopX = Math.max(slop, slop + minTouchableViewWidth - this.getWidth());
                            float newSlopY = Math.max(slop, slop + minTouchableViewHeight - this.getHeight());
                            if(ae_pointInView(newPoint[0], newPoint[1], newSlopX, newSlopY)) {
                                return true;
                            }
                        }
                    }
                }
                return false;
            }
            else
                return true;
        }
        
        private int minTouchableViewHeight, minTouchableViewWidth;
        private boolean viewIsClickableButToSmall(View view)
        {
            minTouchableViewHeight = GlobalHelper.dipToDevicePixels(40);
            minTouchableViewWidth = GlobalHelper.dipToDevicePixels(40);
        
            return (view.getHeight() < minTouchableViewHeight || view.getWidth() < minTouchableViewWidth) && view.hasOnClickListeners();
        }
        
        public boolean ae_pointInView(float localX, float localY, float slopX, float slopY) {
            boolean res = localX >= -slopX && localY >= -slopY && localX < ((getRight() - getLeft()) + slopX) &&
                    localY < ((getBottom() - getTop()) + slopY);
            return res;
        }
        
        private float[] tempPoint;
        public float[] ae_transformPointToViewLocal(float x, float y, View child) {
            if(tempPoint == null) {
                tempPoint = new float[2];
            }
            tempPoint[0] = x + getScrollX() - child.getLeft();
            tempPoint[1] = y + getScrollY() - child.getTop();
        
            return tempPoint;
        }
        

        【讨论】:

          【解决方案8】:

          也许您可以通过查看传递给onTouchEventMotionEvent 的X 和Y 坐标来做到这一点?

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2013-07-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多