主要是学习自定义控件的内容,因为自定义的控件类直接继承了ProgressBar类,所以代码还不是那个繁琐,主要就是一个覆写一个测量方法和绘制方法。
一、自定义的进度条
package com.example.hejingzhou.selfprogress.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.Log;
import android.util.TypedValue;
import android.widget.ProgressBar;
import com.example.hejingzhou.selfprogress.R;
/**
* Created by Hejingzhou on 2016/5/19.
*/
public class HorizontalProgressbarWithProgrss extends ProgressBar {
private String TAG = getClass().getCanonicalName();
private static final int DEFAULT_TEXT_SIZE = 60;//默认的文字尺寸
private static final int DEFAULT_TEXT_COLOR = 0xfffc00d1;//默认的文字颜色
private static final int DEFAULT_TEXT_OFFSET = 10;//默认的文字偏移量
private static final int DEFAULT_HEIGHT_UNREACH = 6;//默认没有完成的高
private static final int DEFAULT_HEIGHT_REACH = 6;//默认完成的高
private static final int DEFAULT_COLOR_REACH = DEFAULT_TEXT_COLOR;//默认完成的颜色
private static final int DEFAULT_COLOR_UNREACH = 0xffd3d6da;//默认没有完成的颜色
private int mTextSize = spTopx(DEFAULT_TEXT_SIZE);
private int mTextColor = DEFAULT_TEXT_COLOR;
private int mTextOffset = dpTopx(DEFAULT_TEXT_OFFSET);
private int mUnReachColor = DEFAULT_COLOR_UNREACH;
private int mUnReachHeight = dpTopx(DEFAULT_HEIGHT_UNREACH);
private int mReachColor = DEFAULT_COLOR_REACH;
private int mReachHeight = dpTopx(DEFAULT_HEIGHT_REACH);
private Paint mPaint = new Paint();
private int mReachWidth;
public HorizontalProgressbarWithProgrss(Context context) {
this(context, null);
}
/***
* 测量
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthVal = MeasureSpec.getSize(widthMeasureSpec);
int heightVal = getMeasuredHeight(heightMeasureSpec);
Log.i(TAG, "宽值" + widthVal);//480
Log.i(TAG, "高值" + heightVal);//30
setMeasuredDimension(widthVal, heightVal);
mReachWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
Log.i(TAG, "mReachWidth:" + mReachWidth + "----getMeasuredWidth:" + getMeasuredWidth() + "----getPaddingLeft:" + getPaddingLeft() + "----getPaddingRight:" + getPaddingRight());
//464 480 8 8
Log.i(TAG, "转化完成的文字大小" + mTextSize);//15
}
private int getMeasuredHeight(int heightMeasureSpec) {
int result;
int mode = MeasureSpec.getMode(heightMeasureSpec);
int size = MeasureSpec.getSize(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {//精确值
result = size;
Log.i(TAG, "精确值情况下" + result);
} else {
int textHeight = (int) (mPaint.descent() - mPaint.ascent());
Log.i(TAG, "textHeight:" + textHeight + "画笔的基线下:" + mPaint.descent() + "画笔的基线上:" + mPaint.ascent());
/*
* descent()
* Return the distance below (positive) the baseline (descent) based on the current typeface and text size.
* 返回根据当前字体和文本大小的距离(正)基线(下)
*
* ascent()
* Return the distance above (negative) the baseline (ascent) based on the current typeface and text size.
* 根据当前字体和文本大小返回距离(负)基准(上)
* */
result = getPaddingTop() + getPaddingBottom() + Math.max(Math.max(mReachHeight, mUnReachHeight), Math.abs(textHeight));
// 上边距(8) 下边距(8) 完成的高度 没有完成的高度 文字的高度 三者最大值(16)
Log.i(TAG, "getPaddingTop" + getPaddingTop() + "\ngetPaddingBottom" + getPaddingBottom() + "\nMax" + Math.max(Math.max(mReachHeight, mUnReachHeight), Math.abs(textHeight)));
Log.i(TAG, "result" + result);//30
if (mode == MeasureSpec.AT_MOST) {//不能超过
result = Math.min(result, size);
Log.i(TAG, "RESULT" + result);
}
}
Log.i(TAG, "Result" + result);
return result;
}
/***
* 绘制
*
* @param canvas
*/
@Override
protected synchronized void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(getPaddingLeft(), getHeight() / 2);
Log.i(TAG, "绘制" + getPaddingLeft() + "\n" + getHeight());
boolean noNeedDrawUnRech = false;//是否需要绘制
//绘制完成的进度条
String text = getProgress() + "%";
int textWidth = (int) mPaint.measureText(text);//测量字符串的宽度
float radio = getProgress() * 1.0f / getMax();//绘制的长度比例 一直变大
//判断进度进度跑完,用不用绘制未完成的进度条
float progressX = radio * mReachWidth;// 整个进度 一直变大
//如果跑过的进度加上文字的宽度大于整条进度条的长度那么就不用绘制后边没跑过的路程了
//因为整个进度已经跑完了,也就是进度达到100%
if (progressX + textWidth > mReachWidth) {
progressX = mReachWidth - textWidth;//这时候progressX就是绘制跑过的进度最大值
noNeedDrawUnRech = true;//不用再绘制没跑到的进度条
}
//绘制文字
mPaint.setColor(mTextColor);
int y = (int) (-(mPaint.descent() + mPaint.ascent()) / 2);
canvas.drawText(text, progressX, y, mPaint);
//绘制进度未到的右部分
if (!noNeedDrawUnRech) {
float start = progressX + mTextOffset / 2 + textWidth;
//Log.i(TAG,"进度不足100%推断:"+start+"\n"+progressX+"\n"+textWidth);
mPaint.setColor(mUnReachColor);
mPaint.setStrokeWidth(mUnReachHeight);
canvas.drawLine(start, 0, mReachWidth, 0, mPaint);
}
//绘制跑完的进度条
float endx = radio * mReachWidth - mTextOffset / 2;
if (endx > 0) {
mPaint.setColor(mReachColor);
mPaint.setStrokeWidth(mReachHeight);
if(radio==1){
endx = endx-textWidth;
}
canvas.drawLine(0, 0, endx, 0, mPaint);
}
canvas.restore();
}
//sp转化为dp
private int dpTopx(int dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics());
}
//px转化为sp
private int spTopx(int spVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, getResources().getDisplayMetrics());
}
public HorizontalProgressbarWithProgrss(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}
}
二、测量绘制方法详解
关于AndroidView的参考这篇博客写的挺好的:http://blog.csdn.net/wdong_love_cl/article/details/51503157说那么多我们就是为了看一下getMeasuredHeight()这个方法:
private int getMeasuredHeight(int heightMeasureSpec) {
int result;
int mode = MeasureSpec.getMode(heightMeasureSpec);
int size = MeasureSpec.getSize(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {//精确值
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;
}
这个还是很明确的,如果指定了精确的值,那么我们就直接使用指定的size了,否则的话我们就指定计算文字的高度,并计算该子控件的尺寸,进行AT_MOST
模式下的赋值。
网上找了一张Paint类中的descent和ascent的图,帮助理解textHeight的值结果:
三、布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
android:weightSum="1">
<com.example.hejingzhou.selfprogress.view.HorizontalProgressbarWithProgrss
android:id="@+id/horizontalProgressbarWithProgrssTop"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:layout_width="match_parent"
android:padding="5dp"
android:progress="60"
android:indeterminate="false" />
</LinearLayout>
四、MainActivity进行模拟进度
package com.example.hejingzhou.selfprogress.activity;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.example.hejingzhou.selfprogress.R;
import com.example.hejingzhou.selfprogress.view.HorizontalProgressbarWithProgrss;
public class MainActivity extends AppCompatActivity {
private HorizontalProgressbarWithProgrss progressBar;
private Handler handler;
private int progrssNum = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (HorizontalProgressbarWithProgrss) findViewById(R.id.horizontalProgressbarWithProgrssTop);
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what==0){
progressBar.setProgress(progrssNum);
}else {
Toast.makeText(getApplicationContext(),"下载完成",Toast.LENGTH_SHORT).show();
//progressBar.setVisibility(View.GONE);
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
while (true){
progrssNum = doWork();
Message message = new Message();
if(progrssNum<100){
message.what = 0;
handler.sendMessage(message);
}else {
message.what = 1;
handler.sendMessage(message);
break;
}
}
}
private int doWork(){
progrssNum+= Math.random()*10;
try{
Thread.sleep(200);
}catch (InterruptedException e){
e.printStackTrace();
}
return progrssNum;
}
}).start();
}
}
画的草图也传不上了,算了。
运行图