編輯:關於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
前言:其實RxJava引起的內存洩漏是我無意中發現了,本來是想了解Retrofit與RxJava相結合中是如何通過適配器模式解決的,結果卻發現了RxJava是會引起內存洩
上兩篇我們分析完了處理器的process方法的findAndParseTargets方法來獲取了一個集合,該集合包含了你使用注解的類的TypeElement和這個類中的注
介紹:Statically typed programming language for the JVM, Android and the browser. 100% i
上一篇把簡單的一些概念理一理,還畫了個圈,那這一篇講一下圖像遮蓋&ldq