xml创建自定义View
一、创建View的属性
在values中创建attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TestView">
<attr name="test_boolean" format="boolean"></attr>
<attr name="test_string" format="string"></attr>
<attr name="test_integer" format="integer"></attr>
<attr name="test_emum" format="enum">
<enum name="top" value="1"></enum>
<enum name="bottom" value="2"></enum>
</attr>
<attr name="test_dimension" format="dimension"></attr>
</declare-styleable>
</resources>
二、创建View类
package com.example.testcustomview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
public class TestView extends View {
private static final String TAG = "TestView";
private boolean booleanTest;
private int integerTest;
private String stringTest = "TestString";
private int enumTest;
private float dimensionTest;
private Paint mPaint;
public TestView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initPant();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TestView);
booleanTest = typedArray.getBoolean(R.styleable.TestView_test_boolean, false);
int count = typedArray.getIndexCount();
for (int i=0;i<count;i++) {
int index = typedArray.getIndex(i);
switch (index) {
case R.styleable.TestView_test_boolean:
booleanTest = typedArray.getBoolean(R.styleable.TestView_test_boolean,false);
break;
case R.styleable.TestView_test_integer:
integerTest = typedArray.getInteger(R.styleable.TestView_test_integer,0);
break;
case R.styleable.TestView_test_string:
stringTest = typedArray.getString(R.styleable.TestView_test_string);
break;
case R.styleable.TestView_test_emum:
enumTest = typedArray.getInt(R.styleable.TestView_test_emum,1);
break;
case R.styleable.TestView_test_dimension:
dimensionTest = typedArray.getDimension(R.styleable.TestView_test_dimension,0);
break;
}
}
Log.d(TAG, "TestView() called with: context = [" + context + "], attrs = [" + attrs + "]");
Log.d(TAG, "TestView: \n booleanTest:"+booleanTest +
"\n integerTest:"+integerTest +
"\n stringTest:"+stringTest +
"\n enumTest:"+enumTest +
"\n dimensionTest:"+dimensionTest);
//回收
typedArray.recycle();
}
private void initPant() {
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(6);
mPaint.setColor(0xFFFF0000);
mPaint.setAntiAlias(true);
}
//测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
//本控件宽
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int width = 0;
// MeasureSpec.EXACTLY 确定宽高
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
int needWidth = measureWidth() + getPaddingLeft() + getPaddingRight();
// MeasureSpec.AT_MOST 不错过给定宽度
if (widthMode == MeasureSpec.AT_MOST) {
width = Math.min(needWidth,widthSize);
} else { //MeasureSpec.UNSPECIFIED 按照计算宽度
width = needWidth;
}
}
//本控件高
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int height = 0;
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
int needHeight = measureHight() + getPaddingTop() + getPaddingBottom();
if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(needHeight,heightSize);
} else {
height = needHeight;
}
}
setMeasuredDimension(width,height);
}
private int measureHight() {
return 0;
}
private int measureWidth() {
return 0;
}
//绘制
@Override
protected void onDraw(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(6);
canvas.drawCircle(getWidth()/2,getHeight()/2,getWidth()/2-mPaint.getStrokeWidth(),mPaint);
mPaint.setStrokeWidth(1);
//横线
canvas.drawLine(0,getHeight()/2,getWidth(),getHeight()/2,mPaint);
//竖线
canvas.drawLine(getWidth()/2,0,getWidth()/2,getHeight(),mPaint);
mPaint.setTextSize(96);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawText(stringTest,0,getHeight(),mPaint);
}
//触发事件
@Override
public boolean onTouchEvent(MotionEvent event) {
stringTest = "8888";
invalidate();
return true;
}
private static final String INSTANCE = "instance";
private static final String KEY_TEXT = "key_text";
//点击后stringTest改为8888,为保存状态用下面两个方法
//保存数据状态
@Nullable
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putString(KEY_TEXT,stringTest);
//保存父View的状态
bundle.putParcelable(INSTANCE,super.onSaveInstanceState());
return bundle;
}
//获取状态
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
Parcelable parcelable = bundle.getParcelable(INSTANCE);
super.onRestoreInstanceState(parcelable);
stringTest = bundle.getString(KEY_TEXT);
return;
}
super.onRestoreInstanceState(state);
}
}
三、在activity_main.xml中加入View
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.testcustomview.TestView
android:id="@+id/mainView"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerInParent="true"
android:background="@color/colorPrimaryDark"
app:test_boolean="true"
app:test_integer="1"
app:test_string="abcgjef"
app:test_dimension="100dp"
app:test_emum="bottom">
</com.example.testcustomview.TestView>
</RelativeLayout>
网友评论