对于刚开始学自定义渣渣的我,对自定义还是挺恐慌的,不过上手后就好了。
先总结下自定义View的步骤:
1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
3、绘制机制:重写onMesure (按需求使用)、重写onSizeChange()、重写onLayout(按需求使用)、重写onDraw
一、自定义View的属性
1、首先在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomView">
<attr name="border_color" format="color"></attr>
<attr name="border_width" format="dimension"></attr>
</declare-styleable>
</resources>
我们定义画笔颜色,画笔字体大小2个属性,format是值该属性的取值类型:
一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;不清楚的可以google一把。
然后在布局中声明我们的自定义View
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation ="vertical"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.weather.org.weather.DefindView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:border_color="#f44336"
app:border_width="2dp"/>
</LinearLayout>
一定要引入 xmlns:app
="http://schemas.android.com/apk/res-auto"或者具体到哪个包下
二 、在View的构造方法中,获得我们的自定义的样式
public class DefindView extends View{
private float mBorderWidth;
private int mBorderColor;
private Paint mPaint;
private RectF mBounds;
private float width ;
private float height;
private float radius;
private int smallLength ;
private int largeLength ;
private int[] FaceColors;
public DefindView(Context context)
{
super(context);
}
public DefindView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray =context.getTheme().obtainStyledAttributes(attrs,R.styleable.CustomView,0,0);
try{
mBorderColor =typedArray.getColor(R.styleable.CustomView_border_color,0xff000000);
mBorderWidth =typedArray.getDimension(R.styleable.CustomView_border_width,3);
}catch (Exception e)
{
e.printStackTrace();
}finally {
typedArray.recycle();
}
init();
}
public DefindView(Context context, AttributeSet attr, int defStyleAttr)
{
super(context,attr,defStyleAttr);
}
private void init(){
mPaint =new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mBorderWidth);
mPaint.setColor(mBorderColor);
FaceColors = new int[]{Color.rgb(0, 0, 0), Color.rgb(0, 0, 0), Color.rgb(0, 0, 0),
Color.rgb(0, 0, 0), Color.rgb(0, 0, 0), Color.rgb(76,175,80)};
}
三、我们重写onSizeChange,onDraw,onMesure调用系统提供的:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBounds =new RectF(getLeft(),getTop(),getRight(),getBottom());
width =mBounds.right - mBounds.left ;
height=mBounds.bottom -mBounds.top;
if(width <height)
{
radius =width/4;
}else{
radius =height/4;
}
smallLength =10;
largeLength =20;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthsize =MeasureSpec.getSize(widthMeasureSpec);
int widthmode =MeasureSpec.getMode(widthMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(0xff000000);
mPaint.setColor(0x66555555);
canvas.drawRoundRect(new RectF(mBounds.centerX()-(float)0.9*width/2,mBounds.centerY()
-(float)0.9*height/2,mBounds.centerX()+(float)0.9*width/2,mBounds.centerY()+(float)0.9*height/2),30,30,mPaint);
mPaint.setColor(mBorderColor);
canvas.drawCircle(mBounds.centerX(),mBounds.centerY(),radius,mPaint);
drawGriend(canvas);
float start_x,start_y;
float end_x,end_y;
for (int i=0;i<60;++i)
{
start_x =radius*(float)Math.cos(Math.PI/180*i*6);
start_y =radius*(float)Math.sin(Math.PI/180*i*6);
if(i%5==0)
{
end_x =start_x+largeLength*(float)Math.cos(Math.PI/180*i*6);
end_y =start_y+largeLength*(float)Math.sin(Math.PI/180*i*6);
}else
{
end_x =start_x+smallLength*(float)Math.cos(Math.PI/180*i*6);
end_y =start_y+smallLength*(float)Math.sin(Math.PI/180*i*6);
}
start_x+=mBounds.centerX();
end_x += mBounds.centerX();
start_y +=mBounds.centerY();
end_y +=mBounds.centerY();
canvas.drawLine(start_x,start_y,end_x,end_y,mPaint);
}
canvas.drawCircle(mBounds.centerX(),mBounds.centerY(),20,mPaint);
//canvas.rotate(60,mBounds.centerX(),mBounds.centerY());
canvas.drawLine(mBounds.centerX(),mBounds.centerY(),mBounds.centerX(),mBounds.centerY()-radius,mPaint);
}
// 渐变色处理
private void drawGriend(Canvas canvas)
{
GradientDrawable gr = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, FaceColors);
gr.setShape(GradientDrawable.OVAL);
gr.setBounds((int)mBounds.centerX()-(int)width/4,(int)mBounds.centerY()-(int)width/4,(int)mBounds.centerX()+(int)width/4,(int)mBounds.centerY()+(int)width/4);
gr.setGradientType(GradientDrawable.RADIAL_GRADIENT);
gr.setGradientRadius(mBounds.width()/4);
gr.draw(canvas);
}
}
四、关于onMeasure
系统帮我们测量的高度和宽度都是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。
所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMesure方法”:
重写之前先了解MeasureSpec的specMode,一共三种类型:
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
效果图: