3個自定義view佈局:矩形TextView,圓形進度條,圓環view

Dewey-W發表於2017-11-03

注:本文3個自定義view佈局共用使用一個xml檔案,


1.自定義矩形TextView的view

package com.bwie.CustomView.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.widget.TextView;
import com.bwie.CustomView.R;

/**
 自定義矩形TextView的view

*/
public class CustomTextView extends TextView{
    private String text;
    private int color;
    private Paint paint;
    private int size;
    private Rect rect;

    public CustomTextView(Context context) {
        super(context);
    }

    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        //通過context.obtainStyledAttributes()方法讀取自定義view的屬性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
        //獲取自定義view的屬性的數量
        int count = typedArray.getIndexCount();
        //通過下標獲取自定義view的屬性
        for (int i=0;i<count;i++){
            int indexAttr = typedArray.getIndex(i);
            switch (indexAttr){
                //獲取自定義view的text屬性
                case R.styleable.CustomTextView_text:
                    text = typedArray.getString(indexAttr);
                    break;
                //獲取自定義view的color屬性,先設定一個預設顏色
                case R.styleable.CustomTextView_textColor:
                    color = typedArray.getInt(indexAttr, Color.YELLOW);
                    break;
                //獲取自定義view的size屬性
                case R.styleable.CustomTextView_textSize:
                    //通過下面前2行程式碼的方法轉換獲取的size屬性資料
                    DisplayMetrics metrics = getResources().getDisplayMetrics();
                    float dimension = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 18, metrics);
                    size = typedArray.getDimensionPixelSize(indexAttr, (int) dimension);
                    break;
            }
        }
        //資料不用時,回收以下
        typedArray.recycle();

        //設定畫筆的size
        paint = new Paint();
        paint.setTextSize(size);

        //設定一個矩形。對text內容進行測量
        rect = new Rect();
        paint.getTextBounds(text,0,text.length(),rect);
    }

    /**
     *  onMeasure方法:計運算元控制元件的尺寸和模式,以及設定自己的寬和高,測量View大小
        widthMeasureSpec:當前父容器的widthMeasureSpec
        heightMeasureSpec:當前父容器的heightMeasureSpec
        使用的是onMeasure函式,關於三種測量模式mode說法:
     (1)EXACTLY:一般是設定了明確的值(100dp)或者是MATCH_PARENT;
     (2)AT_MOST:表示子佈局限制在一個最大值內,一般為WARP_CONTENT;
     (3)UNSPECIFIED:表示子佈局想要多大就多大,很少使用;
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //計算當前父容器下view的模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        //計算當前父容器下view的size
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        System.out.println("width = " + width);
        System.out.println("height = " + height);
    }

    //onDraw方法:繪圖方法(canvas畫布)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //getWidth(): View在設定好佈局後,整個View的寬度     getHeight(): View在設定好佈局後,整個View的高度
        int widht = getWidth();
        int height = getHeight() ;

        //獲取矩形的寬高
        int rectWidth = rect.width();
        int rectHeight = rect.height();

        //設定畫筆的顏色
        paint.setColor(Color.YELLOW);
        //drawRect 就是使用畫筆繪製一個矩形 前面兩個引數代表起始座標, 也就是左上角 後面兩個引數可以標識你想畫的長度和寬度 也可以理解為右下角的座標點。
        canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint);

        //設定畫筆畫出的內容顏色,設定畫出內容的所在位置座標
        paint.setColor(color);
        /**
         * drawText方法:繪製文字。引數:
         * text:要繪製的文字
         * x:繪製原點x座標
         * y:繪製原點y座標
         * paint:用來做畫的畫筆
         */
        canvas.drawText(text,(getWidth()-rect.width())/2,(getHeight()+rect.height())/2,paint);

        System.out.println("getWidth() = " + getWidth());
        System.out.println("getMeasuredWidth() = " + getMeasuredWidth());
    }

    /**
     *
     * @param w view改變後當前的寬
     * @param h  view改變後當前的高
     * @param oldw  view改變前的寬
     * @param oldh   view改變前的高
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        System.out.println("w = " + w + "  " + h  + " " + oldw + "  " + oldh);
    }

    public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}


 在values資料夾下的自定義矩形TextView使用的自定義屬性xml檔案

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--在declare-styleable控制元件中寫自定義view的屬性-->
    <!--    format表示自定義view屬性值的取值型別     -->
    <declare-styleable name="CustomTextView">
        <attr name="text" format="string"/>
        <attr name="textSize" format="dimension"/>
        <attr name="textColor" format="color"/>
    </declare-styleable>

</resources>

2. 自定義圓形進度條view

package com.bwie.CustomView.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

/**
 * 自定義顯示圓形進度條的view
 */
public class CustomProgressView extends View{
    private boolean runing = true;
    private int progress = 0;
    private Paint paint;

    public CustomProgressView(Context context) {
        super(context);
    }

    public CustomProgressView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //建立一個畫筆
        paint = new Paint();

        //設定畫筆的顏色
        paint.setColor(Color.BLUE);
        //設定畫筆的style:內容是填充的空心圓
        paint.setStyle(Paint.Style.STROKE);

        //執行進度條是耗時操作,需放在子執行緒中執行
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (runing){
                    if (progress >= 360){
                        runing = false;
                        return;
                    }
                    progress += 10;

                    //子執行緒重新整理方法:postInvalidate(),此事系統會呼叫onDraw()方法
                    postInvalidate();
                    try {
                        Thread.sleep(188);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //(1)獲取當前自定義view的寬高度
        int x = getWidth() / 2;
        int y = getHeight() / 2;

        //(2)設定圓半徑的大小
        int radius = 300;

        //(3)設定畫筆的粗細
        paint.setStrokeWidth(18);

        //(4)定義一個矩形區域:RectF物件持有一個矩形的四個float座標值
        RectF rectF = new RectF(x - radius, y - radius, x + radius, y + radius);
        /**
         *  畫一個圓弧:drawArc方法:繪製圓弧,該方法用於在畫布上繪製圓弧,通過指定圓弧所在的橢圓物件、起始角度、終止角度來實現。
         *   oval:圓弧所在的橢圓物件。
             startAngle:圓弧的起始角度。
             sweepAngle:圓弧的角度。
             useCenter:是否顯示半徑連線,true表示顯示圓弧與圓心的半徑連線,false表示不顯示。
             paint:繪製時所使用的畫筆。
         */
        canvas.drawArc(rectF,-90,progress,false,paint);

        //(5)把progress轉換為int值
        int intProgress = (int) ((float)progress/360 * 100);

        //(6) measureText  測量字串的寬度
        float textWidth = paint.measureText(intProgress + "%");
        //定義一個矩形區域:Rect物件持有一個矩形的四個integer座標值
        Rect rect = new Rect();
        //測量矩形中內容
        paint.getTextBounds(intProgress+"%",0,(intProgress+"%").length(),rect);

        //設定畫筆寫出內容的size和畫筆的粗細西
        paint.setTextSize(90);
        paint.setStrokeWidth(1);

        //畫出文字  rect.height() 獲取字串的高度
        /**
         * drawText方法引數:
         * text:要繪製的文字
         * x:繪製原點x座標
         * y:繪製原點y座標
         * paint:用來做畫的畫筆
         */
        canvas.drawText(intProgress+"%",x-textWidth/2,y+rect.height()/2,paint);
    }

    public CustomProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

3. 自定義圓環view

package com.bwie.CustomView.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
 * 自定義圓環view
 */
public class CustomCircleView extends View{
    private Paint paint;
    private int xCircle = 200;  //圓心的x座標
    private int yCircle = 200;  //圓心的y座標
    public CustomCircleView(Context context) {
        super(context);
    }

    //CustomCircleView方法:設定自定義圓形view的檢視
    public CustomCircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //設定畫筆的顏色,style,setAntiAlias 抗鋸齒形式(true為去除鋸齒,false則不去),setStrokeWidth方法:設定空心線寬
        paint = new Paint();
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(80);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                //getRawX()和getRawY()獲得的是相對螢幕的位置
                System.out.println("按下時:    x  "+event.getRawX()+"     y  "+event.getRawY());
                break;
            case MotionEvent.ACTION_MOVE:
                //getRawX()和getRawY()獲得的是相對螢幕的位置
                System.out.println("移動時:    x  "+event.getRawX()+"     y  "+event.getRawY());

                //獲取滑鼠移動時的座標,為X軸和Y軸座標重新賦值:getX()和getY()獲得的永遠是view的觸控位置座標
                xCircle = (int) event.getX();
                yCircle = (int)event.getY();
                /**
                 *  Android提供了Invalidate方法實現介面重新整理,但是Invalidate不能直接線上程中呼叫,因為他是違背了單執行緒模型:
                    1. Android UI操作並不是執行緒安全的,並且這些操作必須在UI執行緒中呼叫。
                    invalidate()是用來重新整理View的,必須是在UI執行緒中進行工作。比如在修改某個view的顯示時,呼叫invalidate()才能看到重新繪製的介面。invalidate()的呼叫是把之前的舊的view從主UI執行緒佇列中pop掉。
                   2.Android 程式預設情況下也只有一個程式,但一個程式下卻可以有許多個執行緒。在這麼多執行緒當中,把主要是負責控
                        制UI介面的顯示、更新和控制元件互動的執行緒稱為UI執行緒,由於onCreate()方法是由UI執行緒執行的,所以也可以把UI執行緒理解
                        為主執行緒。其餘的執行緒可以理解為工作者執行緒。invalidate()得在UI執行緒中被調動,在工作者執行緒中可以通過Handler來通
                        知UI執行緒進行介面更新。而postInvalidate()在工作者執行緒中被呼叫。
                 */
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                //getRawX()和getRawY()獲得的是相對螢幕的位置
                System.out.println("抬起時:    x  "+event.getRawX()+"     y  "+event.getRawY());
                break;
        }
        return true;
    }

    /**
     * onMeasure方法:計運算元控制元件的尺寸和模式,以及設定自己的寬和高
        widthMeasureSpec:當前父容器的widthMeasureSpec
        heightMeasureSpec:當前父容器的heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    //onDraw方法:繪圖方法(canvas畫布)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /**
         * canvas.drawCircle()方法:該方法用於在畫布上繪製圓形,通過指定圓形圓心的座標和半徑來實現。該方法是繪製圓形的主要方法,同時也可以通過設定畫筆的空心效果來繪製空心的圓形。
         *  cx:圓心的x座標。
            cy:圓心的y座標。
            radius:圓的半徑。
            paint:繪製時所使用的畫筆。
         */
        canvas.drawCircle(xCircle,yCircle,200,paint);
    }

    public CustomCircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

4.  3個自定義view共用的xml檔案

<?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" >

    <!--    如上:自定義view的佈局檔案必寫屬性:xmlns:app="http://schemas.android.com/apk/res-auto"-->

    <!--    自定義矩形TextView的view控制元件屬性  -->
    <!--    自定義view的屬性宣告時使用   app:     字首   -->
    <com.bwie.CustomView.view.CustomTextView
        android:layout_centerInParent="true"
        android:layout_marginTop="28dp"
        android:layout_width="188dp"
        android:layout_height="188dp"

        app:textSize="18sp"
        app:text="自定義TextView"
        app:textColor="@color/colorAccent"  />


    <!--    自定義圓形進度條view時的控制元件屬性  -->
<!--    <com.bwie.CustomView.view.CustomProgressView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />-->

    <!--    自定義圓形view時的控制元件屬性  -->
<!--    <com.bwie.CustomView.view.CustomCircleView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />-->

</RelativeLayout>


相關文章