Android圆角Tag控件的另类实现
2015-08-07 11:50
615 查看
一般的圆角标签控件都是用xml设置shape做实现。但是如果我们想要做一个更加强大通用的的圆角控件,不需要使用者去关心圆角,只设置背景就可以了。应该怎么实现呢?这个就需要把背景先设置成图片,然后再把这个图片处理成圆角的,最后再设置成背景,基本思路如下代码:
现在问题来了,如果我背景是ShapDrawable(xml:shape)或者设置的StateListDrawable(selector)呢?这就没办法了,ShapeDrawable还可以做自定义,但是StateListDrawable开放的能够自定义的接口很少,完全没办法。
此外,如果我们需要设置drawableLeft或者drawableRight,也会掩盖住背景,圆角也就没了。如果也对这些compound drawable也设置圆角。而且圆角的半径还不能和主空间的半径一样,否则会由于宽高不同,画出来的圆角也会不一样。这个方案太复杂。放弃!
有没有什么简单的办法呢?我们可以用Xfermode来对画板的底图做文章。
这里就要说到Android(估计其他OS也是差不多)控件的实现方式了。
我们在显示屏看到的所有东西,其实都是一块内存,放在一块叫做framebuffer的内存缓冲区里面。这块Buffer以像素点为单位,用一定的色彩规则,给我们排列出了各种看到的屏幕上的东西。Android通过一个叫Surface的系统来管理这一块FrameBuffer,所谓的控件,其实就是按照一定的规则,告诉Surface系统怎么画我这一块区域。这样就能够给我们详细的画出我们想要看到的画面了。关于细节,东西太多,以后再慢慢说。继续说到实现圆角控件的问题来。
Android里面所有的控件,都给我们提供了一个叫onDraw()的方法,参数为Canvas,Canvas就是画布的意思,我们在这块布上面画的东西,就是空间的长相了。这个方法会在View.draw()里面调用下来,告诉系统怎么画这个控件。比如TextView里面的onDraw就基本是一些写字,设置颜色,字体的操作,ImageView就是用Drawable来画canvas的操作。
Android提供了各种各样的方法,方便我们对这块Canvas进行操作。我们想要实现圆角,是不是可以对这块Canvas做点手脚呢?我们可以Xfermode来画一个椭圆形 的Bitmap在原图上面,然后只保留他们相交的这一部分,去掉不相交的部分,圆角控件不就大功告成了?关于Xfermode,有一经典的图深动形象的说明了用法:
我们想要做的就如下图:
生成掩盖图的代码如下:
但是这样并不OK啊。用这个控件却是这样:
为什么会这样呢?圆角是有了,但是为嘛周围是是黑色啊。感觉代码是没问题的呀,后来去网上搜了下,XferMode在开启了硬件加速的情况下有一些局限性(http://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported),估计这个问题也是局限性之一。于是在本widget关闭了硬件加速,重新build后,发现还是没有变化。我就有点晕了,为嘛不是其他颜色,偏偏是黑色啊,而且我的maskBitmap也是设置的白色呀。解释只能为一个了:我的Background没有设置alpha值,没有任何的透明度,导致删除的颜色变为了无颜色状态。我来设置一个带有透明度的背景色试试:
可以了耶。这样,以后无论这个控件做了什么,画出来周围肯定是圆角的。
建议:对于这种需求的建议是建立一个Factory,用来生成一些定义好的Tag控件。这样更加的符合Android规范。也能够很好的管理各种Tag控件。
代码链接:http://download.csdn.net/detail/yutao52shi/8972539
Bitmap bitmap = ((BitmapDrawable)getBackground()).getBitmap(); BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); Matrix matrix = new Matrix(); bitmapShader.setLocalMatrix(matrix); // 设置缩放 float scale = Math.max(getWidth() * 1.0f / bmp.getWidth(), getHeight() * 1.0f / bmp.getHeight()); mBitmapPaint.setShader(bitmapShader); Canvas canvas = new Canvas(bitmap); if (mRadius > 0) { canvas.drawRoundRect(mDrawableRect, mRadius, mRadius, mBitmapPaint); } else { canvas.drawRect(mDrawableRect, mBitmapPaint); } setBackground(new BitmapDrawable(bitmap));上面的代码把背景图取出来后,用着色器画圆角(如果背景色是color,需要做一次ColorDrawable转Bitmap的转换)。这样就能够强制实现一个圆角背景的TextView了。你也可以使用Xfermode来做,网上文章很多,不做赘述了。
现在问题来了,如果我背景是ShapDrawable(xml:shape)或者设置的StateListDrawable(selector)呢?这就没办法了,ShapeDrawable还可以做自定义,但是StateListDrawable开放的能够自定义的接口很少,完全没办法。
此外,如果我们需要设置drawableLeft或者drawableRight,也会掩盖住背景,圆角也就没了。如果也对这些compound drawable也设置圆角。而且圆角的半径还不能和主空间的半径一样,否则会由于宽高不同,画出来的圆角也会不一样。这个方案太复杂。放弃!
有没有什么简单的办法呢?我们可以用Xfermode来对画板的底图做文章。
这里就要说到Android(估计其他OS也是差不多)控件的实现方式了。
我们在显示屏看到的所有东西,其实都是一块内存,放在一块叫做framebuffer的内存缓冲区里面。这块Buffer以像素点为单位,用一定的色彩规则,给我们排列出了各种看到的屏幕上的东西。Android通过一个叫Surface的系统来管理这一块FrameBuffer,所谓的控件,其实就是按照一定的规则,告诉Surface系统怎么画我这一块区域。这样就能够给我们详细的画出我们想要看到的画面了。关于细节,东西太多,以后再慢慢说。继续说到实现圆角控件的问题来。
Android里面所有的控件,都给我们提供了一个叫onDraw()的方法,参数为Canvas,Canvas就是画布的意思,我们在这块布上面画的东西,就是空间的长相了。这个方法会在View.draw()里面调用下来,告诉系统怎么画这个控件。比如TextView里面的onDraw就基本是一些写字,设置颜色,字体的操作,ImageView就是用Drawable来画canvas的操作。
Android提供了各种各样的方法,方便我们对这块Canvas进行操作。我们想要实现圆角,是不是可以对这块Canvas做点手脚呢?我们可以Xfermode来画一个椭圆形 的Bitmap在原图上面,然后只保留他们相交的这一部分,去掉不相交的部分,圆角控件不就大功告成了?关于Xfermode,有一经典的图深动形象的说明了用法:
我们想要做的就如下图:
生成掩盖图的代码如下:
private Bitmap generateMaskBitmap() { Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas bitmapCanvas = new Canvas(bitmap); RectF r = new RectF(0, 0, getWidth(), getHeight()); Rect rect = new Rect(0, 0, getWidth(), getHeight()); Paint bitmapPaint = new Paint(); bitmapPaint.setAlpha(0); bitmapPaint.setColor(Color.TRANSPARENT); bitmapCanvas.drawRect(rect, bitmapPaint); bitmapPaint.reset(); bitmapPaint.setStyle(Paint.Style.FILL); bitmapPaint.setAntiAlias(true); bitmapPaint.setColor(Color.WHITE); bitmapCanvas.drawRoundRect(r, mRadius, mRadius, bitmapPaint); return bitmap; }onDraw代码如下:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mRadius > 0) { Bitmap bitmap = generateMaskBitmap(); Paint bitmapPaint = new Paint(); bitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); canvas.drawBitmap(bitmap, 0f, 0f, bitmapPaint); bitmap.recycle(); } }
但是这样并不OK啊。用这个控件却是这样:
<com.example.widget.TagTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#ff0000" android:text="哎哟不错" app:border_radius="5dp" />
为什么会这样呢?圆角是有了,但是为嘛周围是是黑色啊。感觉代码是没问题的呀,后来去网上搜了下,XferMode在开启了硬件加速的情况下有一些局限性(http://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported),估计这个问题也是局限性之一。于是在本widget关闭了硬件加速,重新build后,发现还是没有变化。我就有点晕了,为嘛不是其他颜色,偏偏是黑色啊,而且我的maskBitmap也是设置的白色呀。解释只能为一个了:我的Background没有设置alpha值,没有任何的透明度,导致删除的颜色变为了无颜色状态。我来设置一个带有透明度的背景色试试:
<com.example.widget.TagTextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#feff0000" android:text="哎哟不错" app:border_radius="5dp" />
可以了耶。这样,以后无论这个控件做了什么,画出来周围肯定是圆角的。
总结
这个虽然实现了强制性的圆角,但是很有局限性,如果View的Canvas本身没有透明通道,被清除的圆角处就会变成黑色。我有尝试事先画一个透明的Color在控件上,但是仍然不管用。具体原因还不知道。还没来得及深入的去看。如果有哥们知道,不妨分享一下原因。建议:对于这种需求的建议是建立一个Factory,用来生成一些定义好的Tag控件。这样更加的符合Android规范。也能够很好的管理各种Tag控件。
代码链接:http://download.csdn.net/detail/yutao52shi/8972539
相关文章推荐
- win8.1 64位环境建设android开发环境
- android 5.0新特性学习总结之阴影特效(二)
- Android KITKAT 以上实现沉浸式状态栏
- Android 中文字体的设置方法和使用技巧
- Android手机提示“未安装应用程序”
- Android enum的使用
- Android编译系统参考手册
- android 广播 实例
- binbinyang---android监听输入法弹起控制ScrollView滚到最顶端或最低端
- Android布局组件
- [Android] ImageView.ScaleType设置图解
- android 5.0新特性学习总结之下拉刷新(一)
- Android LayoutInflater详解
- Android中程序与Service交互的方式——交互方式
- Android应用:获取屏幕大小和密度
- Android Fragment 真正的完全解析(下)
- Android的图片压缩类ThumbnailUtils
- Android中的Shape使用总结
- android开源动画库nineoldandroids
- android ListView item 点击设置背景图片需要注意的问题