1. 程式人生 > >Android自定義View:帶背景顏色的TextView和條形圖--(1)

Android自定義View:帶背景顏色的TextView和條形圖--(1)

  • 初始:
    最近在看《Android群英傳》一書,程式碼自己敲了一遍,想想之前敲了又忘記的慘痛經歷,決定在部落格上記錄自己敲的程式碼,有幾個寫幾篇,放在一個系列裡邊,就這樣,以後看就能一下子找到了。

  • 自定義View
    自定義View我們大致可以從是三個方面著手:
    (1)對現有的控制元件進行擴充套件
    (2)通過組合實現全新的控制元件
    (3)重寫View來實現全新的
    沒怎麼彙總,用到什麼知識就註解解釋了。

  • 對現有的控制元件進行擴充套件
    基於TextView,繪製邊框和背景顏色
    關於Paint和Canvas,他們就相當於畫畫時候的畫筆和畫板,沒有他們我們就不能改變任何東西。

/**
 * 基於已有空間上進行修改
 * 寫一個TextView  繪製邊框  繪製背景顏色
 * @author
fanshenxia * 一個自定義類繼承TextView 重寫構造方法 */
public class MyTextView extends TextView{ private Paint mPaint,mPaint2; //這個不太清楚,重寫了就重寫了 public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } //這個構造用於xml檔案中的構造
public MyTextView(Context context, AttributeSet attrs) { super(context, attrs); init(); } //這個構造方法用於在程式碼中定義控制元件 public MyTextView(Context context) { super(context); init(); } /** * 初始化畫筆 */ private void init() { //例項化畫筆 mPaint = new
Paint(); //設定畫筆顏色 mPaint.setColor(Color.CYAN); //設定它的填充方法,用的多的是FILL 和 STORKE mPaint.setStyle(Paint.Style.FILL); mPaint2 = new Paint(); mPaint2.setColor(Color.LTGRAY); mPaint2.setStyle(Style.FILL); } /** * 重寫onDraw方法 可以在繪製文字前後進行一些自己的操作 * super.onDraw(canvas);呼叫父類方法繪製文字 * 如果繪製矩形的程式碼寫在它的後邊,文字就會被覆蓋 */ @Override protected void onDraw(Canvas canvas) { //在回撥父類方法之前,對TextView來說是繪製文字內容之前 canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint); //繪製裡層矩形 引數:左、上、右、下、畫筆 //除了繪製矩形,用的多的還可以繪製線,圓,扇形,Path等 canvas.drawRect(10, 10, getMeasuredWidth()-10, getMeasuredHeight()-10, mPaint2); super.onDraw(canvas); //繪製文字之後 } }

XML中MyTextView內容:

<custom.MyTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:padding="10dp"
        android:text="我是自定義的控制元件哦~"
        android:textSize="20sp"
        android:visibility="visible" >
    </custom.MyTextView>

執行效果:
這裡寫圖片描述

總結:可以看到,上述xml中TextView的相關屬性保留著,我們沒有自定義其他的任何屬性,只是添加了邊框和背景。 自定義屬性在下邊呢~

  • 通過組合來實現新的控制元件
    這個應該用過吧,平時如果UI給我們的原型圖有好多地方搜尋框都是一樣的,我們會自定義一個控制元件,將它的大體佈局寫好,到用的地方就不用一次一次的寫了。
    這個簡單,我沒寫程式碼,以後把它補上。

  • 自定義全新的View
    這裡實現了三個自定義View,一個條形圖,一個鐘錶繪製,一個百分比圖。先說條形圖。

/**
 * 自定義條形圖
 * 
 * @author fanshenxia
 *
 */
public class MyBarChartView extends View {
    private Context mContext;
    // 柱狀的數值
    private int mNum = 15;
    private Paint mPaint;
    public MyBarChartView(Context context) {
        super(context);
        this.mContext = context;
        init();
    }
    public MyBarChartView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        init();
    }
    public MyBarChartView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        init();
    }
    private void init() {
        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(2);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Style.FILL);
    }
    /**
        重寫onMeasure()方法重新測量控制元件的寬高
        測量方式有三種
        MeasureSpec.EXACTLY:相當xml中控制元件layout_width="match_parent/固定XXdp"
        MeasureSpec.AT_MOST:想當wrap_content,它不能大於父控制元件的寬/高
        MeasureSpec.UNSPECIFIED:不指定其大小測量模式,View想多大就多大,一般用於自定義View
    */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }

    private int measureWidth(int measureSpec) {
        int result = 300;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY) {
            // 指定數值或者match_parent
            result = specSize;
        } else if (specMode == MeasureSpec.AT_MOST) {
            // 為warp_content時
            result = Math.min(result, specSize);
        } else {
            // view想多大就多大
            result = 300;
        }
        return result;
    }

    private int measureHeight(int measureSpec) {
        int result = 400;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY) {
            // 指定數值或者match_parent
            result = specSize;
        } else if (specMode == MeasureSpec.AT_MOST) {
            // 為warp_content時
            result = Math.min(result, specSize);
        } else {
            // view想多大就多大
            result = 400;
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        int rectWidth = (width - 60) / mNum - 20;

        // 繪製座標線
        canvas.drawLine(20, 20, 20, height - 20, mPaint);
        canvas.drawLine(getWidth() - 20, getHeight() - 20, 20, getHeight() - 20, mPaint);

        int min = 50;
        int max = getHeight()-50;
        Random random = new Random();
        int num = random.nextInt(max) % (max - min + 1) + min;
        // 繪製柱體  這裡給的值不是很邏輯的,用的時候需要自己計算後給出相應的值。
        for (int i = 0; i < mNum; i++) {
            canvas.drawRect(20 + rectWidth * i + 20 * (i + 1), num, 20 + rectWidth * (i + 1) + 20 * (i + 1),
                    getHeight() - 20, mPaint);
            num = random.nextInt(max) % (max - min + 1) + min;
        }

        //延時重繪,讓條形圖每1s重新整理一次,它走這個方法時 ,只會執行onDraw方法   init那些不執行,Paint的顏色值等都會保留
        postInvalidateDelayed(1*1000);

    }

}

xml佈局:

<custom.MyBarChartView
        android:id="@+id/myBarChartView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

效果:

這裡寫圖片描述模擬器效果錄屏效果不太好