距离上次写博客很久了,今天终于有机会闲下来写写了,没办法一直忙项目了, 从项目中跟定大神学了点东西 感觉自定义View我掌握的不是很好正好研究了几天 然后写一个小Demo 希望对自定义view不熟悉的同学可以借鉴一下。
自定义View我是按照三步进行的
1.自定义属性(在values文件下面新建attrs)
<!-- 自定义控件的属性 -->
<declare-styleable name="MyViewCircle">
<attr name="circleColor" format="color" />
<attr name="max" format="integer"></attr>
<attr name="circleProgress" format="color" />
<attr name="circleBoderWidth" format="dimension" />
<attr name="cicleBoderWidthColor" format="color" />
<attr name="textColor" format="color" />
<attr name="textSize" format="dimension" />
<attr name="otherSize" format="dimension" />
<attr name="bottomNumSize" format="dimension" />
<attr name="style">
<enum name="STROKE" value="0"></enum>
<enum name="FILL" value="1"></enum>
</attr>
</declare-styleable>
这里面具体的我就不说了 大家百度一下肯定是可以弄明白的
2.在自定义View的构造方法里面获取自定义的属性
public MyCircleView(Context context) {
this(context, null);
this.mContext = context;
}
public MyCircleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
this.mContext = context;
}
public MyCircleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mContext = context;
mPaint = new Paint();
TypedArray array = context.obtainStyledAttributes(attrs,
R.styleable.MyViewCircle);
/**
* 获取自定义属性
*/
circleColor = array.getColor(R.styleable.MyViewCircle_circleColor,
Color.YELLOW);
cicleBoderWidthColor = array.getColor(
R.styleable.MyViewCircle_cicleBoderWidthColor, Color.BLUE);
circleProgressColor = array.getColor(
R.styleable.MyViewCircle_circleProgress, Color.RED);
circleBoderWidth = UiSizeHelper.scalePx(context, 20);
max = array.getInteger(R.styleable.MyViewCircle_max, 100);
styleCircle = array.getInt(R.styleable.MyViewCircle_style, 0);
textColor = array.getColor(R.styleable.MyViewCircle_textColor,
Color.RED);
textSize = UiSizeHelper.scalePx(context, 40);
percentSize = UiSizeHelper.scalePx(context, 22);
otherSize = UiSizeHelper.scalePx(context, 30);
bottomNumSize = UiSizeHelper.scalePx(context, 25);
array.recycle();
}
UiSizeHelper这个是我自定义的一个辅助类 待会直接给大家上传 主要是一些手机屏幕的计算 这个也没什么好说的 如果对自定义View有一点的了解肯定理解这段代码
3.用OnDraw方法开始画(这里没有用onMearsor方法计算 根据具体情况调用)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 得到最外面圆的半径
int centerWidth = getWidth() / 2;
// 内部圆的半径
int circleTwo = (int) (centerWidth - circleBoderWidth);
mPaint.setColor(cicleBoderWidthColor); // 设置圆环的颜色
mPaint.setStyle(Paint.Style.STROKE); // 设置空心
mPaint.setStrokeWidth(circleBoderWidth); // 设置圆环的宽度
mPaint.setAntiAlias(true); // 消除锯齿
canvas.drawCircle(centerWidth, centerWidth, circleTwo, mPaint);
// 点击效果画一个半透明的C
if (is_shade == 1) {
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(color.myciecleBT);
mPaint.setStrokeWidth(0); // 设置圆环的宽度
mPaint.setAntiAlias(true);
canvas.drawCircle((float) centerWidth, (float) centerWidth,
(float) circleTwo-circleBoderWidth/2, mPaint);
}
// 画Text
mPaint.setStrokeWidth(0);
mPaint.setColor(textColor);
mPaint.setTextSize(textSize);
// 设置默认的字体
mPaint.setTypeface(Typeface.DEFAULT);
// 得到画上去的字体的宽度
float textWidth = mPaint.measureText(textContent + "");
// 得到画上去的字体的高度
float textHeight = (float) Math.ceil(mPaint.getFontMetrics().descent
- mPaint.getFontMetrics().ascent) / 2;
if (!TextUtils.isEmpty(textContent + "")) {
canvas.drawText(textContent + "", centerWidth - textWidth / 2 - 5,
centerWidth + textHeight / 2 + 4, mPaint);
mPaint.setTextSize(percentSize);
canvas.drawText("%", centerWidth + textWidth / 2 + 3, centerWidth
+ textHeight / 2 + 2, mPaint);
// 写上面和下面的字
// 上面的字
mPaint.setColor(Color.BLACK);
mPaint.setTextSize(otherSize);
float textWidth1 = mPaint.measureText("年化");
float textHeight1 = (float) Math
.ceil(mPaint.getFontMetrics().descent
- mPaint.getFontMetrics().ascent) / 2;
canvas.drawText("年化", centerWidth - textWidth1 / 2 - 1, centerWidth
- textHeight / 2 - UiSizeHelper.scalePx(mContext, 25),
mPaint);
// 下面的字
mPaint.setTextSize(bottomNumSize);
mPaint.setColor(Color.RED);
float textWidth3 = mPaint.measureText(borrow_duration + "");
if (duration_type == 1) {
// 前面的数字
canvas.drawText(
borrow_duration + "",
centerWidth - textWidth1 / 2 - 15,
centerWidth + textHeight / 2
+ UiSizeHelper.scalePx(mContext, 45), mPaint);
// 后面加上几个月
mPaint.setColor(Color.BLACK);
mPaint.setTextSize(otherSize);
canvas.drawText("个月", centerWidth - textWidth1 / 2 - 15
+ textWidth3, centerWidth + textHeight / 2
+ UiSizeHelper.scalePx(mContext, 45), mPaint);
// 天数
} else {
// 前面的数字
canvas.drawText(
borrow_duration + "",
centerWidth - textWidth1 / 2 + 2,
centerWidth + textHeight / 2
+ UiSizeHelper.scalePx(mContext, 45), mPaint);
// 后面加上几个月
mPaint.setColor(Color.BLACK);
mPaint.setTextSize(otherSize);
canvas.drawText(
"天",
centerWidth - textWidth1 / 2 + 2 + textWidth3,
centerWidth + textHeight / 2
+ UiSizeHelper.scalePx(mContext, 45), mPaint);
}
}
// 画圆弧
// 圆环上面的颜色(进度的颜色)
mPaint.setColor(circleProgressColor);
// 圆环的宽度
mPaint.setStrokeWidth(circleBoderWidth);
RectF oval = new RectF(centerWidth - circleTwo,
centerWidth - circleTwo, centerWidth + circleTwo, centerWidth
+ circleTwo); // 用于定义的圆弧的形状和大小的界限
mPaint.setStrokeCap(Paint.Cap.ROUND);
switch (styleCircle) {
// 空心
case STROKE:
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(oval, -90, progress * 360 / max, false, mPaint);
Log.i("22", String.valueOf(progress / max * 360));
break;
// 实心
case FILL:
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawArc(oval, -90, progress * 360 / max, true, mPaint); // 根据进度画圆弧
break;
default:
break;
}
}
这个上面有详细的注释, 我想说的就几点
a.画笔Paint画图的时候 我这里画的是一个圆 设置了画笔的stroke 其实是这样的
例如 我想画的圆的半径是50 我的mPaint.setStrokeWidth(20); 画笔宽度为20 那么这个画笔会怎么画,对就是图中红色线之间的距离 意思就是paint会居中去画 左边的红线离圆边10 右边的红线离圆边10 说了这么多 都是为了说明paint会居中去画 前提是设置了setStrokeWidth 默认的是0
RectF oval = new RectF(centerWidth - circleTwo,
centerWidth - circleTwo, centerWidth + circleTwo, centerWidth
+ circleTwo); // 用于定义的圆弧的形状和大小的界限
这句话大家最好动手画画 数学好的理解的肯定快点
其实我这个Demo的思路大概就是
先画一个圆 设置一个stroke为20 然后在画一个圆跟上一个圆是重合的 唯一不同的是stroke的颜色 这样两个颜色就区分开了 然后再根据进度做了一个线程的sleep 让大家看起来像动画一样得效果 其实不是动画 然后我自己开始弄不明白画笔怎么画的 我自己又加了一个点击效果
大家看Demo的时候可以看一下我具体怎么写的
这个是我的Mainactivty里面的
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
myCircleView.setProgress((int) (Float.valueOf(String
.valueOf(index)) / Float.valueOf("220") * 100));
}
};
};
这里就是更新UI 看起来有一个动画 其实不是动画
下面的是动态计算一下这个控件占屏幕的宽度和高度
RelativeLayout.LayoutParams sp_params = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
sp_params.width = UiSizeHelper.getScreenWidth() / 3;
sp_params.height = UiSizeHelper.getScreenHeight() / 5;
sp_params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
sp_params
.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
myCircleView.setLayoutParams(sp_params);
Log.i("MA", UiSizeHelper.getScreenHeight() / 5 + "");
myCircleView.setProgress((int) (Float.valueOf(String.valueOf(2))
/ Float.valueOf("220") * 100));
myCircleView.setBorrow_duration(20);
myCircleView.setDuration_type(1);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
initThread();
mThread.start();
}
}, 700);
}
public void initThread() {
mThread = new Thread() {
@SuppressWarnings("static-access")
public void run() {
for (int i = 2; i <= 150; i++) {
index = i;
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
try {
mThread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
};
}
这段代码里面UiSizeHelper是一个适配手机的类 我是根据720*1080去适配的 根据屏幕的比例去设置相应的大小
好 待会我传上Demo大家看一下
总结:
其实自定义View不难 开始会感觉特别难 最难的应该是 测量OnMesear里面的 我这个Demo里面没有涉及 等到时候遇到的时候还是会继续更新博客 如果想做一个中级开发或者高级开发 自定义View是必须掌握的 如果只是想一直静静的当个小程序员 。。。 提供一个我Demo的网址 http://download.youkuaiyun.com/detail/qq_20607305/9293291