概述
自定义View是Android程序员在进阶阶段是必须经历的也是痛苦的,但是它也没有想象的那么麻烦,无非也就是一下几步:
1. 自定义View属性
2. 在构造方法中获得自定义的属性
3. 重新ononDraw:用来绘制该控件的内容
4. 重新onMeasure:用来确定该控件或子控件的大小
5. 重新onLayout:用来确定该控件及子控件在父窗体中的位置
并不是说每次都要重写这些方法,这个还要看实际情况。
实现目标
<img src="http://upload-images.jianshu.io/upload_images/2260249-8c39124081a528a0.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"/>
实现方式
1. 自定义View属性:res/values下新建attrs.xml,在里面声明需要的属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="FloatCricleView">
<attr name="viewText" format="string"></attr>
<attr name="viewTextSize" format="dimension"></attr>
<attr name="viewBackground" format="color"></attr>
<attr name="viewTextColor" format="color"></attr>
</declare-styleable>
</resources>
2. 那么我们如何实现这些属性呢?
在View的构造方法中获得属性,首先我们有必要介绍一下这几个构造方法
public int width;//布局宽
public int height;//布局的高
private Paint circlePaint;//圆画笔
private Paint textPaint;//文字画笔
private int textSize;
private int colorBackground;
private int textColor;
private String text;
public FloatCricleView(Context context) {
this(context, null);
}
public FloatCricleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatCricleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.FloatCricleView, defStyleAttr, 0);
int size = array.getIndexCount();
//设置一些默认参数
colorBackground = Color.GRAY;//背景色
textColor = Color.BLACK;//文字颜色
textSize = 20;//文字大小
text = "NULL";
for (int i = 0; i < size; i++) {
int attr = array.getIndex(i);
switch (attr) {
case R.styleable.FloatCricleView_viewText:
text = array.getString(attr);
break;
case R.styleable.FloatCricleView_viewTextColor:
textColor = array.getColor(attr, Color.BLACK);
break;
case R.styleable.FloatCricleView_viewTextSize:
textSize = array.getDimensionPixelSize(attr, 20);
break;
case R.styleable.FloatCricleView_viewBackground:
colorBackground = array.getColor(attr, Color.GRAY);
break;
default:
break;
}
}
initView();//初始化画笔
}
private void initView() {
circlePaint = new Paint();
circlePaint.setColor(colorBackground);
circlePaint.setAntiAlias(true);
textPaint = new Paint();
textPaint.setTextSize(textSize);
textPaint.setAntiAlias(true);
textPaint.setFakeBoldText(true);
textPaint.setColor(textColor);
}
我们重写了3个构造方法,默认的布局文件调用的是两个参数的构造方法,所以记得让所有的构造调用我们的三个参数的构造,我们在三个参数的构造中获得自定义属性。
3. 重新onDraw,onMeasure
@Overrideprotected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(width / 2, height / 2, width / 2, circlePaint);
float textWidth = textPaint.measureText(text);
float x = width / 2 - textWidth / 2;
Paint.FontMetrics metrics = textPaint.getFontMetrics();
float dy = -(metrics.ascent + metrics.descent) / 2;
float y = height / 2 + dy;
canvas.drawText(text, x, y, textPaint);
}
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//设置宽度
int specMode = View.MeasureSpec.getMode(widthMeasureSpec);
int specSize = View.MeasureSpec.getSize(widthMeasureSpec);
switch (specMode) {
case View.MeasureSpec.EXACTLY://明确指定大小的,或者最大
width = getPaddingLeft() + getPaddingRight() + specSize;
break;
case View.MeasureSpec.AT_MOST://一般为warp_content
width = getPaddingLeft() + getPaddingRight() + dip2px(getContext(), 100);
break;
}
//设置高度
specMode = View.MeasureSpec.getMode(heightMeasureSpec);
specSize = View.MeasureSpec.getSize(heightMeasureSpec);
switch (specMode) {
case View.MeasureSpec.EXACTLY:
height = getPaddingTop() + getPaddingBottom() + specSize;
break;
case View.MeasureSpec.AT_MOST:
height = getPaddingTop() + getPaddingBottom() + dip2px(getContext(), 100);
break;
}
setMeasuredDimension(width, height);}
4. 工具方法
/**
* dp 转 px
*
* @param context
* @param value
* @return
*/
private static int dip2px(Context context, float value) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (value * scale + 0.5f);
}
/**
* px转dp
*
* @param context
* @param value
* @return
*/
private static int px2dip(Context context, float value) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (value / scale + 0.5f);
}
5. 使用
<tangren.szxb.com.floatcrileview.FloatCricleView2
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
add:viewBackground="@color/colorAccent"
add:viewText="90%"
add:viewTextColor="@color/colorPrimary"
add:viewTextSize="20sp" />
6. 结束语
实现的效果也比较简单大神勿喷,至此我的第一篇简书文章也写完了,希望大家相互学习沟通交流,我的csdn地址:http://blog.csdn.net/wu996489865 。因为我是第一次在简书上发布自己的文章,我想问的是为什么代码不会自动换行,我都是手动回车换行的啊(泪崩)!还请大神们指点一二(抱拳)!