android 通用圓角控件
圓角控件就是對 View的Canvas進行改變輪廓的處理
改變輪廓兩種方式:
1.剪切(clip())
剪切clip是對畫布進行剪切,只對剪切后的繪制起效果。
ps:Canvas的圖形變換平移、放縮、旋轉、錯切、裁剪都是只對后面的繪制起效果,
對應Matrix中preXXX,Matrix變換分為preXXX,postXXX,setXXX;preXXX將新的變換操作插到隊列 前,postXXX將新的變換操作插到隊列后,setXXX是先reset()清除前面的變換操作并設置新的變換操作,且都是只對后面的繪制起效果。
Canvas的save,restore對變換操作進行保存,和還原,帶參的restoreToCount(save()),可以指定還原到第幾次保存的狀態。
2.遮罩(PorterDuffXfermode)
安卓提供多種遮罩模式選擇
遮罩是設置在Paint上的只對 當前繪制的操作有效
下面利用這兩種方式實現圓角控件
onDraw
onDrawForeground
dispatchDraw
這三個回調函數都是可以操作View的Canvas;onDraw,onDrawForeground這兩個是在View繪制背景,自身內容和前景時回調 的 只有設置了背景、自身內容、前景時才會配回調,并且對這兩個函數的參數Canvas上的操作,只對背景、自身內容、前景有效。
dispatchDraw是繪制子控件時的回調,參數Canvas可以對子控件的畫布進行處理。
通用圓角控件必須對子控件的對應位置也是原價所以我門選擇在dispatchDraw中進行圓角處理。
剪切(clip())
@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
效果圖:

上圖能看到明顯的鋸齒 因為 安卓雖提供了抗鋸齒功能但是是在Paint上操作的 clip過程沒有用到Paint 無法達到抗鋸齒目的;
遮罩(PorterDuffXfermode)
寫法一
@Override
protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas);
drawTopLeft(canvas);//用PorterDuffXfermode
drawTopRight(canvas);//用PorterDuffXfermode
drawBottomLeft(canvas);//用PorterDuffXfermode
drawBottomRight(canvas);//用PorterDuffXfermode
}1234567812345678
效果圖:

上圖有黑色底色,view的Canvas底層畫布的BItmap是 RGB_565的所以怎么畫都有一個黑色底色。
我們可以new Canvas一個底層畫布的BItmap是 ARGB_8888的繪制完后 再把這個底層畫布的BItmap 繪制到View 的Canvas上
寫法二
@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
效果圖:

實現了,但是這種映射方式實現的 如果子控件中存在滑動控件,滑動時無法實時刷新,用Glide加載image到ImageView中時,WebView load時 都無法實時刷新,出現無法加載的效果,雖然可以加上invalidate通知刷新 但是掉幀明顯掉幀
我們只能用回寫法一 但是要解決黑色背景的問題
只要加上一句代碼 就能解決 默認黑色背景的問題
canvas.saveLayer(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), imagePaint,Canvas.ALL_SAVE_FLAG);11
@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
效果圖:

因為view的Canvas底層畫布的BItmap是 RGB_565
我們只要在保存為圖層就行了。用過Photoshop的都知道默認底層畫布是不透明的,要先解鎖,而這里解鎖就是保存為圖層。