1. 程式人生 > >自定義View之圓形TextView

自定義View之圓形TextView

【1】自定義View的屬性 :

  在res/values下新建一個attrs.xml檔案,在裡面定義我們的屬性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="titleText" format="string"/>
    <attr name="titleTextColor" format="color"/>
    <attr name="backColor" format="color"/>
    <attr name="titleTextSize" format="dimension"/>
<declare-styleable name="CustomCircleView">
    <attr name="titleText"/>
    <attr name="titleTextColor"/>
    <attr name="titleTextSize"/>
    <attr name="backColor"/>
</declare-styleable>
</resources>

在佈局定義View,這裡定義了5個:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >


    <com.dhl.customview.view.CustomCircleView
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:titleText="123456"
        app:titleTextColor="@color/colorAccent"
        android:padding="5dp"
        android:layout_marginLeft="30dp"
        android:layout_centerInParent="true"
        app:titleTextSize="14sp"
        />
    <com.dhl.customview.view.CustomCircleView
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:titleText="123456"
        app:titleTextColor="@color/colorAccent"
        android:padding="5dp"
        android:layout_marginLeft="30dp"
       android:layout_alignParentRight="true"
        app:titleTextSize="18sp"
        />
    <com.dhl.customview.view.CustomCircleView
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:titleText="123456"
        app:titleTextColor="@color/colorAccent"
        android:padding="5dp"
        android:layout_marginLeft="30dp"
        app:backColor="#abdc25"
        android:layout_alignParentLeft="true"
        app:titleTextSize="18sp"
        />
    <com.dhl.customview.view.CustomCircleView
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:titleText="123456"
        app:titleTextColor="@color/colorAccent"
        android:padding="5dp"
        android:layout_marginLeft="30dp"
       android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        app:titleTextSize="18sp"
        />

    <com.dhl.customview.view.CustomCircleView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:titleText="123456"
        app:titleTextColor="@color/colorAccent"
        android:padding="5dp"
        android:layout_marginLeft="30dp"
        app:backColor="#abdc25"
        android:layout_alignParentBottom="true"
       android:layout_alignParentLeft="true"
        app:titleTextSize="18sp"
        />

</RelativeLayout>

【2】在構造方法中獲取自定義屬性:
public CustomCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a= context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomCircleView,defStyleAttr,0);
        /**
         * 這個獲得是你實際在layout中設定的屬性個數
         */
        int n = a.getIndexCount();
        for(int i = 0;i<n;i++)
        {
            int attr = a.getIndex(i);
            switch (attr)
            {
                case R.styleable.CustomCircleView_titleText:
                    titleText = a.getString(attr);
                    break;
                case R.styleable.CustomCircleView_titleTextColor:
                    titleColor = a.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.CustomCircleView_backColor:
                    backColor = a.getColor(attr,Color.GREEN);
                    break;
                case R.styleable.CustomCircleView_titleTextSize:
                    titleSize = a.getDimensionPixelSize(attr,(int) TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    break;
            }
        }
        a.recycle();
        mPaint = new Paint();
        mPaint.setTextSize(titleSize);
        mBound = new Rect();
        /**
         * 獲得繪製文字的寬和高
         */
        mPaint.getTextBounds(titleText, 0, titleText.length(), mBound);


    }

獲取自定義屬性,並且遍歷每個屬性,set相應的值。

【3】重寫onMeasure:

  MeasureSpce的mode有三種:EXACTLY, AT_MOST,UNSPECIFIED,EXACTLY顧名思義,有了準確的值,一般是固定的寬高度或者match_parent。AT_MOST表示在一個範圍內,一般為wrap_content。UNSPECIFIED表示View能有多大就多大,很少用到。

 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.e(TAG,"onMeasure");
        int width = getMySize(100,widthMeasureSpec);
        int height = getMySize(100,heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

封裝計算相應的寬高
/**
     * 計算相應的寬高
     * @param defaultSize 預設值
     * @param measureSpec
     * @return
     */
    private int getMySize(int defaultSize, int measureSpec) {
        int mySize = defaultSize;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);

        switch (mode) {
            case MeasureSpec.UNSPECIFIED: {//如果沒有指定大小,就設定為預設大小
                mySize = defaultSize;
                break;
            }
            case MeasureSpec.AT_MOST: {//如果測量模式是最大取值為size
                //我們將自己計算View的大小
                mPaint.setTextSize(titleSize);
                mPaint.getTextBounds(titleText, 0, titleText.length(), mBound);
                float textWidth = mBound.width();
                int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
                mySize = desired;
                break;
            }
            case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改變它
                mySize = size;
                break;
            }
        }
        return mySize;
    }

主要是對AT_MOST這種情況的處理,這個處理好了,View的寬和高就是對的。

【4】重寫onDraw():

    這個主要是把相應View繪製出來,

 protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.e(TAG,"onDraw");
        //圓的半徑
        int r = Math.min(getMeasuredWidth() / 2,getMeasuredHeight()/2);
        //圓心的橫座標
        int centerX =r;
        //圓心的縱座標
        int centerY =r;
        Paint paint = new Paint();
        paint.setColor(Color.GREEN);
        mPaint.setColor(backColor);
        //繪製圓
        canvas.drawCircle(centerX, centerY, r, mPaint);
        mPaint.setColor(titleColor);
        //繪製Text
        canvas.drawText(titleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
    }
一個圓形的TextView就此完成,下面貼出全部程式碼:
public class CustomCircleView extends View {

    private static final String TAG = CustomCircleView.class.getSimpleName();
    /**
     * 文字
     */
    private String titleText ;
    /**
     * 字型顏色
     */
    private int titleColor =Color.BLACK ;
    /**
     * 背景顏色,設定預設顏色
     */
    private int backColor = Color.GRAY;

    /**
     * 色值
     */
    private int titleSize ;

    /**
     *控制文字的範圍
     */
    private Rect mBound;
    private Paint mPaint;

    public CustomCircleView(Context context) {
        this(context, null);
    }

    public CustomCircleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);

    }

    public CustomCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a= context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomCircleView,defStyleAttr,0);
        /**
         * 這個獲得是你實際在layout中設定的屬性個數
         */
        int n = a.getIndexCount();
        for(int i = 0;i<n;i++)
        {
            int attr = a.getIndex(i);
            switch (attr)
            {
                case R.styleable.CustomCircleView_titleText:
                    titleText = a.getString(attr);
                    break;
                case R.styleable.CustomCircleView_titleTextColor:
                    titleColor = a.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.CustomCircleView_backColor:
                    backColor = a.getColor(attr,Color.GREEN);
                    break;
                case R.styleable.CustomCircleView_titleTextSize:
                    titleSize = a.getDimensionPixelSize(attr,(int) TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    break;
            }
        }
        a.recycle();
        mPaint = new Paint();
        mPaint.setTextSize(titleSize);
        mBound = new Rect();
        /**
         * 獲得繪製文字的寬和高
         */
        mPaint.getTextBounds(titleText, 0, titleText.length(), mBound);


    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.e(TAG,"onDraw");
        //圓的半徑
        int r = Math.min(getMeasuredWidth() / 2,getMeasuredHeight()/2);
        //圓心的橫座標
        int centerX =r;
        //圓心的縱座標
        int centerY =r;
        Paint paint = new Paint();
        paint.setColor(Color.GREEN);
        mPaint.setColor(backColor);
        //繪製圓
        canvas.drawCircle(centerX, centerY, r, mPaint);
        mPaint.setColor(titleColor);
        //繪製Text
        canvas.drawText(titleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.e(TAG,"onMeasure");
        int width = getMySize(100,widthMeasureSpec);
        int height = getMySize(100,heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    /**
     * 計算相應的寬高
     * @param defaultSize 預設值
     * @param measureSpec
     * @return
     */
    private int getMySize(int defaultSize, int measureSpec) {
        int mySize = defaultSize;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);

        switch (mode) {
            case MeasureSpec.UNSPECIFIED: {//如果沒有指定大小,就設定為預設大小
                mySize = defaultSize;
                break;
            }
            case MeasureSpec.AT_MOST: {//如果測量模式是最大取值為size
                //我們將自己計算View的大小
                mPaint.setTextSize(titleSize);
                mPaint.getTextBounds(titleText, 0, titleText.length(), mBound);
                float textWidth = mBound.width();
                int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
                mySize = desired;
                break;
            }
            case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改變它
                mySize = size;
                break;
            }
        }
        return mySize;
    }

    public void setTitleText(String str)
    {
        titleText = str ;
        invalidate();
    }

}
OK,下面貼出效果圖


如有問題,請留言。。。