編輯:關於Android編程
先看看效果吧
根據官方APIDemo給出的Xfermode例子我們可以看到下圖展示那樣
但是,說實話,看到這張圖,我是懵逼的,我們應該去自己試一試來加深下自己的理解,所以我畫了下,得到的結果如下,僅供參考!最好自己試一試
我先畫的正方形,後畫的圓
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); //圓和正方形相交部分清除,圓與透明部分相交也清除 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); //覆蓋在表面,相交部分正方形被清除,圓與透明相交不被清除 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST)); //清除覆蓋在上面的圓,圓相交不相交部分都清除 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));//圓覆蓋在表面,正方形內部沒影響,圓與透明相交不被清除 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));//清除覆蓋在上面的圓,圓與透明部分相交不清除 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//顯示相交的,覆蓋在上面的圓,清除不相交部分的圓和相交部分的正方形 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));//圓被清除,相交部分隨著圓的透明度變化而正比例變化 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));//清除相交部分的圓和正方形,不相交不清除 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));//圓被清除,相交部分隨著圓的透明度變化而反比例 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));//顯示相交的,覆蓋在上面的圓,清除不相交部分的圓不清除相交部分的正方形,和SRC_IN對比 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));//相交部分圓被清除,相交部分隨著圓的透明度變化而正比例變化,與DST_IN對比 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));//相交部分圓被清除,相交部分隨著圓的透明度變化而反比例變化 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));//相交部分加黑色素,透明度越大,越黑 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));//相交部分加亮色素,透明度越大,越亮 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));//相交部分加黑色素,透明度越大,越黑,圓不相交部分被清除 // paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));//相交部分加亮色素,透明度越大,越亮,感覺和LIGHTEN 一樣
僅供參考,最好自己試一試。
弄清楚各遮罩的效果,就可以開始做了,這裡面,實現圓形頭像我使用的是PorterDuff.Mode.SRC_IN
//顯示相交的,覆蓋在上面的圓,清除不相交部分的圓和相交部分的正方形
嗯,這可以先畫一個實心圓,然後在把背景圖片畫上去,PorterDuff.Mode.SRC_IN會清除不相交部分的背景圖,所以就只剩下相交部分的圓形背景圖了,這樣就實現了。原理就是這樣,我們來看看實際怎麼實現吧
/** * Created by Administrator on 2016/10/16 0016. */ public class CircleView extends View { public CircleView(Context context) { this(context,null); } public CircleView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public void draw(Canvas canvas) { //重寫view的draw方法,view繪制的時候會調用此方法 Bitmap bitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas1 = new Canvas(bitmap); //bitmap為畫紙,canvas1是畫板 super.draw(canvas1); //把背景圖畫在canvas1這張畫板上,但注意畫是畫在紙上的,即背景圖在bitmap上 Bitmap bitmap2 = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);//注意這裡要帶A即支持透明 Canvas canvas2 = new Canvas(bitmap2); //再來一張新畫紙,新畫板 Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.WHITE); canvas2.drawARGB(0,0,0,0); //初始化畫紙帶透明 //畫實心圓 paint的style默認是fill填充的 canvas2.drawCircle(bitmap.getWidth()/2,bitmap.getHeight()/2,bitmap.getWidth()/2,paint); //設置遮罩的模式 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); //前面我們已經通過super.draw(canvas1);把背景都畫在了畫紙bitmap上,即此時畫背景即是畫bitmap canvas2.drawBitmap(bitmap,0,0,paint);//此時,其實我們想要的效果就得到了,但是如果就這麼結束的話, //是不行的,因為我們畫在canvas2畫板上!!!而view要顯示是要畫在,它自己帶的參數canvas畫板上的!!! //所以我們這裡把canvas2畫板上的畫紙bitmap2畫上來,這樣view就達到我們想要的效果了 canvas.drawBitmap(bitmap2,0,0,null); addStroke(canvas); } /**增加描邊*/ private void addStroke(Canvas canvas) { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setStrokeWidth(2); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); canvas.drawCircle(getWidth() / 2, getHeight() / 2, (float) (getHeight() / 2 -1), paint); } }
布局
我注釋寫的還算詳細吧,跟著注釋跟著代碼一起讀覺得比較好理解.
接下來的微信效果其實也是大巫小巫的問題,不是問題,這裡用的是PorterDuff.Mode.SRC_ATOP
//顯示相交的,覆蓋在上面的圓,清除不相交部分的圓不清除相交部分的正方形,和SRC_IN對比
它與SRC_IN不同的就是他不會清除相交部分 覆蓋在下面的圖形,然後我們改變上面的透明度就行了。選來看看那個圖標
格子表示透明,好吧,中間那坨本來也是透明的,是我塗的白色,這裡推薦一個阿裡的圖標網站http://www.iconfont.cn/,真的很好,雷鋒啊。嗯,原理是這樣,來看看怎麼實現吧
public class GradientView extends View{ public GradientView(Context context) { this(context,null); } public GradientView(Context context, AttributeSet attrs) { this(context, attrs,0); } int alpha = 255; public GradientView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); final Timer timer = new Timer(); TimerTask task = new TimerTask() { @Override public void run() { alpha -= 10; //每300毫秒背景色越透明 if (alpha < 0){ timer.cancel(); }else{ postInvalidate(); //主動調draw方法刷新視圖 } } }; timer.schedule(task,1000,300); } public Bitmap icon; public void setBitmap(Bitmap bitmap){ icon = bitmap; //外部傳圖標 postInvalidate();//主動調draw方法刷新視圖 } private String txt; public void setText(String text){ txt = text; //外部傳文字 postInvalidate();//主動調draw方法刷新視圖 } @Override public void draw(Canvas canvas) { //與上面的實現圓形頭像一樣,只是換了個遮罩模式 Bitmap bitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas1 = new Canvas(bitmap); super.draw(canvas1); //背景色畫在bitmap上 Bitmap bitmap1 = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas2 = new Canvas(bitmap1); canvas2.drawARGB(0,0,0,0); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(13); paint.setColor(Color.BLACK); if(txt != null){ //畫文字 canvas2.drawText(txt,getWidth()/2,getHeight()-5,paint); } if(icon != null){ //畫圖片 Matrix matrix = new Matrix(); //幫助實現bitmap縮放 matrix.postScale(getWidth()/3.0f/icon.getWidth(),getWidth()/3.0f/icon.getHeight()); canvas2.drawBitmap(Bitmap.createBitmap(icon,0,0,icon.getWidth(),icon.getHeight(),matrix,true),getWidth()/3,0,null); } paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); paint.setAlpha(alpha); //透明度變化 canvas2.drawBitmap(bitmap,0,0,paint); //將背景色畫在圖標和文字上面,相交的部分才顯示 canvas.drawBitmap(bitmap1,0,0,null); //最後畫在畫板上,不理解回去看圓形圖片實現 //四周的邊框 Paint paint1 = new Paint(); paint1.setColor(Color.RED); paint.setAntiAlias(true); paint1.setStyle(Paint.Style.STROKE); canvas.drawRect(0,0,getWidth(),getHeight(),paint1); } }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_circle); GradientView gv = (GradientView) findViewById(R.id.gv); gv.setBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.weixin_logo)); gv.setText("微信"); }
最重要的還是知道了各種遮罩的特性,有時間自己最好試一下,下次就用這個GradientView來實現微信的底部導航菜單吧
本篇繼續來講自定義ViewGroup,給大家帶來一個實例:FlowLayout。何為FlowLayout,就是控件根據ViewGroup的寬,自動的往右添加,如果當前行剩
一直以來Android性能測試一直是Android測試中一個被一部分人遺忘,有被一部分人無可奈何的東西。在絕大部分的創業公司,性能測試基本上都是被遺忘的,因為功能測試和穩
好久沒有更新博客,寫這篇技術時,感覺很多東西生疏了好多。於是心有感慨:我們做技術的,要是長時間不搞技術,那就是被技術搞!所以攻守之間,大家謹慎思量。冬天已過,放假出去玩耍
介紹參考安卓Dialog源碼,他的builder設計模式實現方式是,使用內部類來實現功能,外部類的作用是通過build()函數,來對內部類進行參數設置,例如setter方