【问题标题】:Android Custom Shape ButtonAndroid 自定义形状按钮
【发布时间】:2012-12-01 10:27:09
【问题描述】:

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

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

请帮忙。谢谢你。

【问题讨论】:

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


【解决方案1】:

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

  1. 您呈现的自定义形状
  2. 按钮的右上角不应是可点击的

所以这是分三步解决的方法:

步骤 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>
    

第二步

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

  • 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 -->

第三步

在您的主要活动中,您为两个按钮和掩码定义点击事件,如下所示:

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

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

希望它以某种方式有所帮助:)

【讨论】:

  • 我会测试它:)。非常感谢。
  • 没问题。希望它能通过你的测试:)
  • @sromku 感谢您的代码。但是,有一个问题。如果图像中的黑色区域不是黑色而是透明的,那么无论背景是什么,都可以通过该透明区域看到它。在我的场景中,有一个图像而不是那个黑色背景。我该怎么做?
【解决方案2】:

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

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

【讨论】:

    【解决方案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>
    

    【讨论】:

    • 如果用户按下空白区域会发生什么?它会抓住这个事件吗?如果可以,你如何克服这个问题?
    • 它不会捕获空白区域事件
    • 刚刚测试了它(使用模拟器,因为很难准确地触摸正确的位置),你是不正确的 - 触摸空白区域仍然会触发 onClick(当然还有 onTouch)。问题是,如何避免它。
    • @GM Ramesh 谢谢。我试过你的代码。但我可以点击空白区域。我想避免触及空白区域。我该怎么办?
    • @ErhanDemirci,我已经编辑了答案,请检查一次....我添加了 android:useLevel = "true"
    【解决方案4】:

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

    <?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 半径值来制作你提到的按钮形状

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

          与其做所有这些更改,不如在按钮周围的部分使用框架布局,并用一些东西(圆形,如圆形按钮)遮住右上角的部分,并在该部分上不分配点击侦听器。这实际上隐藏了较低的框架(即您的原始按钮)并用非活动部分掩盖它。

          【讨论】:

            【解决方案8】:

            我尝试了@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;
            }
            

            【讨论】:

              猜你喜欢
              • 2016-08-16
              • 1970-01-01
              • 2018-03-12
              • 2016-07-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-08-19
              相关资源
              最近更新 更多