前几天写的demo没法自己实现在xml声明使用,而是把radius之类的写死了。这样很不方便。
于是继续改进。 栋哥推荐的书的作者,刚神 果然非常厉害。
首先 需要对ondraw方法重写一下。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int width = getWidth()-paddingLeft-paddingRight;
int height = getHeight()-paddingTop-paddingTop;
int radius = Math.min(width,height)/2;
// 绘制圆环
canvas.drawCircle(width/2, height/2, radius, mPaint);
}
这样就可以完成自定义长宽高了,并且半径自动适应。
其实这时候我们还可以进一步完善,给他们提供attr 就是我们自定义的属性。什么是自定义的属性?比如我们用android:layout_width,其实就是android系统给我们提供的。注意 ,如果我们要使用自定义属性 请在布局代码中加入;
xmlns:app="http://schemas.android.com/apk/res-auto"
例如:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
这里的xmlns:android=“http://schemas.android.com/apk/res/android” 就是自动扫描android给我们提供的默认属性。
第一步 在values目录下 创建xml文件,记住要以attrs开头
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<declare-styleable name="CustomView">
<!--->attr name是我们自己定义的名字,format是android给我们提供的格式 可以搜一下<!-->
<attr name="circle_color" format="color"></attr>
<!--->照虎画🐱 自己定一个属性试一下<!-->
<attr name="circle_style" format="float"></attr>
</declare-styleable>
</resources>
这样就ok的设置了自定义的属性。
接下来就是我们view控件里的操作了,比如这里。先声明;
private int mColor ;
private float mStrokeWidth;
接着我们就要取出在布局中定义的属性,若没有定义,我们应该默认一个。
public CustomView(Context context,AttributeSet attrs,int defStyleAttr){
super(context,attrs,defStyleAttr);
TypedArray a =context.obtainStyledAttributes(attrs,R.styleable.CustomView);
mColor = a.getColor(R.styleable.CustomView_circle_color,Color.RED);
mStrokeWidth = a.getFloat(R.styleable.CustomView_circle_style,20);
a.recycle();
initPaint();
}
ok,基本大功告成,在init的时候对画笔什么的进行相关设置即可。贴上完整的view代码。
package com.example.testviewgroup;
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 CustomView extends View {
private Paint mPaint;// 画笔
private int mColor ;
private float mStrokeWidth;
public CustomView(Context context) {
this(context, null);
// 初始化画笔
initPaint();
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CustomView(Context context,AttributeSet attrs,int defStyleAttr){
super(context,attrs,defStyleAttr);
TypedArray a =context.obtainStyledAttributes(attrs,R.styleable.CustomView);
mColor = a.getColor(R.styleable.CustomView_circle_color,Color.RED);
mStrokeWidth = a.getFloat(R.styleable.CustomView_circle_style,20);
a.recycle();
initPaint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 初始化画笔
*/
private void initPaint() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
/*
* 设置画笔样式为描边,圆环嘛……当然不能填充不然就么意思了
*
* 画笔样式分三种:
* 1.Paint.Style.STROKE:描边
* 2.Paint.Style.FILL_AND_STROKE:描边并填充
* 3.Paint.Style.FILL:填充
*/
//描边
mPaint.setStyle(Paint.Style.STROKE);
//为画笔设置咱们自己的颜色属性
mPaint.setColor(mColor);
mPaint.setStrokeWidth(mStrokeWidth);
/*
* 设置描边的粗细,单位:像素px
* 注意:当setStrokeWidth(0)的时候描边宽度并不为0而是只占一个像素
*/
// mPaint.setStrokeWidth(mStrokeWidth);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);`在这里插入代码片`
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int width = getWidth()-paddingLeft-paddingRight;
int height = getHeight()-paddingTop-paddingTop;
int radius = Math.min(width,height)/2;
// 绘制圆环
canvas.drawCircle(width/2, height/2, radius, mPaint);
}}
我们定义了一个圆环,提供了两个自定义属性,分别是颜色和秒边宽度,若不定义,默认为红色和20.
👌
这个时候问题又来了,根据绘制源码知道,若不对onMeasure加以处理,在对自定义view设置wrap_content时,会等于父控件的大小,就等于math_parent, 显然是不对的。
所以对onMeasure进行重写。
@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(widthMeasureSpec ==MeasureSpec.AT_MOST){
setMeasuredDimension(200,heightSpecSize);
}
else if(heightSpecMode == MeasureSpec.AT_MOST){
setMeasuredDimension(widthSpecSize,200);
}
}
上述代码是的200是我们默认设置的(AT_MOST下的自定义宽度,关于MeasureSpec,您可以自行搜索,这里推荐一本书《android开发艺术探索》),你也可以自己设置它。
-------------分割线----------------
但是问题又来了,我们要考虑用户使用,这是一个圆。当用户设定宽/高其中一个为 确切的 值,另一个为wrap_content时候,我们显然应该是将其中一个设定的为圆。并且处理一下我们圆的线宽,以免不能完整显示。故再次修改一下,完整代码如下
package com.example.testviewgroup;
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 CustomView extends View {
private Paint mPaint;// 画笔
private int mColor;
private float mStrokeWidth;
public CustomView(Context context) {
this(context, null);
// 初始化画笔
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
mColor = a.getColor(R.styleable.CustomView_circle_color, Color.RED);
mStrokeWidth = a.getFloat(R.styleable.CustomView_circle_style, 20);
a.recycle();
initPaint();
}
@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 (widthMeasureSpec == MeasureSpec.AT_MOST) {
setMeasuredDimension(heightSpecSize, heightSpecSize);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, widthSpecSize);
}
}
/**
* 初始化画笔
*/
private void initPaint() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
/*
* 设置画笔样式为描边,圆环嘛……当然不能填充不然就么意思了
*
* 画笔样式分三种:
* 1.Paint.Style.STROKE:描边
* 2.Paint.Style.FILL_AND_STROKE:描边并填充
* 3.Paint.Style.FILL:填充
*/
//描边
mPaint.setStyle(Paint.Style.STROKE);
//为画笔设置咱们自己的颜色属性
mPaint.setColor(mColor);
mPaint.setStrokeWidth(mStrokeWidth);
/*
* 设置描边的粗细,单位:像素px
* 注意:当setStrokeWidth(0)的时候描边宽度并不为0而是只占一个像素
*/
// mPaint.setStrokeWidth(mStrokeWidth);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int width = getWidth() - paddingLeft - paddingRight;
int height = getHeight() - paddingTop - paddingTop;
int radius = Math.min(width, height) / 2;
// 绘制圆环
canvas.drawCircle(width / 2, height / 2, radius - mStrokeWidth, mPaint);
}
}
然后我们可以提供一些优先级之类的属性,这个自己去做就好了,要搭配自定义的ViewGroup使用。