自定义View对于很多新手来说都是谈之色变,当然我也不例外,但是在某些情况下需要更加炫酷的效果,更加人性化的体验还是不得不自己去撸一些特定的View出来。自定义View是我们进阶路上的拦路虎,更是我们进阶必备的技能之一。本文主要是记录自身的学习,同时指导新手的学习,高手请避让撸代码去。。。
本文就从自定义最基础的部分开始聊起。
自定义View包含的主要内容如下:
1)自定义属性。
2)测量(onMeasure)、绘制(onDraw)。
下面看下主要实现的效果(实现中间显示文字的View,类似于TextView,可设置字体颜色,字体大小,显示内容等等):
实现过程如下:
1)继承自View,重写其构造方法:
如下:
public DemoTextView01(Context context) {
this(context,null);
}
public DemoTextView01(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public DemoTextView01(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
1,在values目录下创建attrs.xml文件
内容如下(也可将上面的定义直接写在下面的标签内):
2,在自定义View中获取相关的属性值如下(直接在初始化的时候完成,即构造器中):
// 获取到我们自定义的属性值
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DemoTextView01,defStyleAttr,0);
// 遍历取到相关的值
for (int i = 0; i < array.getIndexCount(); i++) {
//取到相关属性的ID值,根据Id匹配取到相关的值
int attr = array.getIndex(i);
switch (attr){
case R.styleable.DemoTextView01_text://指定的属性对应的ID值写法:R.styleable.类名_属性名
mText=array.getString(attr);
break;
case R.styleable.DemoTextView01_textColor:
//取到设置的字体颜色的值,默认的字体颜色为黑色
mTextColor=array.getColor(attr, Color.BLACK);
break;
case R.styleable.DemoTextView01_textSize:
//设置默认值为16sp TypeValue是对sp和dp进行相互的转换(各种尺寸的转换)
mTextSize = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
default:
break;
}
}
array.recycle();
mPaint = new Paint();
mPaint.setTextSize(mTextSize);
mBound = new Rect();
mPaint.getTextBounds(mText,0,mText.length(),mBound);
4)重写onDraw方法进行绘制如下:
@Override
protected void onDraw(Canvas canvas) {
//设置底色为灰色 并进行绘制
mPaint.setColor(Color.GRAY);
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaint);
//设置字体颜色为设置的字体颜色,并进行内容的绘制
mPaint.setColor(mTextColor);
canvas.drawText(mText,getWidth()/2-mBound.width()/2,getHeight()/2+mBound.height()/2, mPaint);
}
5)重写onMeasure进行测量(重点)如下:
Android控件测量MeasureSpec共三种模式,如下:
EXACTLY:指的是设置了明确的值或者是MATCH_PARENT。
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT。
UNSPECIFIED:不限制,很少使用。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY){
width = widthSize;
}else {
mPaint.setTextSize(mTextSize);
mPaint.getTextBounds(mText,0,mText.length(),mBound);
float textWidth = mBound.width();
int desire = (int)(getPaddingLeft()+textWidth+ getPaddingRight());
width = desire;
}
if (heightMode == MeasureSpec.EXACTLY){
height = heightSize;
}else {
mPaint.setTextSize(mTextSize);
mPaint.getTextBounds(mText,0,mText.length(),mBound);
float textHeight = mBound.height();
int desire = (int)(getPaddingTop()+textHeight+ getPaddingBottom());
height = desire;
}
setMeasuredDimension(width,height);
}
注意:此步骤非常重要如果缺失,则在宽度高度为wrap_content的时候会铺满整个屏幕,如下:
到此为止,自定义控件已经完成了。
自定义控件的使用如下:
xml文件内容:
xmlns:text="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.jinjin.viewstudy.viewstudy.MainActivity">
注意:红色字体部分为引入命名控件,缺失则不能正常使用。
到此即可实现上述的显示效果。
源码下载:Demo