Android自定义形状按钮

时间:2012-12-13 14:00:54

标签: android android-layout android-widget onclicklistener android-custom-view

如何在Android中制作自定义形状的可点击视图或按钮?

当我点击时,我想避免触及空白区域。

enter image description here

请帮忙。谢谢。

8 个答案:

答案 0 :(得分:38)

有趣的问题。我尝试了一些解决方案,这是我发现的,与你想要实现的结果相同。下面的解决方案解决了2个问题:

  1. 自定义形状
  2. 按钮的右上角不应该是可点击的
  3. 所以这是3个步骤的解决方案:

    第1步

    创建两个形状。

    • 按钮的第一个简单矩形形状: shape_button_beer.xml

      <?xml version="1.0" encoding="utf-8"?>
      <shape xmlns:android="http://schemas.android.com/apk/res/android" >
      
          <gradient
              android:angle="90"
              android:endColor="#C5D9F4"
              android:startColor="#DCE5FD" />
      
          <corners
              android:bottomLeftRadius="5dp"
              android:bottomRightRadius="5dp"
              android:topLeftRadius="5dp" >
          </corners>
      
      </shape>
      
    • 第二个形状用作按钮右上角的遮罩: shape_button_beer_mask.xml 。它是黑色纯色的简单圆圈。

      <?xml version="1.0" encoding="utf-8"?>
      <shape xmlns:android="http://schemas.android.com/apk/res/android"
          android:shape="oval" >
      
          <solid android:color="#000000" />
      
      </shape>
      

    第2步

    在您的主布局中,按下一种方法添加按钮:

    • RelativeLayout是此自定义按钮的容器
    • 第一个LinearLayout是蓝色按钮,里面有啤酒图标和文字
    • 第二个ImageView是蓝色按钮上方的遮罩。这里有肮脏的伎俩:
      1. 在正确的位置设置遮罩的边距为负
      2. 我们定义id可以在点击时覆盖(参见步骤3)
      3. android:soundEffectsEnabled="false" - 这样用户就不会觉得他按下了某些东西。

    XML:

        <!-- Custom Button -->
        <RelativeLayout
            android:layout_width="120dp"
            android:layout_height="80dp" >
    
            <LinearLayout
                android:id="@+id/custom_buttom"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:background="@drawable/shape_button_beer" >
    
                <!-- Beer icon and all other stuff -->
    
                <ImageView
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:layout_marginLeft="5dp"
                    android:layout_marginTop="15dp"
                    android:src="@drawable/beer_icon" />
            </LinearLayout>
    
            <ImageView
                android:id="@+id/do_nothing"
                android:layout_width="120dp"
                android:layout_height="100dp"
                android:layout_alignParentRight="true"
                android:layout_alignParentTop="true"
                android:layout_marginRight="-50dp"
                android:layout_marginTop="-50dp"
                android:background="@drawable/shape_button_beer_mask"
                android:soundEffectsEnabled="false" >
            </ImageView>
        </RelativeLayout>
        <!-- End Custom Button -->
    

    第3步

    在您的主要活动中,您为以下两者的点击事件定义:按钮和面具,如下所示:

    LinearLayout customButton = (LinearLayout) findViewById(R.id.custom_buttom);
    customButton.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(View arg0)
        {
            Toast.makeText(getApplicationContext(), "Clicked", Toast.LENGTH_SHORT).show();
        }
    });
    
    // Mask on click will do nothing
    ImageView doNothing = (ImageView) findViewById(R.id.do_nothing);
    doNothing.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(View arg0)
        {
            // DO NOTHING
        }
    });
    

    就是这样。我知道这不是一个完美的解决方案,但在您描述的用例中它可以提供帮助。 我已经在我的手机上测试了它,这就是当你点击蓝色区域时它的外观,其他区域什么也不会发生:

    • enter image description here

    希望它有所帮助:)

答案 1 :(得分:15)

使用OnTouch而不是OnClick并检查您在按钮中使用的图像的alpha值。如果它不等于零,请执行您想要的任何操作。 检查以下代码,

final Bitmap bitmap;  //Declare bitmap     
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.TheImage);


public boolean onTouch(View v, MotionEvent event) {

        int eventPadTouch = event.getAction();
        float iX=event.getX();
    float iY=event.getY();

        switch (eventPadTouch) {

            case MotionEvent.ACTION_DOWN:
                if (iX>=0 & iY>=0 & iX<bitmap.getWidth() & iY<bitmap.getHeight()) { //Makes sure that X and Y are not less than 0, and no more than the height and width of the image.                
                    if (bitmap.getPixel((int) iX, (int) iY)!=0) {
                        // actual image area is clicked(alpha not equal to 0), do something 
                    }               
                }
                return true;                
        }           
        return false;
}

答案 2 :(得分:3)

你可以尝试这个:

        <Button
        android:id="@+id/logout"
        android:layout_width="240dp"
        android:layout_height="28dp"
        android:layout_weight="1"
        android:gravity="center"
        android:text="ContactsDetails"
        android:textColor="#ffffff" android:layout_marginLeft="50dp" android:background="@drawable/round"/>

并在drawable文件夹中创建round.xml文件:

        <?xml version="1.0" encoding="utf-8"?>
      <shape xmlns:android="http://schemas.android.com/apk/res/android"
      android:shape="rectangle" android:padding="0dp" android:useLevel = "false">
    <!-- you can use any color you want I used here gray color-->
     <solid android:color="#ABABAB"/> 
       <corners
       android:bottomRightRadius="0dp"
         android:bottomLeftRadius="0dp"
       android:topLeftRadius="0dp"
      android:topRightRadius="70dp"/>
       </shape>

答案 3 :(得分:3)

使用图层列表,您可以设计任何形状的任何渐变按钮顶部 这是示例

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle">
            <corners

                android:topLeftRadius="0dp"
                android:topRightRadius="0dp"
                android:bottomLeftRadius="2dp"
                android:bottomRightRadius="15dp"
                />            
            <!-- The border color -->
            <solid android:color="#ffffff" />
        </shape>
    </item>

    <item android:right="2dp"
        android:left="2dp"
        android:bottom="2dp">
            <shape>            
            <gradient                
                android:startColor="#002a36"
                android:centerColor="#457c8e"
                android:endColor="#e6ffff"               
                android:angle="90" 
                  android:centerY="1"
                android:centerX="0.5"           
                />          

            <corners

                android:topLeftRadius="0dp"
                android:topRightRadius="0dp"
                android:bottomLeftRadius="2dp"
                android:bottomRightRadius="15dp"
                />            
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" 
                />                        
        </shape>
    </item>
</layer-list> 

使用-ve radius值来制作按照你提到的按钮形状

答案 4 :(得分:1)

我有类似的问题,但我不想依赖代码来检查像素值。我想要一种简单的方法(不是类重载)来将触摸事件约束到drawable的子部分。下面我使用LinearLayout作为drawable然后在里面我放了一个透明按钮(带文字)。我可以调整按钮的边距来定位可点击区域。

    <LinearLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:background="@drawable/circle">
        <Button
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            android:id="@+id/btnTimer1"
            android:text="0:00"
            android:textColor="#ffffff"
            android:textSize="22dp"
            android:layout_margin="20dp"
            android:background="@android:color/transparent"/>
    </LinearLayout>

答案 5 :(得分:0)

最好和最简单的解决方案(as4me)我找到here - it is subclassed Button,因此它支持选择器。所以你需要做的就是为每个按钮状态绘制/添加相应的png以使用选择器并在xml中声明onClick或在代码中添加OnClickListener,你就可以开始了。

答案 6 :(得分:0)

您应该在按钮周围的部分使用框架布局,而不是进行所有这些更改,并使用一些东西(圆形,如圆形按钮)屏蔽右上部分,并在该部分上不指定任何单击侦听器。这实际上隐藏了较低的框架(即原始按钮)并用非活动部件遮住它。

答案 7 :(得分:0)

我尝试了@Basim Sherif(link)的答案,但只有当按钮大小与原始图像相同时,它才能正常工作。如果按钮被拉伸,则可点击区域将变小,如果按钮设置为较小尺寸,则可点击区域将大于实际按钮。

解决方案很简单,即缩放iX和iY值以匹配原始位图。

这是我修改后的代码版本:

final Bitmap bitmap;  //Declare bitmap     
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.TheImage);

public boolean onTouch(View v, MotionEvent event) {

    int eventPadTouch = event.getAction();
    float iX=event.getX();
    float iY=event.getY();

    // Get the dimensions used in the view
    int realW = this.getWidth();
    int realH = this.getHeight();

    // Get the dimensions of the actual image
    int bitmapW = bitmap.getWidth();
    int bitmapH = bitmap.getHeight();

    // Scale the coordinates from the view to match the actual image
    float scaledX = iX * bitmapW / realW;
    float scaledY = iY * bitmapH / realH;

    switch (eventPadTouch) {
        case MotionEvent.ACTION_DOWN:
            if (scaledX >= 0 & scaledY >= 0 & scaledX < bitmap.getWidth() & scaledY < bitmap.getHeight()) { //Makes sure that X and Y are not less than 0, and no more than the height and width of the image.                
                if (bitmap.getPixel((int) scaledX, (int) scaledY)!=0) {
                    // actual image area is clicked(alpha not equal to 0), do something 
                }
            }
            return true;
    }
    return false;
}