效果圖:
xml 布局:
···
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:ap="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.progress.MainActivity">
<LinearLayout
android:layout_width="200dp"
android:layout_height="200dp"
android:orientation="vertical">
<com.example.progress.RoundProgress
android:id="@+id/progress"
android:layout_width="180dp"
android:layout_height="180dp"
android:layout_marginTop="10dp"
ap:roundColor="@color/colorPrimaryDark"
ap:textColor="@color/colorPrimary"
ap:textSize="20dp"
ap:max="100"
ap:progress="70"
ap:roundProgressColor="@color/colorAccent"
/>
</LinearLayout>
</LinearLayout>
···
MainActivity 中代碼
···
package com.example.progress;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private RoundProgress progress;
Runnable runnable=new Runnable() {
@Override
public void run() {
progress.setMax(100);
for (int i = 0; i <90 ; i++) {
progress.setProgress(i+1);
SystemClock.sleep(20);
//強(qiáng)制重繪
// progress.invalidate(); //只有主線程可以調(diào)用
progress.postInvalidate();//主線程 分線程都可以如此調(diào)用
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
progress = (RoundProgress) findViewById(R.id.progress);
//分線程 實(shí)現(xiàn)進(jìn)度變化
new Thread(runnable).start();
}
}
···
自定義類 RoundProgress 繼承View
具體代碼
···
package com.example.progress;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**
Created by shkstart on 2016/12/3 0003.
-
自定義視圖
*/
public class RoundProgress extends View {//設(shè)置繪制的圓環(huán)及文本的屬性---->使用自定義屬性替換
// private int roundColor = Color.GRAY;//圓環(huán)的顏色
// private int roundProgressColor = Color.RED;//圓弧的顏色
// private int textColor = Color.BLUE;//文本的顏色
//
// private int roundWidth = UIUtils.dp2px(10);//圓環(huán)的寬度
// private int textSize = UIUtils.dp2px(20);//文本的字體大小
//
// private int max = 100;//圓環(huán)的最大值
// private int progress = 60;//圓環(huán)的進(jìn)度//使用自定義屬性來(lái)初始化如下的變量
private int roundColor;//圓環(huán)的顏色
private int roundProgressColor ;//圓弧的顏色
private int textColor;//文本的顏色private float roundWidth ;//圓環(huán)的寬度
private float textSize ;//文本的字體大小private int max;//圓環(huán)的最大值
private int progress;//圓環(huán)的進(jìn)度private int width;//當(dāng)前視圖的寬度(=高度)
private Paint paint;//畫筆
public RoundProgress(Context context) {
this(context, null);
}public RoundProgress(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}public RoundProgress(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);//初始化畫筆 paint = new Paint(); paint.setAntiAlias(true);//去除毛邊 //獲取自定義的屬性 //1.獲取TypeArray的對(duì)象(調(diào)用兩個(gè)參數(shù)的方法) TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundProgress); //2.取出所有的自定義屬性 roundColor = typedArray.getColor(R.styleable.RoundProgress_roundColor, Color.GRAY); roundProgressColor = typedArray.getColor(R.styleable.RoundProgress_roundProgressColor,Color.RED); textColor = typedArray.getColor(R.styleable.RoundProgress_textColor,Color.GREEN); roundWidth = typedArray.getDimension(R.styleable.RoundProgress_roundWith,UIUtils.dp2px(10)); textSize = typedArray.getDimension(R.styleable.RoundProgress_textSize,UIUtils.dp2px(20)); max = typedArray.getInteger(R.styleable.RoundProgress_max,100); progress = typedArray.getInteger(R.styleable.RoundProgress_progress,30); //3.回收處理 typedArray.recycle();
}
public int getMax() {
return max;
}public void setMax(int max) {
this.max = max;
}public int getProgress() {
return progress;
}public void setProgress(int progress) {
this.progress = progress;
}//測(cè)量:獲取當(dāng)前視圖寬高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = this.getMeasuredWidth();
}//canvas:畫布,對(duì)應(yīng)著視圖在布局中的范圍區(qū)間。范圍的左上頂點(diǎn)即為坐標(biāo)原點(diǎn)
@Override
protected void onDraw(Canvas canvas) {
//1.繪制圓環(huán)
//獲取圓心坐標(biāo)
int cx = width / 2;
int cy = width / 2;
float radius = width / 2 - roundWidth / 2;
paint.setColor(roundColor);//設(shè)置畫筆顏色
paint.setStyle(Paint.Style.STROKE);//設(shè)置圓環(huán)的樣式
paint.setStrokeWidth(roundWidth);//設(shè)置圓環(huán)的寬度
canvas.drawCircle(cx, cy, radius, paint);//2.繪制圓弧 RectF rectF = new RectF(roundWidth / 2, roundWidth / 2, width - roundWidth / 2, width - roundWidth / 2); paint.setColor(roundProgressColor);//設(shè)置畫筆顏色 canvas.drawArc(rectF,0,progress * 360 / max ,false,paint); //3.繪制文本 String text = progress * 100 / max + "%"; //設(shè)置paint paint.setColor(textColor); paint.setTextSize(textSize); paint.setStrokeWidth(0); Rect rect = new Rect();//創(chuàng)建了一個(gè)矩形,此時(shí)矩形沒有具體的寬度和高度 paint.getTextBounds(text,0,text.length(),rect);//此時(shí)的矩形的寬度和高度即為整好包裹文本的矩形的寬高 //獲取左下頂點(diǎn)的坐標(biāo) int x = width / 2 - rect.width() / 2; int y = width / 2 + rect.height() / 2; canvas.drawText(text,x,y,paint);
}
}
···
MyApplication類
···
package com.example.progress;
import android.app.Application;
import android.content.Context;
import android.os.Handler;
/**
-
Created by shkstart on 2016/12/2 0002.
*/
public class MyApplication extends Application {//在整個(gè)應(yīng)用執(zhí)行過(guò)程中,需要提供的變量
public static Context context;//需要使用的上下文對(duì)象:application實(shí)例
public static Handler handler;//需要使用的handler
public static Thread mainThread;//提供主線程對(duì)象
public static int mainThreadId;//提供主線程對(duì)象的id@Override
public void onCreate() {
super.onCreate();context = this.getApplicationContext(); handler = new Handler(); mainThread = Thread.currentThread();//實(shí)例化當(dāng)前Application的線程即為主線程 mainThreadId = android.os.Process.myTid();//獲取當(dāng)前線程的id //設(shè)置未捕獲異常的處理器
// CrashHandler.getInstance().init();
//初始化ShareSDK
}
}
···
UiUtils類
···
package com.example.progress;
import android.content.Context;
import android.os.Handler;
import android.view.View;
import android.widget.Toast;
/**
專門提供為處理一些UI相關(guān)的問(wèn)題而創(chuàng)建的工具類,
-
提供資源獲取的通用方法,避免每次都寫重復(fù)的代碼獲取結(jié)果。
*/
public class UIUtils {public static Context getContext(){
return MyApplication.context;
}public static Handler getHandler(){
return MyApplication.handler;
}//返回指定colorId對(duì)應(yīng)的顏色值
public static int getColor(int colorId){
return getContext().getResources().getColor(colorId);
}//加載指定viewId的視圖對(duì)象,并返回
public static View getView(int viewId){
View view = View.inflate(getContext(), viewId, null);
return view;
}public static String[] getStringArr(int strArrId){
String[] stringArray = getContext().getResources().getStringArray(strArrId);
return stringArray;
}//將dp轉(zhuǎn)化為px
public static int dp2px(int dp){
//獲取手機(jī)密度
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (dp * density + 0.5);//實(shí)現(xiàn)四舍五入
}public static int px2dp(int px){
//獲取手機(jī)密度
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (px / density + 0.5);//實(shí)現(xiàn)四舍五入
}//保證runnable中的操作在主線程中執(zhí)行
public static void runOnUiThread(Runnable runnable) {
if(isInMainThread()){
runnable.run();
}else{
UIUtils.getHandler().post(runnable);
}
}
//判斷當(dāng)前線程是否是主線程
private static boolean isInMainThread() {
int currentThreadId = android.os.Process.myTid();
return MyApplication.mainThreadId == currentThreadId;}
public static void toast(String message,boolean isLengthLong){
Toast.makeText(UIUtils.getContext(), message,isLengthLong? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show();
}
}
···