Android圆角Tag控件的另类实现
一般的圆角标签控件都是用xml设置shape做实现。可是假设我们想要做一个更加强大通用的的圆角控件,不须要使用者去关心圆角,仅仅设置背景就能够了。
应该怎么实现呢?这个就须要把背景先设置成图片,然后再把这个图片处理成圆角的,最后再设置成背景。基本思路例如以下代码:
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" />
能够了耶。这样,以后不管这个控件做了什么,画出来周围肯定是圆角的。
总结
我有尝试事先画一个透明的Color在控件上,可是仍然无论用。详细原因还不知道。还没来得及深入的去看。假设有哥们知道。最好还是分享一下原因。
相关文章
- Android 开源库StickyListHeadersListView来实现ListView列表分组效果
- Android_(控件)使用ImageView简单实现图片翻转
- Android自定义控件----RadioGroup实现APP首页底部Tab的切换
- android自定义listview实现header悬浮框效果
- Android智能聊天机器人
- 如何在使用eclipse的情况下,清理android项目中的冗余class文件和资源文件以及冗余图片
- 转载—— android 瀑布流的实现详解,附源码
- android-------手写签名系统的设计与实现之实现画笔设置
- 腾讯Android七年老开发教你快速突击大厂面试,必知必会的知识点,高薪必备
- Android 7.1 修改副屏显示DPI
- Android 实现RecyclerView长按弹出Menu 点击删除
- 【Android笔记53】Android之实现应用程序中的画中画效果
- android app用百度ocr识别sdk实现手写扫描功能(二)
- Android 实现三种响铃模式的切换
- Android 自定义SeekBar 实现分段显示不同背景颜色
- Android 自定义写字板控件实现签名功能
- android 10.0实现通过系统属性控制挂载otg设备功能
- Android Listview 自定义实现局部刷新(非notifyDataSetChange())
- 基于FFmpeg和Android的音视频同步播放实现
- 【Android Gradle 插件】Gradle 依赖管理 ⑥ ( dependencies 依赖查找路径 | dependencies 依赖冲突 | dependencies 依赖库层级分析 )
- 【Android 插件化】插件化原理 ( 类加载器 )
- Android 实现自己定义多级树控件和全选与反选的效果
- Android开发系列(十八):自己定义控件样式在drawable目录下的XML实现
- Android 自定义RecyclerView 实现真正的Gallery效果
- 从设计到实现,一步步教你实现Android-Universal-ImageLoader-缓存
- 【Android界面实现】使用GestureOverlayView控件实现手势识别
- Android自己定义控件系列一:Android怎样实现老版优酷client三级环形菜单
- android:怎么实现一个控件与另一个指定控件左对齐
- Android 开发常用方法及命令
- Android 11.0 添加关机铃声功能实现