由于重新理了理博客,所以将此篇更改了下作为自定义的代码篇,如果不懂自定义View的原理和实现步骤的可以先看看android之自定义View和ViewGroup(一),具体解释不就不废话了,前面那个链接上写的很清楚了,此篇直接来代码实现。
假如我们需要实现的效果是这样(模拟器有点卡,见谅):
那么我们可以分析下,需要什么思路,由于图片问题,图中可能不明显,结构是这样的:
三层,一层红色背景,一层内层粉色,再是一个绿色进度的方形条,那么我们就画一个方形红色背景,在里面再画一个粉色方形,然后里面再画一个绿色的方形进行移动,但是移动范围是粉色框内,思路就是这样,那么开始
我们自定义view就需要几个属性:
1.背景色(图中红色)
2.内部背景色(图中粉色)
3.进度方块的颜色(图中绿色)
4.红色框的线的宽度(因为我画的是一个没有填充满颜色的红色框,只是画的一个带红色stroke的框,可以根据自己的想法更改)
那么自定义一共要三个模块:
1.在attrs中自定义需要的属性(上面4个)
2.继承View自定义View进行画进度条的逻辑
3.在xml布局中使用
我们先来第一个模块:
(1)在attrs中自定义需要的属性(如果没有attrs.xml就在res/values下建个就行)
<attr name="backColor" format="color"/>
<attr name="progressColor" format="color"/>
<attr name="strokeColor" format="color"/>
<attr name="strokeWidth" format="dimension"/>
<declare-styleable name="CustomprogressBarView">
<attr name="strokeWidth"/>
<attr name="backColor"/>
<attr name="progressColor"/>
<attr name="strokeColor"/>
</declare-styleable>
我们自定义出了4个需要的属性,name表示引用名,format表示值的类型,
(2)继承View自定义View进行画进度条的逻辑实现,写CustomProgressbar继承自View,然后重写它的ondraw方法
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import com.custom.my.R;
/**
* Created by Administrator on 2016/1/14.
*/
public class CustomProgressbar extends View {
private int backColor; //图中粉色
private int progressColor; //图中绿色
private int strokeColor; //图中红色
private Paint paintBack; //粉色对应的画笔
private Paint paintProgress; //绿色对应的画笔
private Paint paintStroke; //红色对应的画笔
private int progress = 1; //代表进度快(将图中粉色分为30块,那么进度条的方式就是从第一块移动到第二块,第三块。。。最后一块,回到第一块),可以自定义
private int num = 30; //粉色分为多少块,这里设为30块,如果需要自定义也可以,这里就直接这样吧
private float averageWidth; //平均宽度
private float strokeWidth; //红色的宽度
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
@Override
public void run() {
postInvalidate(); //刷新view
progress ++;
if(progress == num+1){
progress = 1;
}
handler.postDelayed(this,100); //每隔100ms就移动一次(比如粉色分为30块,绿色在第一块,然后100ms后移动到第二块,第三块。。。等)
}
};
public CustomProgressbar(Context context) {
this(context,null);
}
public CustomProgressbar(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public CustomProgressbar(Context context, AttributeSet attrs, int defStyleAttr) { //构造方法不用说了吧
super(context, attrs, defStyleAttr);
TypedArray arr = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomprogressBarView, defStyleAttr, 0); //获取自定义的属性值组
backColor = arr.getColor(R.styleable.CustomprogressBarView_backColor, Color.WHITE); //获取自定义的属性backColor,默认为白色
progressColor = arr.getColor(R.styleable.CustomprogressBarView_progressColor, Color.RED); //获取自定义的属性progressColor,默认为红色
strokeColor = arr.getColor(R.styleable.CustomprogressBarView_strokeColor, Color.GRAY); //获取自定义的属性,外框的颜色即图中的红色框
strokeWidth = arr.getDimensionPixelSize(R.styleable.CustomprogressBarView_strokeWidth, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, getResources().getDisplayMetrics())); //获取自定义的设置线条框宽度的属性,默认为15sp
arr.recycle(); //用完后记得recycle();
paintBack = new Paint();
paintProgress = new Paint();
paintStroke = new Paint();
handler.postDelayed(runnable,100); //使用handler实现定时更新操作
}
@Override
protected void onDraw(Canvas canvas) { //重写onDraw来画进度条
int width = getWidth(); //获取自定义的View的宽度
int height = getHeight(); //获取自定义的View的高度
//设置红色框画笔属性,宽度和颜色,描边效果
paintStroke.setColor(strokeColor);
paintStroke.setStrokeWidth(strokeWidth);
paintStroke.setDither(true);
paintStroke.setStyle(Paint.Style.STROKE);
canvas.drawRect(strokeWidth / 2, strokeWidth / 2, width - strokeWidth / 2, height - strokeWidth / 2, paintStroke); //画红色矩形框,参数是:左上右下位置和画笔
//设置粉色框的画笔属性
paintBack.setColor(backColor);
paintBack.setDither(true);
canvas.drawRect(strokeWidth, strokeWidth, width-strokeWidth, height-strokeWidth, paintBack);//画粉色矩形框,参数同上
float contentWidth = width - strokeWidth*2; //这里画绿色进度条框,需要计算宽了,因为我们分成了30块,横向滑动,绿色块的活动范围是粉色框,粉色框宽度是总宽度减去左右2根红色线条的宽度
averageWidth = contentWidth/30; //绿色进度框的宽度
//绿色进度框的画笔属性
paintProgress.setColor(progressColor);
paintProgress.setDither(true);
canvas.drawRect((progress - 1) * averageWidth + strokeWidth, strokeWidth, progress * averageWidth + strokeWidth, height - strokeWidth, paintProgress); //根据progress记录绿色框移动到粉色的30块中的哪一块,然后在对应的位置画下绿色框
}
}
(3)在xml布局中使用
记得加入命名空间:图中绿色代码,或者:xmlns:xjj="http://schemas.android.com/apk/res-auto",这个xjj可以自定义,定义后,代码中引用属性的时候就要用这个,即下面的红色代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:xjj="http://schemas.android.com/apk/res/com.custom.my"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.custom.my.activity.CustomProgressActivity">
<com.custom.my.view.CustomProgressbar
android:layout_centerInParent="true"
xjj:backColor="#50ff0000"
xjj:strokeWidth = "1dp"
xjj:strokeColor = "#50ff00ff"
xjj:progressColor = "#00ffaa"
android:layout_width="100dp"
android:layout_height="5dp" />
</RelativeLayout>
然后这样就完成了进度条的自定义效果,执行就行了 ,有什么不懂得可以问我。
对了,如果看了android之自定义View和ViewGroup(一)的人可能会问了,不是说要重写onMeasure方法测量大小么?测量大小也是需要分情况的,比如自定义这个进度条,必须给一个固定的宽高值,不然设置成wrap_content也不知改设置多大啊。当然你可以设置为wrap_content然后在onMeasure方法里面测量的时候如果是wrap_content就给他一个默认的宽高之类的;由于咋们使用这个进度条肯定是给定了具体的宽高,所以就不需要根据测量模式进行测量了,因为给了固定宽高,那么默认测量的结果就是固定宽高的结果,结果正确。只有在没有设置固定宽高的时候需要测量,比如自定义ImageView那种,设置为wrap_content就需要测量了,此时ImageView的宽高需要设为图片的宽和高才正确。而这个进度条这个你根本不知道内容宽高,所以设为具体值合理又方便。