关于简单的自定义view以及相关知识
自定义View的简单步骤
1.新建一个Class继承自View(自定义View一般有直接继承View,某个具体的控件如ImageView,ViewGroup三种)。
2.在values中styles自定义属性。
3.在自定义View类中实现至少两个构造方法并完成属性以及画笔的初始化。
4.重写onMeasure(int,int)(看需求需不需要重写,View的onMeasure wrap_content和match_parent的效果一样,模式都是EXACTLY)和onDraw(Canvas)方法。
public class CustomView extends View {
private Paint mPaint;
private int color;
private float borderWith;
public CustomView(Context context){
this(context,null);
}
public CustomView(Context context,AttributeSet attrs){
super(context,attrs);
init(attrs);
}
public void init(AttributeSet attrs){
if(attrs != null){
//获得自定义View的属性集
TypedArray array = getContext().obtainStyledAttributes(attrs,R.styleable.CustomView);
//第二个参数表示defalut值
color = array.getColor(R.styleable.CustomView_borderColor,0xdddddd);
borderWith = array.getDimension(R.styleable.CustomView_borderWith,2);
/
/TypedArrary对象使用完一定要回收
array.recycle();
}
//初始化画笔
mPaint = new Paint();
}
@Override
public void onMeasure(int withMeasureSpec,int heightMeasureSpec){
super.onMeasure(withMeasureSpec,heightMeasureSpec);
}
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
/
/消除锯齿
mPaint.setAntiAlias(true);
/
/空心画笔
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(color);
/
/画笔粗细
mPaint.setStrokeWidth(borderWith);
/
/在cavans上绘制
canvas.drawCircle(getWidth()/2,getHeight()/2,100,mPaint);
布局文件
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.shinelon.defineview.CustomView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
app:borderColor="#000000"
app:borderWith="5dp"/>
<com.example.shinelon.defineview.CustomView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
app:borderColor="#000000"
app:borderWith="5dp"/>
</LinearLayout>
自定义属性
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<declare-styleable name="CustomView">
<attr name = "borderColor" format="color"/>
<attr name = "borderWith" format="dimension"/>
</declare-styleable>
</resources>
关于布局的命名空间,
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
第一个表示使用系统自带属性,第二个表示使用库或者自定义属性。第二个命名空间可以随意命名,一般为命为app或者自定义的相关名字。需要注意的是,在Eclipse中自定义的res-auto必须是自定义View的包名,而在AndroidStudio中必须只能是res-auto。
关于onMeasure(int withMeasureSpec,int heightMeasureSpec)
其中withMeasureSpec和heightMeasureSpec均为父View传过来的int型数据,其中32位里的前两位由模式组成,后面为表示宽(高)的数据。关于模式,分别有EXACTLY,AT_MOST和UNSPECIFIED三种。父View的ViewFGroup会根据子View的请求来决定传给子View的参数具体是什么,当子View为match_parent或者wrap_content时,模式均为EXACTLY。第三个UNSEPECIFIED极少用。
MeasureSpec.getMode(withMeasureSpec)可以获得模式,同理MeasureSpec.getSize()获得大小
MeasureSpec.EXACTLY:父视图希望子视图的大小应该是specSize中指定的。
MeasureSpec.AT_MOST:子视图的大小最多是specSize中指定的值,也就是说不建议子视图的大小超过specSize中给定的值。
MeasureSpec.UNSPECIFIED:我们可以随意指定视图的大小。
----------
源码
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
// Mode = UNSPECIFIED使用提供的默认大小
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
// Mode = EXACTLY,AT_MOST时使用测量的大小
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
可见,具体的测量任务,常常还需要我们自己进行测量。好了,下次学习ViewGroup并把心得写下来。