編輯:關於Android編程
拖了這麼久才開始更新csdn,著實是懶到家了,寫這篇博客的目的就是為了幫助更多的android入門開發者更多的了解自定義控件,畢竟自定義控件對新手來說還是比較神秘的,多說無益,直接上圖:

以上就是今天我們要實現的效果,乍一看是不是覺得高端大氣上檔次,完全沒有什麼頭緒怎麼去實現這麼“高端”的東西。還會不定時的反問自己可以嗎?對,你可以的。讓我們一起來學習如何寫這樣的控件吧。
【前言】自定義view 的幾個步驟
自定義view的屬性在view 的構造方法中獲取我們自定義的屬性的值重寫onMeasure方法(有時不需要重寫這個方法)重寫onDraw方法
【正文】
項目結構圖奉上:


我們先來分析一下這個控件。這個控件主要有兩種顏色,加載的速度,圓環的寬度,好像也沒有其他屬性值了。
1.自定義屬性:attrs.xml
這裡的format主要常見的有以下幾種屬性:reference 、color、boolean、dimension、float、integer、string、fraction、enum、flag。更多用法以及如何在初始化時獲取相應的值,可以百度,這裡不是我們的重點。
2.在構造方法中獲取我們自定義的屬性:CustomProgressbar.java
// 設置第一圈顏色
private int mFirstColor=Color.GREEN;
// 設置第二圈顏色
private int mSecondColor=Color.RED;
// 設置圈的寬度
private int mCircleWidth=20;
// 設置顏色填充畫筆
private Paint mPaint;
// 設置當前進度
private int mProgress;
// 設置當前進度加載速度
private int speed=20;
// 是否開始下一個
private boolean isNext = false;
public CustomProgressbar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomProgressbar(Context context) {
this(context, null);
}
/**
* 必要的初始化,獲取一些自定義的值
*
* @param context
* @param attrs
* @param defStyle
*/
public CustomProgressbar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 獲取自定義的屬性集合
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomProgressbar, defStyle, 0);
// 獲取自定義屬性的個數
int n = array.getIndexCount();
Log.i(test, 自定義屬性的個數: + n);
// 遍歷屬性值
for (int i = 0; i < n; i++) {
int attr = array.getIndex(i);
Log.i(test, 自定義的屬性為:+attr);
switch (attr) {
case R.styleable.CustomProgressbar_firstColor:
// 獲取第一圈顏色值
mFirstColor = array.getColor(attr, Color.GREEN);
break;
case R.styleable.CustomProgressbar_secondColor:
// 獲取第一圈顏色值
mSecondColor = array.getColor(attr, Color.RED);
break;
case R.styleable.CustomProgressbar_circleWidth:
// 設置默認圈的寬度為20px
mCircleWidth = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 20, getResources().getDisplayMetrics()));
break;
case R.styleable.CustomProgressbar_speed:
// 獲取默認加載速度
speed = array.getInt(attr, 20);
break;
}
}
// 回收
array.recycle();
mPaint = new Paint();
// 繪圖線程 此線程為耗時線程,放在子線程中執行,防止主線程的卡頓
new Thread() {
public void run() {
while (true) {
mProgress++;
if (mProgress == 360) {
mProgress = 0;
// 如果沒有開始下一個,則設置isNext為true
if (!isNext) {
isNext = true;
} else {
isNext = false;
}
}
// 刷新UI
// postInvalidate()此方法可以直接在UI線程調用,invalidate()則需要在handler中進行調用
postInvalidate();
try {
Thread.sleep(speed);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
很多童鞋肯定會問,為什麼這個操作要放在thread裡進行操作?重寫view是一個耗時操作,所以我們這裡就直接放在子線程裡進行重繪了,防止程序卡死。還有一個需要注意的就是postInvalidate()方法。postInvalidate()此方法可以直接在UI線程調用,invalidate()則需要在handler中進行調用,想要了解更多的童鞋也可以自己百度兩者之間的區別。
4.重寫onDraw方法,進行view 的繪制:CustomProgressbar.java
@Override
protected void onDraw(Canvas canvas) {
// 獲取圓心的x坐標
int center = getWidth() / 2;
// 獲取圓的半徑
int radius = center - mCircleWidth / 2;
// 設置填充的寬度
mPaint.setStrokeWidth(mCircleWidth);
mPaint.setAntiAlias(true);
// 設置填充的style
mPaint.setStyle(Paint.Style.STROKE);
// new RectF(left, top, right, bottom) 為距離x軸,y軸之間的距離
// 定義rect的形狀
RectF f = new RectF(center - radius, center - radius, center + radius, center + radius);
if (!isNext) {
// 第一圈顏色完整,第二圈顏色跑
mPaint.setColor(mFirstColor);// 設置畫筆顏色
// 畫出圓環
canvas.drawCircle(center, center, radius, mPaint);
// 設置圓環顏色
mPaint.setColor(mSecondColor);
/*
* public void drawArc(RectF oval, float startAngle, float sweepAngle,
* boolean useCenter, Paint paint) oval :指定圓弧的外輪廓矩形區域。 startAngle:
* 圓弧起始角度,單位為度。 sweepAngle: 圓弧掃過的角度,順時針方向,單位為度。 useCenter:
* 如果為True時,在繪制圓弧時將圓心包括在內,通常用來繪制扇形。 paint: 繪制圓弧的畫板屬性,如顏色,是否填充等。
*/
canvas.drawArc(f, -90, mProgress, false, mPaint);
} else {
// 第一圈顏色完整,第二圈顏色跑
mPaint.setColor(mSecondColor);// 設置畫筆顏色
// 畫出圓環
canvas.drawCircle(center, center, radius, mPaint);
// 設置圓環顏色
mPaint.setColor(mFirstColor);
/*
* public void drawArc(RectF oval, float startAngle, float sweepAngle,
* boolean useCenter, Paint paint) oval :指定圓弧的外輪廓矩形區域。 startAngle:
* 圓弧起始角度,單位為度。 sweepAngle: 圓弧掃過的角度,順時針方向,單位為度。 useCenter:
* 如果為True時,在繪制圓弧時將圓心包括在內,通常用來繪制扇形。 paint: 繪制圓弧的畫板屬性,如顏色,是否填充等。
*/
canvas.drawArc(f, -90, mProgress, false, mPaint);
}
}
這裡關於drawArc的方法,我已經加了詳細的注釋,不明白的同學可以自己百度一下。
由於上面貼的是片段代碼,這裡給出CustomProgressbar.java的全部代碼:
package com.beyole.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import com.beyole.circlewaitting.R;
public class CustomProgressbar extends View {
// 設置第一圈顏色
private int mFirstColor=Color.GREEN;
// 設置第二圈顏色
private int mSecondColor=Color.RED;
// 設置圈的寬度
private int mCircleWidth=20;
// 設置顏色填充畫筆
private Paint mPaint;
// 設置當前進度
private int mProgress;
// 設置當前進度加載速度
private int speed=20;
// 是否開始下一個
private boolean isNext = false;
public CustomProgressbar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomProgressbar(Context context) {
this(context, null);
}
/**
* 必要的初始化,獲取一些自定義的值
*
* @param context
* @param attrs
* @param defStyle
*/
public CustomProgressbar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 獲取自定義的屬性集合
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomProgressbar, defStyle, 0);
// 獲取自定義屬性的個數
int n = array.getIndexCount();
Log.i(test, 自定義屬性的個數: + n);
// 遍歷屬性值
for (int i = 0; i < n; i++) {
int attr = array.getIndex(i);
Log.i(test, 自定義的屬性為:+attr);
switch (attr) {
case R.styleable.CustomProgressbar_firstColor:
// 獲取第一圈顏色值
mFirstColor = array.getColor(attr, Color.GREEN);
break;
case R.styleable.CustomProgressbar_secondColor:
// 獲取第一圈顏色值
mSecondColor = array.getColor(attr, Color.RED);
break;
case R.styleable.CustomProgressbar_circleWidth:
// 設置默認圈的寬度為20px
mCircleWidth = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 20, getResources().getDisplayMetrics()));
break;
case R.styleable.CustomProgressbar_speed:
// 獲取默認加載速度
speed = array.getInt(attr, 20);
break;
}
}
// 回收
array.recycle();
mPaint = new Paint();
// 繪圖線程 此線程為耗時線程,放在子線程中執行,防止主線程的卡頓
new Thread() {
public void run() {
while (true) {
mProgress++;
if (mProgress == 360) {
mProgress = 0;
// 如果沒有開始下一個,則設置isNext為true
if (!isNext) {
isNext = true;
} else {
isNext = false;
}
}
// 刷新UI
// postInvalidate()此方法可以直接在UI線程調用,invalidate()則需要在handler中進行調用
postInvalidate();
try {
Thread.sleep(speed);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
@Override
protected void onDraw(Canvas canvas) {
// 獲取圓心的x坐標
int center = getWidth() / 2;
// 獲取圓的半徑
int radius = center - mCircleWidth / 2;
// 設置填充的寬度
mPaint.setStrokeWidth(mCircleWidth);
mPaint.setAntiAlias(true);
// 設置填充的style
mPaint.setStyle(Paint.Style.STROKE);
// new RectF(left, top, right, bottom) 為距離x軸,y軸之間的距離
// 定義rect的形狀
RectF f = new RectF(center - radius, center - radius, center + radius, center + radius);
if (!isNext) {
// 第一圈顏色完整,第二圈顏色跑
mPaint.setColor(mFirstColor);// 設置畫筆顏色
// 畫出圓環
canvas.drawCircle(center, center, radius, mPaint);
// 設置圓環顏色
mPaint.setColor(mSecondColor);
/*
* public void drawArc(RectF oval, float startAngle, float sweepAngle,
* boolean useCenter, Paint paint) oval :指定圓弧的外輪廓矩形區域。 startAngle:
* 圓弧起始角度,單位為度。 sweepAngle: 圓弧掃過的角度,順時針方向,單位為度。 useCenter:
* 如果為True時,在繪制圓弧時將圓心包括在內,通常用來繪制扇形。 paint: 繪制圓弧的畫板屬性,如顏色,是否填充等。
*/
canvas.drawArc(f, -90, mProgress, false, mPaint);
} else {
// 第一圈顏色完整,第二圈顏色跑
mPaint.setColor(mSecondColor);// 設置畫筆顏色
// 畫出圓環
canvas.drawCircle(center, center, radius, mPaint);
// 設置圓環顏色
mPaint.setColor(mFirstColor);
/*
* public void drawArc(RectF oval, float startAngle, float sweepAngle,
* boolean useCenter, Paint paint) oval :指定圓弧的外輪廓矩形區域。 startAngle:
* 圓弧起始角度,單位為度。 sweepAngle: 圓弧掃過的角度,順時針方向,單位為度。 useCenter:
* 如果為True時,在繪制圓弧時將圓心包括在內,通常用來繪制扇形。 paint: 繪制圓弧的畫板屬性,如顏色,是否填充等。
*/
canvas.drawArc(f, -90, mProgress, false, mPaint);
}
}
}
自定義的控件已經定義結束,接下來就是如何調用我們寫的view了,首先,在我們的主布局文件:activity_main.xml中進行引用:
xmlns:beyole=http://schemas.android.com/apk/res/com.beyole.circlewaitting這裡的com.beyole.circlewaitting就是我們應用程序的包名。如何知道我們應用程序的包名?直接在AndroidManifest.xml文件中

主布局文件裡面沒有更改內容:MainActivity.java
package com.beyole.circlewaitting;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
寫到這裡是不是不難了,其實代碼也就這麼一點點,只要多寫多練,就一定會熟能生巧的。
制作一個簡單的新聞客戶端
前面介紹了在Android中實現網絡通信,這篇文章將是對前面介紹的技術的一個綜合運用,制作一個簡單的新聞客戶端,在這個新聞客戶端中用到了ListView、ListView
Android性能優化之利用Rxlifecycle解決RxJava內存洩漏詳解
前言:其實RxJava引起的內存洩漏是我無意中發現了,本來是想了解Retrofit與RxJava相結合中是如何通過適配器模式解決的,結果卻發現了RxJava是會引起內存洩
深入理解ButterKnife源碼並掌握原理(三)
上兩篇我們分析完了處理器的process方法的findAndParseTargets方法來獲取了一個集合,該集合包含了你使用注解的類的TypeElement和這個類中的注
Java的進化? Kotlin初探與集成Android項目
介紹:Statically typed programming language for the JVM, Android and the browser. 100% i
自定義View時,用到Paint Canvas的一些溫故,講講平時一些效果是怎麼畫的(基礎篇 二,圖像遮蓋,Canvas靜態變化)
上一篇把簡單的一些概念理一理,還畫了個圈,那這一篇講一下圖像遮蓋&ldq