自定義圓形ImageView(仿QQ頭像)

highcoder1發表於2016-07-14



我們可以發現,現在的app對圓形圖片的使用越來越普遍,特別是使用者的頭像等。圓形圖片外觀柔和、友好、飽滿,能大大提升使用者的視覺體驗。所以今天我們就來看看怎樣自定義圓形的ImageView(一些說明與應該注意的點就寫在註釋裡了)。


①首先我們要自定義該CustomImageView的屬性:在values目錄下建立一個attrs.xml檔案,宣告自定義ImageView可以設定哪些屬性。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomImageView">
        <!--這是一個設定原圖片的屬性-->
        <attr name="src" format="reference"/>
    </declare-styleable>
</resources>



②下面CustomImageView在xml佈局檔案中的使用,別忘了加自己的名稱空間(xmlns:app="http://schemas.android.com/apk/res-auto"

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/bottomTabStart"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".activtiy.MainActivity">

    <com.tangao.test.view.CustomImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:src="@drawable/panda" />

</LinearLayout>

③當然,我們還需要用程式碼來實現這個自定義的ImageView(這才是最主要的)。

public class CustomImageView extends View {
    // 圖片
    private Bitmap mSrc;
    // 控制元件的寬度
    private int mWidth;
    // 控制元件的高度
    private int mHeight;

    /**
     * 把自定義的這個CustomImageView放到佈局檔案中並設定屬性,資源解析器就會進入這個構造方法,對資源進行解析
     *
     * @param context
     * @param attrs
     */
    public CustomImageView(Context context, AttributeSet attrs)// 由資源解析程式使用
    {
        this(context, attrs, 0);
    }

    public CustomImageView(Context context)// 由程式碼使用
    {
        this(context, null);
    }

    /**
     * 初始化一些自定義的引數
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public CustomImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // 屬性(id)陣列
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.CustomImageView, defStyle, 0);
        //a.getIndexCount();// 獲取佈局檔案中CustomImageView設定的屬性個數
        //a.getIndex(i);// 返回xml中CustomImageView設定了的各屬性的 “屬性名”(就是attrs.xml中的部分或全部屬性),取值0,1,2...
//        if (a.getIndexCount() == 0) {
//            try {
//                throw new Exception("自定義ImageView未設定圖片資源");
//            } catch (Exception e) {
//                e.printStackTrace();
//            } finally {
//                Log.i("CustomImageViewError", "自定義ImageView未設定圖片資源");
//            }
//        }
        //根據屬性名,獲取到對應的圖片ID,再將id對應的drawable圖片轉成點陣圖
        mSrc = BitmapFactory.decodeResource(getResources(),
                a.getResourceId(R.styleable.CustomImageView_src, 0));

        a.recycle();// 記得最後要recycle掉
    }

    /**
     * 計算控制元件的高度和寬度,最後重置寬高度
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        /**
         * 設定寬度
         */
        int specMode = MeasureSpec.getMode(widthMeasureSpec);//MeasureSpec是一個用static修飾的內部類,因此可以當做一個普通的類來使用
        /**
         * 當customImageView設定成match_parent或具體值時,那就是 螢幕的寬高度 或 具體值
         * 當customImageView設定成wrap_content時,specsize為customImageView的最大寬高度(決定於父控制元件寬高度)
         */
        int specSize = MeasureSpec.getSize(widthMeasureSpec);

        if (specMode == MeasureSpec.EXACTLY)// xml中customImageView中設定的match_parent , accurate(確定的值)
        {
            mWidth = specSize;
        } else {
            // 由圖片決定的寬,padding是xml中customImageView中設定的padding
            int desireByImg = getPaddingLeft() + getPaddingRight() + mSrc.getWidth();
            if (specMode == MeasureSpec.AT_MOST)// wrap_content
            {
                /**
                 * 當某些圖片較大時,使用wrap_content可能超出螢幕,此時 specSize < desireByImg
                 * 當某些圖片較小時,沒超出螢幕,此時 specSize > desireByImg
                 */
                mWidth = Math.min(desireByImg, specSize);
            }
        }

        /***
         * 設定高度
         */
        specMode = MeasureSpec.getMode(heightMeasureSpec);
        specSize = MeasureSpec.getSize(heightMeasureSpec);

        if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
        {
            mHeight = specSize;
        } else {
            int desire = getPaddingTop() + getPaddingBottom() + mSrc.getHeight();
            if (specMode == MeasureSpec.AT_MOST)// wrap_content
            {
                mHeight = Math.min(desire, specSize);
            }
        }
        setMeasuredDimension(mWidth, mHeight);//自行設定XML中customImageView的寬高,必須在onMesure()中呼叫

    }

    /**
     * 繪製
     */
    @Override
    protected void onDraw(Canvas canvas) {

        int min = Math.min(mWidth, mHeight);
        /**
         * 長度如果不一致,按小的值進行壓縮
         */
        mSrc = Bitmap.createScaledBitmap(mSrc, min, min, false);
        canvas.drawBitmap(createCircleImage(mSrc, min), 0, 0, null);

    }

    /**
     * 根據原圖和變長繪製圓形圖片
     *
     * @param source
     * @param min
     * @return
     */
    private Bitmap createCircleImage(Bitmap source, int min) {
        final Paint paint = new Paint();
        paint.setAntiAlias(true);//抗鋸齒
        //建立一個原圖片大小的bitmap,Config.ARGB_8888說明是32位點陣圖
        Bitmap target = Bitmap.createBitmap(min, min, Config.ARGB_8888);
        /**
         * 產生一個同樣大小的畫布
         */
        Canvas canvas = new Canvas(target);
        /**
         * 首先繪製圓形
         */
        canvas.drawCircle(min / 2, min / 2, min / 2, paint);
        /**
         * 使用SRC_IN
         */
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//取畫布繪製的圓形和圖片的交集
        /**
         * 繪製圖片
         */
        canvas.drawBitmap(source, 0, 0, paint);
        return target;//返回截出的圓形圖片
    }
}


如此,執行後便可以看到圓形圖片,下面我的執行結果: 希望大家都能成功切出圓形圖片!



本文轉載自:http://blog.csdn.net/lmj623565791/article/details/24555655








相關文章