【一看就懂】自定義View入門實踐,實現外方內圓效果

本文作爲自定義View的初步實踐,取材自《android開發藝術探索》

效果如下

在這裏插入圖片描述

方法

activity_main.xml

這是我們自己設計的CircleView控件,其在xml中如下表示

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <com.example.makeview.CircleView
        android:id="@+id/circle_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:circle_color="#FFFF00"
        android:padding="20dp"
        android:background="#000000"
        tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>

其中

 xmlns:app="http://schemas.android.com/apk/res-auto"

表示自定義屬性,使得我們可以在CircleView中使用

         app:circle_color="#FFFF00"

那circle哪來的呢?這裏我們要爲CircleView構建屬於自己的attr.xml屬性

attr.xml屬性

在values新建attr.xml
寫上

<?xml version="1.0" encoding="utf-8"?>
<!--給CircleView自定義屬性,屬性名爲circle_color,
格式爲color,id=R.styleable.CircleView_circle_color-->
<resources>
    <declare-styleable name="CircleView">
        <attr name="circle_color" format="color" />
    </declare-styleable>
</resources>

其中資源爲styleable ,和id,layout同級,名字爲CircleView,這裏我們定義的屬性名爲circle_color,屬性是color,當然也可以定義其他format

CircleView

接下來再在新建的CircleView類中取到color,在構造方法中

  public CircleView(Context context, AttributeSet attributeSet) {
        super(context,attributeSet);
        TypedArray a = context.obtainStyledAttributes(attributeSet,R.styleable.CircleView);
        mColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED);
        a.recycle();
        Log.d(TAG, "CircleView: Second");
        init();
    }
   private void init() {
        mPaint.setColor(mColor);
    }

用TypedAtrray獲取,如圖
在這裏插入圖片描述

重寫onDraw()

然後我們再將已經獲取顏色的Paint用canvas畫出圓形,重寫onDraw方法

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingLeft();
        int paddingTop = getPaddingLeft();
        int paddingBottom = getPaddingLeft();
        int width = getWidth() - paddingLeft - paddingRight; //縮減寬高
        int height = getHeight() - paddingTop - paddingBottom;
        int radius = Math.min(width,height)/2;
        canvas.drawCircle(width/2+paddingLeft,height/2+paddingBottom,radius,mPaint);
    }

這裏我們畫了很大的精力取計算Padding,因爲我們在activity_main中設置了padding,但是自定義控件默認是不支持的,所以我們要自己改,具體是將width左右都減小padding,由於我們的圓心是由width和height決定,爲了不影響圓心位置,我們在畫的時候加上一個padding即可,這樣看起來就是畫的圓形兩邊都縮小了,但是位置不變

重寫onMeasure()

另外一個需要重寫的是onMeasure(),主要用來做尺寸的測量,因爲我們在activity_main中設置了wrap_content,但是自定義控件不知道wrap_content是多少,只當match_content,所以我們要在這裏設定

 @Override
    protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, 200);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize,200);
        }
    }

這裏默認設置成200

java源代碼

package com.example.makeview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;



public class CircleView extends View {
    final String TAG = "CircleView";
    private int mColor =Color.RED;
    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public CircleView(Context context) {
        super(context);
        Log.d(TAG, "CircleView: First");
        init();
    }

    public CircleView(Context context, AttributeSet attributeSet) {
        super(context,attributeSet);
        TypedArray a = context.obtainStyledAttributes(attributeSet,R.styleable.CircleView);
        mColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED);
        a.recycle();
        Log.d(TAG, "CircleView: Second");
        init();
    }

    public CircleView(Context context, AttributeSet attributeSet, int defStyleAttr) {
        super(context, attributeSet, defStyleAttr);
        Log.d(TAG, "CircleView: Third");

        init();
    }

    private void init() {
        mPaint.setColor(mColor);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingLeft();
        int paddingTop = getPaddingLeft();
        int paddingBottom = getPaddingLeft();
        int width = getWidth() - paddingLeft - paddingRight; //縮減寬高
        int height = getHeight() - paddingTop - paddingBottom;
        int radius = Math.min(width,height)/2;
        canvas.drawCircle(width/2+paddingLeft,height/2+paddingBottom,radius,mPaint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec){
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, 200);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(200, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize,200);
        }
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章