我可以通过编程方式增加按钮onclick-area吗?

时间:2010-06-01 11:42:51

标签: android button

有时候我的UI中有一个按钮,它很小,很难点击。到目前为止我的解决方案是在photoshop中的按钮周围添加透明边框。只是增加按钮上的填充不起作用,因为这也会拉伸图像。因为每次想要更改可点击表面时打开photoshop都有点大惊小怪,有没有办法以编程方式执行此操作?我尝试在按钮后面放置一个framelayout并使其可点击,但是按钮不会改变触摸的外观。当然我还可以在framelayout上添加一个ontouchlistener来改变按钮的外观,但是如果我有几个按钮那么就会有一些代码。

干杯,

8 个答案:

答案 0 :(得分:34)

我个人而言,我会使用TouchDelegate。这使您可以处理触摸目标,视觉视图可以作为两个不同的东西。非常方便...

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

答案 1 :(得分:28)

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

  1. 使用一个说明为LinearLayout的按钮环绕按钮,该按钮围绕按钮填充。
  2. 将相同的onclick添加到LinearLayout作为Button。
  3. 在Button中,将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>
    

答案 2 :(得分:12)

这是一个非常晚的“我也是”,但在谈到这个以及寻找解决方案的其他问题之后,我发现了一个简单,优雅的解决方案。

另一位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"是* .png文件中的几个按钮状态,在可绘制的* .xml文件中定义,如下所示:

<?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不同,您可以在单个视图中根据需要重复使用此按钮。没有额外的听众。它不会向您的层次结构中添加任何视图或组,并且您不会整天使用填充和边距。

简单。优雅。

答案 3 :(得分:2)

我认为您的解决方案是目前最好的解决方案,如果您不想深入了解某些Android内容并自行拦截所有的motionEvent和TouchEvents,那么您还需要触发按下的视图按钮自己等。

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

答案 4 :(得分:1)

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

谢谢。

答案 5 :(得分:0)

只需在布局中提供填充以代替边距

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

答案 6 :(得分:0)

我不推荐这样做,很少有人会觉得有用。

对我来说,这是最好的解决方案,因为TouchDelegate并非在所有View层次结构方案中都适用。

解决方案基于覆盖隐藏的

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;
}

答案 7 :(得分:-1)

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