Android 完美實現圖片圓角和圓形(對實現進行分析)


分類: 【android 進階之路】
 41707人閱讀 評論(81) 收藏 舉報

目錄(?)[+]

轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/24555655

本來想在網上找個圓角的例子看一看,不盡人意啊,基本都是官方的Demo的那張原理圖,稍後會貼出。於是自己自定義了個View,實現圖片的圓角以及圓形效果。效果圖:


第一個是原圖,第二個是圓形效果,第三第四設置了不同的圓角大小。

準備改變一個博客的風格,首先給大家講一下原理,讓大家明白了,然後再貼代碼,不然可以直接看那麼長的代碼也比較痛苦,核心代碼其實就那麼幾行:

核心代碼分析:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.      * 根據原圖和變長繪製圓形圖片 
  3.      *  
  4.      * @param source 
  5.      * @param min 
  6.      * @return 
  7.      */  
  8.     private Bitmap createCircleImage(Bitmap source, int min)  
  9.     {  
  10.         final Paint paint = new Paint();  
  11.         paint.setAntiAlias(true);  
  12.         Bitmap target = Bitmap.createBitmap(min, min, Config.ARGB_8888);  
  13.         /** 
  14.          * 產生一個同樣大小的畫布 
  15.          */  
  16.         Canvas canvas = new Canvas(target);  
  17.         /** 
  18.          * 首先繪製圓形 
  19.          */  
  20.         canvas.drawCircle(min / 2, min / 2, min / 2, paint);  
  21.         /** 
  22.          * 使用SRC_IN 
  23.          */  
  24.         paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));  
  25.         /** 
  26.          * 繪製圖片 
  27.          */  
  28.         canvas.drawBitmap(source, 00, paint);  
  29.         return target;  
  30.     }  

其實主要靠:paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));這行代碼,爲什麼呢,我給大家解釋下,SRC_IN這種模式,兩個繪製的效果疊加後取交集展現後圖,怎麼說呢,咱們第一個繪製的是個圓形,第二個繪製的是個Bitmap,於是交集爲圓形,展現的是BItmap,就實現了圓形圖片效果。圓角,其實就是先繪製圓角矩形,是不是很簡單,以後別人再說實現圓角,你就把這一行代碼給他就行了。

從Android的示例中,給大家證明一下:

下面有一張PorterDuff.Mode的16中效果圖,咱們的只是其一:


源碼咱們只關心誰先誰後繪製的:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. canvas.translate(x, y);  
  2.               canvas.drawBitmap(mDstB, 00, paint);  
  3.               paint.setXfermode(sModes[i]);  
  4.               canvas.drawBitmap(mSrcB, 00, paint);  
  5.               paint.setXfermode(null);  
  6.               canvas.restoreToCount(sc);  
可以看出先繪製的Dst,再繪製的Src,最後的展示是SrcIn那個圖:顯示的區域是二者交集,展示的是Src(後者)。和咱們前面結論一致。效果16種,大家可以自由組合展示不同的效果。


好了,原理和核心代碼解釋完成。下面開始寫自定義View。

1、自定義屬性:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   
  4.     <attr name="borderRadius" format="dimension" />  
  5.     <attr name="type">  
  6.         <enum name="circle" value="0" />  
  7.         <enum name="round" value="1" />  
  8.     </attr>  
  9.     <attr name="src" format="reference"></attr>  
  10.   
  11.     <declare-styleable name="CustomImageView">  
  12.         <attr name="borderRadius" />  
  13.         <attr name="type" />  
  14.         <attr name="src" />  
  15.     </declare-styleable>  
  16.   
  17. </resources>  

2、構造中獲取自定義的屬性:
[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  * TYPE_CIRCLE / TYPE_ROUND 
  3.  */  
  4. private int type;  
  5. private static final int TYPE_CIRCLE = 0;  
  6. private static final int TYPE_ROUND = 1;  
  7.   
  8. /** 
  9.  * 圖片 
  10.  */  
  11. private Bitmap mSrc;  
  12.   
  13. /** 
  14.  * 圓角的大小 
  15.  */  
  16. private int mRadius;  
  17.   
  18. /** 
  19.  * 控件的寬度 
  20.  */  
  21. private int mWidth;  
  22. /** 
  23.  * 控件的高度 
  24.  */  
  25. private int mHeight;  
  26.   
  27. public CustomImageView(Context context, AttributeSet attrs)  
  28. {  
  29.     this(context, attrs, 0);  
  30. }  
  31.   
  32. public CustomImageView(Context context)  
  33. {  
  34.     this(context, null);  
  35. }  
  36.   
  37. /** 
  38.  * 初始化一些自定義的參數 
  39.  *  
  40.  * @param context 
  41.  * @param attrs 
  42.  * @param defStyle 
  43.  */  
  44. public CustomImageView(Context context, AttributeSet attrs, int defStyle)  
  45. {  
  46.     super(context, attrs, defStyle);  
  47.   
  48.     TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, defStyle, 0);  
  49.   
  50.     int n = a.getIndexCount();  
  51.     for (int i = 0; i < n; i++)  
  52.     {  
  53.         int attr = a.getIndex(i);  
  54.         switch (attr)  
  55.         {  
  56.         case R.styleable.CustomImageView_src:  
  57.             mSrc = BitmapFactory.decodeResource(getResources(), a.getResourceId(attr, 0));  
  58.             break;  
  59.         case R.styleable.CustomImageView_type:  
  60.             type = a.getInt(attr, 0);// 默認爲Circle  
  61.             break;  
  62.         case R.styleable.CustomImageView_borderRadius:  
  63.             mRadius= a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f,  
  64.                     getResources().getDisplayMetrics()));// 默認爲10DP  
  65.             break;  
  66.         }  
  67.     }  
  68.     a.recycle();  
  69. }  

3、onMeasure中獲取控件寬高:

[java] view plaincopy
  1. /** 
  2.      * 計算控件的高度和寬度 
  3.      */  
  4.     @Override  
  5.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
  6.     {  
  7.         // super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  8.         /** 
  9.          * 設置寬度 
  10.          */  
  11.         int specMode = MeasureSpec.getMode(widthMeasureSpec);  
  12.         int specSize = MeasureSpec.getSize(widthMeasureSpec);  
  13.   
  14.         if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate  
  15.         {  
  16.             mWidth = specSize;  
  17.         } else  
  18.         {  
  19.             // 由圖片決定的寬  
  20.             int desireByImg = getPaddingLeft() + getPaddingRight()  
  21.                     + mSrc.getWidth();  
  22.             if (specMode == MeasureSpec.AT_MOST)// wrap_content  
  23.             {  
  24.                 mWidth = Math.min(desireByImg, specSize);  
  25.             } else  
  26.   
  27.                 mWidth = desireByImg;  
  28.         }  
  29.   
  30.         /*** 
  31.          * 設置高度 
  32.          */  
  33.   
  34.         specMode = MeasureSpec.getMode(heightMeasureSpec);  
  35.         specSize = MeasureSpec.getSize(heightMeasureSpec);  
  36.         if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate  
  37.         {  
  38.             mHeight = specSize;  
  39.         } else  
  40.         {  
  41.             int desire = getPaddingTop() + getPaddingBottom()  
  42.                     + mSrc.getHeight();  
  43.   
  44.             if (specMode == MeasureSpec.AT_MOST)// wrap_content  
  45.             {  
  46.                 mHeight = Math.min(desire, specSize);  
  47.             } else  
  48.                 mHeight = desire;  
  49.         }  
  50.   
  51.         setMeasuredDimension(mWidth, mHeight);  
  52.   
  53.     }  



4、根據Type繪製:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.      * 繪製 
  3.      */  
  4.     @Override  
  5.     protected void onDraw(Canvas canvas)  
  6.     {  
  7.   
  8.         switch (type)  
  9.         {  
  10.         // 如果是TYPE_CIRCLE繪製圓形  
  11.         case TYPE_CIRCLE:  
  12.   
  13.             int min = Math.min(mWidth, mHeight);  
  14.             /** 
  15.              * 長度如果不一致,按小的值進行壓縮 
  16.              */  
  17.             mSrc = Bitmap.createScaledBitmap(mSrc, min, min, false);  
  18.   
  19.             canvas.drawBitmap(createCircleImage(mSrc, min), 00null);  
  20.             break;  
  21.         case TYPE_ROUND:  
  22.             canvas.drawBitmap(createRoundConerImage(mSrc), 00null);  
  23.             break;  
  24.   
  25.         }  
  26.   
  27.     }  
  28.   
  29.     /** 
  30.      * 根據原圖和變長繪製圓形圖片 
  31.      *  
  32.      * @param source 
  33.      * @param min 
  34.      * @return 
  35.      */  
  36.     private Bitmap createCircleImage(Bitmap source, int min)  
  37.     {  
  38.         final Paint paint = new Paint();  
  39.         paint.setAntiAlias(true);  
  40.         Bitmap target = Bitmap.createBitmap(min, min, Config.ARGB_8888);  
  41.         /** 
  42.          * 產生一個同樣大小的畫布 
  43.          */  
  44.         Canvas canvas = new Canvas(target);  
  45.         /** 
  46.          * 首先繪製圓形 
  47.          */  
  48.         canvas.drawCircle(min / 2, min / 2, min / 2, paint);  
  49.         /** 
  50.          * 使用SRC_IN,參考上面的說明 
  51.          */  
  52.         paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));  
  53.         /** 
  54.          * 繪製圖片 
  55.          */  
  56.         canvas.drawBitmap(source, 00, paint);  
  57.         return target;  
  58.     }  
  59.   
  60.     /** 
  61.      * 根據原圖添加圓角 
  62.      *  
  63.      * @param source 
  64.      * @return 
  65.      */  
  66.     private Bitmap createRoundConerImage(Bitmap source)  
  67.     {  
  68.         final Paint paint = new Paint();  
  69.         paint.setAntiAlias(true);  
  70.         Bitmap target = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888);  
  71.         Canvas canvas = new Canvas(target);  
  72.         RectF rect = new RectF(00, source.getWidth(), source.getHeight());  
  73.         canvas.drawRoundRect(rect, mRadius, mRadius, paint);  
  74.         paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));  
  75.         canvas.drawBitmap(source, 00, paint);  
  76.         return target;  
  77.     }  

好了,我就不解析代碼了,自定義View這是第五篇了,,,,寫得好惡心,,,,


各位贊一個或者留個言,算是對我的支持~


源碼點擊下載



=========================================簡單修復了一下,在ScrollView以及AdapterView中的headview的顯示異常的bug============

修復後代碼下載:

源碼點擊下載


相關博文,同時也推薦使用:

Android Xfermode 實戰 實現圓形、圓角圖片

Android BitmapShader 實戰 實現圓形、圓角圖片


版權聲明:本文爲博主原創文章,未經博主允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章