您的位置:首页 > 移动开发 > Android开发

Android自定义带进度的刻度条

2016-11-30 00:24 148 查看
如何自定义控件?

1.自定义属性的声明和获取;

2.测量onMeasure;

3.布局onLayout(ViewGroup);

4.绘制onDraw;

5.onTouchEvent;

6.onInterceptTouchEvent(ViewGroup);

7.状态的恢复与保存(与Activity生命周期有关);

自定义绘制的PrograssBar的水平进度条如下 有刻度 刻度在中间显示



下面由代码实现:在values文件夹下建立attr.xml 放入要绘制进度条所需要的。分别是进度条字体①颜色和②大小,Prograss走过的条的③颜色和④高度,Prograss没有走过的条的⑤颜色和⑥高度,还有显示数值和进度条之间有间隔的空间⑦宽度。一共七个参数。

<?xml version="1.0" encoding="utf-8"?>
<resources>

<!-- 声明-->
<attr name="prograss_unreach_color" format="color"></attr>
<attr name="prograss_unreach_height" format="dimension"></attr>
<attr name="prograss_reach_color" format="color"></attr>
<attr name="prograss_reach_height" format="dimension"></attr>
<attr name="text_color" format="color"></attr>
<attr name="text_size" format="dimension"></attr>
<attr name="prograss_text_offset" format="dimension"></attr>

<!-- 使用-->
<declare-styleable name="horizonalPrograssBarWithPrograss">
<attr name="prograss_unreach_color" ></attr>
<attr name="prograss_unreach_height" ></attr>
<attr name="prograss_reach_color" ></attr>
<attr name="prograss_reach_height" ></attr>
<attr name="text_color"></attr>
<attr name="text_size"></attr>
<attr name="prograss_text_offset" ></attr>

</declare-styleable>
</resources>


新建包View,并新建类class horizonalPrograssBarWithPrograss,如下:

package com.chase.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.ProgressBar;

import com.chase.cn.customprograssbar.R;

/**
* Created by Chase on 2016/11/29.
*/

public class horizonalPrograssBarWithPrograss extends ProgressBar {

//这里声明默认值
private static final int DEFAULT_TEXT_SIZE = 10;//sp
private static final int DEFAULT_TEXT_COLOR = 0xFFC00D1;
private static final int DEFAULT_COLOR_UNREACH = 0XFFD3D6DA;
private static final int DEFAULT_HEIGHT_UNREACH = 2;//dp
private static final int DEFAULT_COLOR_REACH = 0xFFC00D1;
private static final int DEFAULT_HRIGHT_REACH = 2;//dp
private static final int DEFAULT_TEXT_OFFSET = 10;//dp

//编写我们的值
protected int mTextSize = dp2px(DEFAULT_TEXT_SIZE);
protected int mTextColor = DEFAULT_TEXT_COLOR;
protected int mUnReachColor = DEFAULT_COLOR_UNREACH;
protected int mUnReachHeight = DEFAULT_HEIGHT_UNREACH;
protected int mReachColor = DEFAULT_COLOR_REACH;
protected int mReachHeight = DEFAULT_HRIGHT_REACH;
protected int mTextOffset = dp2px(DEFAULT_TEXT_OFFSET);

protected Paint mPaint = new Paint();
protected int mRealWidth; //进度条实际宽度

//2个参数法
public horizonalPrograssBarWithPrograss(Context context, AttributeSet attrs) {
this(context, attrs, 0);//2个参数继承3个参数的方法
}

public horizonalPrograssBarWithPrograss(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
obtainStyledAttrs(attrs);

}

//获取自定义属性
private void obtainStyledAttrs(AttributeSet attrs) {
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.horizonalPrograssBarWithPrograss);

mTextSize = (int) ta.getDimension(R.styleable.horizonalPrograssBarWithPrograss_text_size, mTextSize);
mTextColor = ta.getColor(R.styleable.horizonalPrograssBarWithPrograss_text_color, mTextColor);
mTextOffset = (int) ta.getDimension(R.styleable.horizonalPrograssBarWithPrograss_prograss_text_offset, mTextOffset);
mUnReachColor = ta.getColor(R.styleable.horizonalPrograssBarWithPrograss_prograss_unreach_color, mUnReachColor);
mUnReachHeight = (int) ta.getDimension(R.styleable.horizonalPrograssBarWithPrograss_prograss_unreach_height, mUnReachHeight);
mReachColor = ta.getColor(R.styleable.horizonalPrograssBarWithPrograss_prograss_reach_color, mReachColor);
mReachHeight = (int) ta.getDimension(R.styleable.horizonalPrograssBarWithPrograss_prograss_reach_height, mReachHeight);

mPaint.setTextSize(mTextSize);
ta.recycle();
}

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        int widthMode = MeasureSpec.getMode(widthMeasureSpec);//因为是水平进度条 默认宽度为用户的输入 所以不需要判断
int widthVal = MeasureSpec.getSize(widthMeasureSpec);

int height = measureHeight(heightMeasureSpec);

setMeasuredDimension(widthVal, height);//完成测量
//实际绘制区域的宽度
mRealWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
}

//高度测量的方法
private int measureHeight(int heightMeasureSpec) {
int result = 0;
int mode = MeasureSpec.getMode(heightMeasureSpec);
int size = MeasureSpec.getSize(heightMeasureSpec);

if (mode == MeasureSpec.EXACTLY) {//精确值,用户给的精确值 如200dp marchparent
result = size;
} else {//否则宽度 是由文字的距离确定的
int textHeight = (int) (mPaint.descent() - mPaint.ascent());
result = getPaddingTop() //上边距
+ getPaddingBottom() //下边距
+ Math.max(Math.max(mReachHeight, mUnReachHeight), Math.abs(textHeight));//三者最大值
if (mode == MeasureSpec.AT_MOST) {
result = Math.min(result, size);
}
}

return result;
}

//1个参法
public horizonalPrograssBarWithPrograss(Context context) {
this(context, null);//继承两个参数的构造方法,当用户在使用时new的时候用,但是使用时一般是用两个参数的构造方法的
}

/**
*
下面是绘制进度条的方法
* @param canvas
*/
@Override
protected synchronized void onDraw(Canvas canvas) {
//        super.onDraw(canvas);
canvas.save();
canvas.translate(getPaddingLeft(), getHeight() / 2);//移动画布到 最左边 正中间

//判断是否需要绘制unReachBar
boolean noNeedUnReach = false;
//DrawReachBar如下:

//拿到文本宽度
String text = getProgress() + "";
int textWidth = (int) mPaint.measureText(text);

float ratio = getProgress() * 1.0f / getMax();
float prograssX = ratio * mRealWidth;
if (prograssX + textWidth > mRealWidth) {
prograssX = mRealWidth - textWidth;//防止进度条到100文本出去
noNeedUnReach = true;
}

float endX = prograssX - mTextOffset / 2;
if (endX > 0) {
mPaint.setColor(mReachColor);
mPaint.setStrokeWidth(mReachHeight);
canvas.drawLine(0, 0, endX, 0, mPaint);
}

//Draw Text
mPaint.setColor(mTextColor);
int y = (int) (-(mPaint.descent()+mPaint.ascent())/2);
canvas.drawText(text,prograssX,y,mPaint);

//Draw UnReachBar
if (!noNeedUnReach){
float start = prograssX + mTextOffset/2 +textWidth;
mPaint.setColor(mUnReachColor);
mPaint.setStrokeWidth(mReachHeight);
canvas.drawLine(start,0,mRealWidth,0,mPaint);
}

canvas.restore();
}

//转换方法dp 2 px
protected int dp2px(int dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, getResources().getDisplayMetrics());
}

protected int sp2px(int spVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, getResources().getDisplayMetrics());
}

}


这是进度条的绘制完成了,上面进度条高度的测量,实际比较关键的是测量文字的高度,如图:


然后现在可以在activity_main.xml中定义并测试:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:chase="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<com.chase.view.horizonalPrograssBarWithPrograss
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:padding="5dp"
android:progress="50" />

<com.chase.view.horizonalPrograssBarWithPrograss
android:id="@+id/prograss2"
chase:text_color="#44ff0000"
chase:prograss_unreach_color="#000"
chase:prograss_reach_height="5dp"
chase:prograss_unreach_height="5dp"
chase:text_size="10sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:padding="5dp"
android:progress="0" />

</LinearLayout>
</ScrollView>


就如上面的,我们可以自定义进度条的属性,只需要添加这行:(这是在AS中)

xmlns:chase="http://schemas.android.com/apk/res-auto"


Eclipse中应该要这么写(跟上ManiFeast中显示的包名):

xmlns:chase="http://schemas.android.com/apk/com.chase.cn.customprograssbar"


如前面所示,第二个PrograssBar进行进度变化测试,在MainActivity中:用handler进行测试:

package com.chase.cn.customprograssbar;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;

import com.chase.view.horizonalPrograssBarWithPrograss;

public class MainActivity extends AppCompatActivity {

private horizonalPrograssBarWithPrograss mPrograss;

private static final int MSG_UPDATE = 0x111;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
int prograss = mPrograss.getProgress();
mPrograss.setProgress(++prograss);
if (prograss>=100){
handler.removeMessages(MSG_UPDATE);
}else {
handler.sendEmptyMessageDelayed(MSG_UPDATE,100);
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPrograss = (horizonalPrograssBarWithPrograss) findViewById(R.id.prograss2);
handler.sendEmptyMessage(MSG_UPDATE);
}
}


下面的代码自定义了圆形的进度条,样式与上面的相似,只不过是圆形的。

首先在attr.xml中继续添加:添加圆形需要的半径

<declare-styleable name="RoundPrograssBarWithPrograss">
<attr name="radius" format="dimension" ></attr>
</declare-styleable>


在View包下新建class RoundPrograssBarWithPrograss,继承上面的水平进度条。

package com.chase.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;

import com.chase.cn.customprograssbar.R;

/**
* Created by Chase on 2016/11/30.
*/

public class RoundPrograssBarWithPrograss extends horizonalPrograssBarWithPrograss {

private int mRadius = dp2px(30);

private int mMaxPaintWidth;

public RoundPrograssBarWithPrograss(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

mReachHeight = (int) (mUnReachHeight * 2.5f);

TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.RoundPrograssBarWithPrograss);
mRadius = (int) ta.getDimension(R.styleable.RoundPrograssBarWithPrograss_radius, mRadius);
ta.recycle();

//设置画笔属性
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);//抗锯齿
mPaint.setDither(true);//防抖动
mPaint.setStrokeCap(Paint.Cap.ROUND);//设置连接处为弧形
}

public RoundPrograssBarWithPrograss(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public RoundPrograssBarWithPrograss(Context context) {
this(context, null);
}

@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

mMaxPaintWidth = Math.max(mReachHeight, mUnReachHeight);//最大画笔宽度 取进度条 unreach和reach的最大
//设置期望值,默认四个Paddding一致
int expect = mRadius * 2 + mMaxPaintWidth + getPaddingLeft() + getPaddingRight();

//用系统提供的方法来测量用户给出的数据
int width = resolveSize(expect, widthMeasureSpec);
int height = resolveSize(expect, heightMeasureSpec);

int realWidth = Math.min(width, height);//取二者最小值,防止宽高取得不一致、

mRadius = (realWidth - getPaddingLeft() - getPaddingRight() - mMaxPaintWidth) / 2;

setMeasuredDimension(realWidth, realWidth);
}

@Override
protected synchronized void onDraw(Canvas canvas) {
String text = getProgress() + "%";
float textWidth = mPaint.measureText(text);
float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;

canvas.save();
canvas.translate(getPaddingLeft() + mMaxPaintWidth / 2, getPaddingTop() + mMaxPaintWidth / 2);
mPaint.setStyle(Paint.Style.STROKE);
//Draw unReachBar
mPaint.setColor(mUnReachColor);
mPaint.setStrokeWidth(mUnReachHeight);
canvas.drawCircle(mRadius,mRadius,mRadius,mPaint);
//Draw ReachBar
mPaint.setColor(mReachColor);
mPaint.setStrokeWidth(mReachHeight);
float sweepAngle = getProgress()*1.0f/getMax()*360; //算出弧度
canvas.drawArc(new RectF(0,0,mRadius*2,mRadius*2),0,sweepAngle,false,mPaint);
//Draw Text
mPaint.setColor(mTextColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawText(text,mRadius-textWidth/2,mRadius-textHeight,mPaint);
canvas.restore();
}
}


然后再activity_main.xml中可以使用了:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:chase="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<com.chase.view.horizonalPrograssBarWithPrograss
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:padding="5dp"
android:progress="50" />

<com.chase.view.horizonalPrograssBarWithPrograss
android:id="@+id/prograss2"
chase:text_color="#44ff0000"
chase:prograss_unreach_color="#000"
chase:prograss_reach_height="5dp"
chase:prograss_unreach_height="5dp"
chase:text_size="10sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:padding="5dp"
android:progress="0" />

<com.chase.view.RoundPrograssBarWithPrograss
android:id="@+id/prograss3"
chase:text_color="#44ff0000"
chase:radius="25dp"
chase:prograss_reach_height="5dp"
chase:prograss_unreach_height="5dp"
chase:text_size="10sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:padding="5dp"
android:progress="30" />

<com.chase.view.RoundPrograssBarWithPrograss
android:id="@+id/prograss4"
chase:text_color="#44ff0000"
chase:radius="80dp"
chase:prograss_reach_height="5dp"
chase:prograss_unreach_height="5dp"
chase:text_size="10sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:padding="5dp"
android:progress="20" />

</LinearLayout>
</ScrollView>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 布局 控件