本文作爲自定義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);
}
}
}