android 通用圆角控件

koda8144 8年前
   <h3>圆角控件就是对 View的Canvas进行改变轮廓的处理 <br> <br> 改变轮廓两种方式: <br> 1.剪切(clip()) <br> 剪切clip是对画布进行剪切,只对剪切后的绘制起效果。 <br> ps:Canvas的图形变换平移、放缩、旋转、错切、裁剪都是只对后面的绘制起效果, <br> 对应Matrix中preXXX,Matrix变换分为preXXX,postXXX,setXXX;preXXX将新的变换操作插到队列 前,postXXX将新的变换操作插到队列后,setXXX是先reset()清除前面的变换操作并设置新的变换操作,且都是只对后面的绘制起效果。 <br> Canvas的save,restore对变换操作进行保存,和还原,带参的restoreToCount(save()),可以指定还原到第几次保存的状态。 <br> <br> <br> 2.遮罩(PorterDuffXfermode) <br> 安卓提供多种遮罩模式选择 <br> <img alt="android 通用圆角控件" src="https://simg.open-open.com/show/70b38eaa4c9a83ea584884e76f7b928e.png"> <br> 遮罩是设置在Paint上的只对 当前绘制的操作有效 </h3>    <p>下面利用这两种方式实现圆角控件</p>    <p><br> onDraw <br> onDrawForeground <br> dispatchDraw <br> 这三个回调函数都是可以操作View的Canvas;onDraw,onDrawForeground这两个是在View绘制背景,自身内容和前景时回调 的 只有设置了背景、自身内容、前景时才会配回调,并且对这两个函数的参数Canvas上的操作,只对背景、自身内容、前景有效。 <br> dispatchDraw是绘制子控件时的回调,参数Canvas可以对子控件的画布进行处理。 <br> <br> 通用圆角控件必须对子控件的对应位置也是原价所以我门选择在dispatchDraw中进行圆角处理。</p>    <p>剪切(clip())</p>    <pre>  <code class="language-java">    @Override      protected void dispatchDraw(Canvas canvas) {        int width = getWidth();        int height = getHeight();          Path path = new Path();          path.moveTo(0, topLeftRadius);          path.arcTo(new RectF(0, 0, topLeftRadius * 2, topLeftRadius * 2), -180, 90);          path.lineTo(width - topRightRadius, 0);          path.arcTo(new RectF(width - 2 * topRightRadius, 0, width, topRightRadius * 2), -90, 90);          path.lineTo(width, height - bottomRightRadius);          path.arcTo(new RectF(width - 2 * bottomRightRadius, height - 2 * bottomRightRadius, width, height), 0, 90);          path.lineTo(bottomLeftRadius, height);          path.arcTo(new RectF(0, height - 2 * bottomLeftRadius, bottomLeftRadius * 2, height), 90, 90);          path.close();          canvas.clipPath(path);        super.dispatchDraw(canvas);      }12345678910111213141516171234567891011121314151617</code></pre>    <p>效果图: <br> <img alt="android 通用圆角控件" src="https://simg.open-open.com/show/57f7ed3e77a4eeda35ad66ace589a682.png"></p>    <p> </p>    <p>上图能看到明显的锯齿 因为 安卓虽提供了抗锯齿功能但是是在Paint上操作的 clip过程没有用到Paint 无法达到抗锯齿目的;</p>    <p>遮罩(PorterDuffXfermode)</p>    <h2>写法一</h2>    <pre>  <code class="language-java"> @Override      protected void dispatchDraw(Canvas canvas) {        super.dispatchDraw(canvas);          drawTopLeft(canvas);//用PorterDuffXfermode          drawTopRight(canvas);//用PorterDuffXfermode          drawBottomLeft(canvas);//用PorterDuffXfermode          drawBottomRight(canvas);//用PorterDuffXfermode      }1234567812345678</code></pre>    <p>效果图: <br> <img alt="android 通用圆角控件" src="https://simg.open-open.com/show/bb344eed72a53d9718fb495873a51eeb.png"></p>    <p>上图有黑色底色,view的Canvas底层画布的BItmap是 RGB_565的所以怎么画都有一个黑色底色。</p>    <p>我们可以new Canvas一个底层画布的BItmap是 ARGB_8888的绘制完后 再把这个底层画布的BItmap 绘制到View 的Canvas上</p>    <h2>写法二</h2>    <pre>  <code class="language-java">    @Override      protected void dispatchDraw(Canvas canvas) {          Bitmap bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);          Canvas newCanvas = new Canvas(bitmap);        super.dispatchDraw(newCanvas);          drawTopLeft(newCanvas);          drawTopRight(newCanvas);          drawBottomLeft(newCanvas);          drawBottomRight(newCanvas);          canvas.drawBitmap(bitmap,0,0,imagePaint);//        invalidate();      }123456789101112123456789101112</code></pre>    <p>效果图: <br> <img alt="android 通用圆角控件" src="https://simg.open-open.com/show/730ad44f7ab5660ea9165b4b62b1c8eb.png"></p>    <p>实现了,但是这种映射方式实现的 如果子控件中存在滑动控件,滑动时无法实时刷新,用Glide加载image到ImageView中时,WebView load时 都无法实时刷新,出现无法加载的效果,虽然可以加上invalidate通知刷新 但是掉帧明显掉帧</p>    <p>我们只能用回写法一 但是要解决黑色背景的问题 <br> 只要加上一句代码 就能解决 默认黑色背景的问题</p>    <pre>  <code class="language-java">canvas.saveLayer(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), imagePaint,Canvas.ALL_SAVE_FLAG);11</code></pre>    <pre>  <code class="language-java"> @Override      protected void dispatchDraw(Canvas canvas) {  canvas.saveLayer(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), imagePaint,Canvas.ALL_SAVE_FLAG);        super.dispatchDraw(canvas);          drawTopLeft(canvas);//用PorterDuffXfermode          drawTopRight(canvas);//用PorterDuffXfermode          drawBottomLeft(canvas);//用PorterDuffXfermode          drawBottomRight(canvas);//用PorterDuffXfermode          canvas.restore();      }1234567891012345678910</code></pre>    <p>效果图: <br> <img alt="android 通用圆角控件" src="https://simg.open-open.com/show/b486845e66bd4d5afb0450ea811c477f.png"></p>    <p>因为view的Canvas底层画布的BItmap是 RGB_565 <br> 我们只要在保存为图层就行了。用过Photoshop的都知道默认底层画布是不透明的,要先解锁,而这里解锁就是保存为图层。</p>