Android 自定義View 三板斧之三——重寫View來實現全新控件

  一般狀況下,Android實現自定義控件無非三種方式。canvas

  Ⅰ、繼承現有控件,對其控件的功能進行拓展。dom

  Ⅱ、將現有控件進行組合,實現功能更增強大控件。ide

  Ⅲ、重寫View實現全新的控件函數

  本文來討論最難的一種自定義控件形式,重寫View來實現全新的控件。佈局

  首先,咱們要明白在什麼樣的狀況下,須要重寫View來實現一種全新的控件,通常當咱們遇到了原生控件沒法知足咱們現有的需求的時候,咱們此時就能夠考慮建立一個全新的View來實現咱們所須要的功能。建立一個全新View實現自定義控件,無非分紅這麼幾步:post

  Ⅰ、在OnMeasure()方法中,測量自定義控件的大小,使自定義控件可以自適應佈局各類各樣的需求。this

  Ⅱ、在OnDraw()方法中,利用哼哈二將(Canvas與Paint)來繪製要顯示的內容。spa

  Ⅲ、在OnLayout()方法中來肯定控件顯示位置。code

  Ⅳ、在OnTouchEvent()方法處理控件的觸摸事件。orm

  相應的思惟導圖以下:

  多說無益,咱們經過幾個小案例,來說解到底如何實現自定義控件。

  1、一個帶有比例進度的環形控件

   首先看一下這個控件的示意圖:

 

  經過這個簡單的示意圖,咱們對於項目所完成的比例,就一目瞭然了。經過這個簡單的示意圖,咱們能夠很快速的把這個圖形分紅三個部分。Ⅰ、外層的環,Ⅱ、裏面的園,3、相應文字。有了這個思路之後,咱們只須要在onDraw()方法中一個個進行繪製罷了。我這裏爲了簡單起見,把這個控件的寬度設置爲屏幕的寬度。

  首先,仍是老樣子,爲自定義控件設置一些自定義屬性,便於調用者對其進行擴展,自定義屬性的設置代碼爲:

  <declare-styleable name="circleView">
        <attr name="textSize" format="dimension" />
        <attr name="text" format="string" />
        <attr name="circleColor" format="color" />
        <attr name="arcColor" format="color" />
        <attr name="textColor" format="color" />
        <attr name="startAngle" format="integer" />
        <attr name="sweepAngle" format="integer" />
    </declare-styleable>

  Ⅰ、textSize——對應中間文本文字的大小

  Ⅱ、text——對應中間文本

  Ⅲ、circleColor——對應內圓的顏色

  Ⅳ、arcColor——對應外環的顏色

  Ⅴ、textColor——對應文本的顏色

  Ⅵ、startAngle——對應外環的起始角度

  Ⅶ、sweepAngle——對應外環掃描角度

  緊接着,就是在自定義控件的初始化方法中來獲取這些自定義屬性:

TypedArray ta = context.obtainStyledAttributes(attrs,
                R.styleable.circleView);
        if (ta != null) {
            circleColor = ta.getColor(R.styleable.circleView_circleColor, 0);
            arcColor = ta.getColor(R.styleable.circleView_arcColor, 0);
            textColor = ta.getColor(R.styleable.circleView_textColor, 0);
            textSize = ta.getDimension(R.styleable.circleView_textSize, 50);
            text = ta.getString(R.styleable.circleView_text);
            startAngle = ta.getInt(R.styleable.circleView_startAngle, 0);
            sweepAngle = ta.getInt(R.styleable.circleView_sweepAngle, 90);
            ta.recycle();
        }

  這裏在多說一嘴子,爲了釋放更多的資源,必定要將TypedArray這個對象進行資源的釋放。

  接下來,在OnMeasure()方法中,初始化要繪製畫筆樣式,獲取屏幕的寬度,計算中間位置的座標,以及指定外接矩形的寬高代碼以下:

    private void init() {
        int length = Math.min(width, height);
        mCircleXY = length / 2;
        mRadius = length * 0.5f / 2;
        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCirclePaint.setColor(circleColor);
        mRectF = new RectF(length * 0.1f, length * 0.1f, length * 0.9f,
                length * 0.9f);

        mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mArcPaint.setColor(arcColor);
        mArcPaint.setStyle(Paint.Style.STROKE);
        mArcPaint.setStrokeWidth((width * 0.1f));

        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextSize(textSize);
        mTextPaint.setColor(textColor);
        mTextPaint.setTextAlign(Align.CENTER);

    }

  將咱們設置的自定義屬性,設置給不一樣筆刷。

  作了這麼多準備之後,咱們所需的就是在OnDraw方法中繪製內圓、外環與文字。代碼以下:

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

    private void drawSth(Canvas canvas) {
        canvas.drawCircle(mCircleXY, mCircleXY, mRadius, mCirclePaint);
        canvas.drawArc(mRectF, startAngle, sweepAngle, false, mArcPaint);
        canvas.drawText(text, 0, text.length(), mCircleXY, mCircleXY + textSize
                / 4, mTextPaint);
    }

  須要指出的是,畫環形須要在一個指定矩形區域畫取,而且要指定起始角度,掃描角度,這些變量都是自定義屬性。 

  這個自定義控件的最終的運行效果爲:

  2、動態條形圖

  條形圖,應該在圖表展現系統中再普通不過的一種圖標了。靜態示意圖就像這樣:

   經過這個簡單的示意圖,咱們所須要作的是,繪製一個個的矩形,而後將每個矩形x座標平移必定的單位,咱們還看到這麼一個現象:每一個條形圖的起始點不一致,而終止點是同樣的。起始座標用個Random(隨機函數)剛恰好,實現靜態條形圖就是這樣的思路:

   首先,在OnMeasure()方法中計算出每一個矩形寬與高,這裏爲了方便起見,每一個矩形默認的高爲屏幕的高,每一個矩形的寬這裏定義爲屏幕的寬度乘以80%除以矩形的個數。而後根據寬與高來初始化筆刷(Paint)。爲何要根據寬與高來初始化筆刷了,這裏我爲了使自定義View更加的逼真,我這裏使用LinearGradient(線性渲染器)進行了渲染,這個對象須要使用矩形寬與高。須要指出來的是這個自定義控件是動態的,我只須要onDraw方法不斷髮生重繪,這裏爲了防止控件刷新太快,我這裏每隔300毫秒刷新視圖。這個控件的完整源代碼以下:

public class VolumneView extends View {

    private Paint mPaint;
    private int mCount;
    private int mWidth;
    private int mRectHeight;
    private int mRectWidth;
    private LinearGradient mLinearGradient;
    private double mRandom;
    private float mcurrentHeight;

    public static final int OFFSET = 5;

    public VolumneView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context, attrs);

    }

    private void initView(Context context, AttributeSet attrs) {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.GREEN);
        mPaint.setStyle(Paint.Style.FILL);
        TypedArray ta = context.obtainStyledAttributes(attrs,
                R.styleable.volumneView);
        if (ta != null) {
            mCount = ta.getInt(R.styleable.volumneView_count, 6);
            ta.recycle();
        }
    }

    public VolumneView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = getMeasuredWidth();
        mRectHeight = getMeasuredHeight();
        mRectWidth = (int) (mWidth * 0.8 / mCount);
        mLinearGradient = new LinearGradient(0, 0, mRectWidth, mRectHeight,
                Color.GREEN, Color.YELLOW, TileMode.CLAMP);
        mPaint.setShader(mLinearGradient);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < mCount; i++) {
            mRandom = Math.random();
            mcurrentHeight = (float) (mRectHeight * mRandom);
            float width = (float) (mWidth * 0.4 / 2 + OFFSET);
            canvas.drawRect(width + i * mRectWidth, mcurrentHeight, width
                    + (i + 1) * mRectWidth, mRectHeight, mPaint);
        }
        postInvalidateDelayed(300);
    }

  最終,運行效果以下:

  後記,經過這兩個簡單控件,相信你們對於自定義控件基本步驟有說了解,固然,要真正作好自定義控件的話,咱們還須要按這個步驟一步步的來。任重而道遠。

  本人才疏學淺,懇請吐槽。

相關文章
相關標籤/搜索