真实的自定义按钮形状

7
给定任意形状(填充圆、星形、三角形、具有透明区域的位图等),我想知道是否可能(使用最新的Android API)了解用户是否已单击视图或其外部。
例如,如果我有一个圆形按钮,我想知道用户是否已单击圆形内部,而不是外部。
这是否可能?
如果不行,也许我可以轮询触摸事件的像素,如果它是透明的,则忽略它,如果它不是透明的,则将其处理为单击事件?
4个回答

8
ImageView image=(ImageView) findViewById(R.id.imageView1);
image.setOnTouchListener(this);
Bitmap bitmap = ((BitmapDrawable)image.getDrawable()).getBitmap();    

@Override
public boolean onTouch(View v, MotionEvent event) {
    // TODO Auto-generated method stub
    int pixel = bitmap.getPixel((int)event.getX(), (int)event.getY());
    int alphaValue=Color.alpha(pixel);
    return true;
}

通过这种方式,您可以获取所触摸像素的 alpha 值。现在,您可以轻松地检查所触摸的像素是否透明。


非常好的答案@Adnan Zahid。它运行得非常好。我已经在具有不同密度/分辨率的模拟器上进行了测试。非常感谢你,兄弟。 - JiTHiN
似乎不起作用,因为它没有正确采样位图。我认为这取决于密度。也许只有在使用wrap_content时才能正常工作。 - android developer
我已经发布了一段真正有效的代码,而且即使视图是imageView也没有关系。 - android developer
@AdnanZahid:它有效了,谢谢。但是如果我想要三个不同形状的按钮的alpha值,并在onTouch事件中处理它,该怎么办?有可能区分三个不同的按钮吗?您可以在此处查看我所说的图像的屏幕截图:https://dev59.com/hXbZa4cB1Zd3GeqPBxdQ - Ponting
@Ponting,如何制作一个显示每个按钮的掩码?为每个按钮设置不同颜色。这个掩码甚至不需要在视图上显示,只需要作为一个二维数组加载即可。 - android developer
显示剩余4条评论

2

我也想做类似的事情,最终我使用FrameLayout进行了调整。FrameLayout允许您在彼此之上添加多个视图。

添加一个FrameLayout,在其中可以添加一个“View”,并将其高度和宽度设置为match_parent。在View的顶部,添加您想要的按钮。

然后在您的代码中,获取View的引用并在其上设置onClickListener,以便每当用户触摸该View时,您都可以处理该事件。还要在其他按钮上设置单击监听器。

现在只需处理触摸事件即可。您现在将知道用户是否单击了按钮或其外部(用户已单击View)。

如果您想创建透明或半透明按钮,请参阅https://stackoverflow.com/a/11689915/1117338

希望这可以帮助到您。


1
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    FrameLayout root = (FrameLayout)findViewById(R.id.root);
    root.setOnTouchListener(new OnTouchListener() {         
        public boolean onTouch(View v, MotionEvent event) {
            if (v.getId() == ID){
                // Your code
            }
            return true;
        }
    });
}

1

好的,我已经找到了一个适用于任何类型视图的工作解决方案。

一些注意事项:

  • 可悲的是,它使用了与视图大小相同的位图,但仅用了极短的时间。

    之后,它会保持在可见区域内和可见区域外的位置。

  • 我可以通过创建一个具有标志的整数数组来使其更加内存友好。目前它是一个简单的布尔数组。

  • 我可以在JNI中检查位图的alpha值,避免同时拥有位图和数组的(短暂)时间。

  • 如果有人能帮助改进它,那将非常棒。

以下是代码:

public class MainActivity extends Activity
  {
  boolean[] _inVisibleAreaMap;
  private int _width,_height;

  @Override
  protected void onCreate(final Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final View view=findViewById(R.id.imageView1);
    view.setDrawingCacheEnabled(true);
    runJustBeforeBeingDrawn(view,new Runnable()
      {
        @Override
        public void run()
          {
          final Bitmap bitmap=view.getDrawingCache();
          _width=bitmap.getWidth();
          _height=bitmap.getHeight();
          _inVisibleAreaMap=new boolean[_width*_height];
          for(int y=0;y<_width;++y)
            for(int x=0;x<_height;++x)
              _inVisibleAreaMap[y*_width+x]=Color.alpha(bitmap.getPixel(x,y))!=0;
          view.setDrawingCacheEnabled(false);
          bitmap.recycle();
          }
      });
    view.setOnTouchListener(new OnTouchListener()
      {
        @Override
        public boolean onTouch(final View v,final MotionEvent event)
          {
          final int x=(int)event.getX(),y=(int)event.getY();
          boolean isIn=x>=0&&y>=0&&x<_width&&y<_height;
          // if inside bounding box , check if in the visibile area
          if(isIn)
            isIn=_inVisibleAreaMap[y*_width+x];
          if(isIn)
            Log.d("DEBUG","in");
          else Log.d("DEBUG","out");
          return true;
          }
      });
    }

  private static void runJustBeforeBeingDrawn(final View view,final Runnable runnable)
    {
    final ViewTreeObserver vto=view.getViewTreeObserver();
    final OnPreDrawListener preDrawListener=new OnPreDrawListener()
      {
        @Override
        public boolean onPreDraw()
          {
          runnable.run();
          final ViewTreeObserver vto=view.getViewTreeObserver();
          vto.removeOnPreDrawListener(this);
          return true;
          }
      };
    vto.addOnPreDrawListener(preDrawListener);
    }
  }

如果imageView的宽度和高度设置为wrap_content,并且它确实得到了所需的尺寸,那么你才可以使用Adnan Zahid solution,写法如下:

public class MainActivity extends Activity
  {
  private int _width,_height;

  @Override
  protected void onCreate(final Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ImageView image=(ImageView)findViewById(R.id.imageView1);
    final Bitmap bitmap=((BitmapDrawable)image.getDrawable()).getBitmap();
    _width=bitmap.getWidth();
    _height=bitmap.getHeight();
    image.setOnTouchListener(new OnTouchListener()
      {
        @Override
        public boolean onTouch(final View v,final MotionEvent event)
          {
          final int x=(int)event.getX(),y=(int)event.getY();
          boolean isIn=x>=0&&y>=0&&x<_width&&y<_height;
          if(isIn)
            {
            final int pixel=bitmap.getPixel((int)event.getX(),(int)event.getY());
            final int alphaValue=Color.alpha(pixel);
            isIn=alphaValue!=0;
            }
          if(isIn)
            Log.d("DEBUG","in");
          else Log.d("DEBUG","out");
          return true;
          }
      });
    }
  }

编辑:runJustBeforeBeingDrawn的替代方法:https://dev59.com/i2865IYBdhLWcg3whO3E#28136027


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接