由於重新理了理博客,所以將此篇更改了下作爲自定義的代碼篇,如果不懂自定義View的原理和實現步驟的可以先看看android之自定義View和ViewGroup(一),具體解釋不就不廢話了,前面那個鏈接上寫的很清楚了,此篇直接來代碼實現。
假如我們需要實現的效果是這樣(模擬器有點卡,見諒):
那麼我們可以分析下,需要什麼思路,由於圖片問題,圖中可能不明顯,結構是這樣的:
三層,一層紅色背景,一層內層粉色,再是一個綠色進度的方形條,那麼我們就畫一個方形紅色背景,在裏面再畫一個粉色方形,然後裏面再畫一個綠色的方形進行移動,但是移動範圍是粉色框內,思路就是這樣,那麼開始
我們自定義view就需要幾個屬性:
1.背景色(圖中紅色)
2.內部背景色(圖中粉色)
3.進度方塊的顏色(圖中綠色)
4.紅色框的線的寬度(因爲我畫的是一個沒有填充滿顏色的紅色框,只是畫的一個帶紅色stroke的框,可以根據自己的想法更改)
那麼自定義一共要三個模塊:
1.在attrs中自定義需要的屬性(上面4個)
2.繼承View自定義View進行畫進度條的邏輯
3.在xml佈局中使用
我們先來第一個模塊:
(1)在attrs中自定義需要的屬性(如果沒有attrs.xml就在res/values下建個就行)
<attr name="backColor" format="color"/>
<attr name="progressColor" format="color"/>
<attr name="strokeColor" format="color"/>
<attr name="strokeWidth" format="dimension"/>
<declare-styleable name="CustomprogressBarView">
<attr name="strokeWidth"/>
<attr name="backColor"/>
<attr name="progressColor"/>
<attr name="strokeColor"/>
</declare-styleable>
我們自定義出了4個需要的屬性,name表示引用名,format表示值的類型,(2)繼承View自定義View進行畫進度條的邏輯實現,寫CustomProgressbar繼承自View,然後重寫它的ondraw方法
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import com.custom.my.R;
/**
* Created by Administrator on 2016/1/14.
*/
public class CustomProgressbar extends View {
private int backColor; //圖中粉色
private int progressColor; //圖中綠色
private int strokeColor; //圖中紅色
private Paint paintBack; //粉色對應的畫筆
private Paint paintProgress; //綠色對應的畫筆
private Paint paintStroke; //紅色對應的畫筆
private int progress = 1; //代表進度快(將圖中粉色分爲30塊,那麼進度條的方式就是從第一塊移動到第二塊,第三塊。。。最後一塊,回到第一塊),可以自定義
private int num = 30; //粉色分爲多少塊,這裏設爲30塊,如果需要自定義也可以,這裏就直接這樣吧
private float averageWidth; //平均寬度
private float strokeWidth; //紅色的寬度
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
@Override
public void run() {
postInvalidate(); //刷新view
progress ++;
if(progress == num+1){
progress = 1;
}
handler.postDelayed(this,100); //每隔100ms就移動一次(比如粉色分爲30塊,綠色在第一塊,然後100ms後移動到第二塊,第三塊。。。等)
}
};
public CustomProgressbar(Context context) {
this(context,null);
}
public CustomProgressbar(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public CustomProgressbar(Context context, AttributeSet attrs, int defStyleAttr) { //構造方法不用說了吧
super(context, attrs, defStyleAttr);
TypedArray arr = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomprogressBarView, defStyleAttr, 0); //獲取自定義的屬性值組
backColor = arr.getColor(R.styleable.CustomprogressBarView_backColor, Color.WHITE); //獲取自定義的屬性backColor,默認爲白色
progressColor = arr.getColor(R.styleable.CustomprogressBarView_progressColor, Color.RED); //獲取自定義的屬性progressColor,默認爲紅色
strokeColor = arr.getColor(R.styleable.CustomprogressBarView_strokeColor, Color.GRAY); //獲取自定義的屬性,外框的顏色即圖中的紅色框
strokeWidth = arr.getDimensionPixelSize(R.styleable.CustomprogressBarView_strokeWidth, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15, getResources().getDisplayMetrics())); //獲取自定義的設置線條框寬度的屬性,默認爲15sp
arr.recycle(); //用完後記得recycle();
paintBack = new Paint();
paintProgress = new Paint();
paintStroke = new Paint();
handler.postDelayed(runnable,100); //使用handler實現定時更新操作
}
@Override
protected void onDraw(Canvas canvas) { //重寫onDraw來畫進度條
int width = getWidth(); //獲取自定義的View的寬度
int height = getHeight(); //獲取自定義的View的高度
//設置紅色框畫筆屬性,寬度和顏色,描邊效果
paintStroke.setColor(strokeColor);
paintStroke.setStrokeWidth(strokeWidth);
paintStroke.setDither(true);
paintStroke.setStyle(Paint.Style.STROKE);
canvas.drawRect(strokeWidth / 2, strokeWidth / 2, width - strokeWidth / 2, height - strokeWidth / 2, paintStroke); //畫紅色矩形框,參數是:左上右下位置和畫筆
//設置粉色框的畫筆屬性
paintBack.setColor(backColor);
paintBack.setDither(true);
canvas.drawRect(strokeWidth, strokeWidth, width-strokeWidth, height-strokeWidth, paintBack);//畫粉色矩形框,參數同上
float contentWidth = width - strokeWidth*2; //這裏畫綠色進度條框,需要計算寬了,因爲我們分成了30塊,橫向滑動,綠色塊的活動範圍是粉色框,粉色框寬度是總寬度減去左右2根紅色線條的寬度
averageWidth = contentWidth/30; //綠色進度框的寬度
//綠色進度框的畫筆屬性
paintProgress.setColor(progressColor);
paintProgress.setDither(true);
canvas.drawRect((progress - 1) * averageWidth + strokeWidth, strokeWidth, progress * averageWidth + strokeWidth, height - strokeWidth, paintProgress); //根據progress記錄綠色框移動到粉色的30塊中的哪一塊,然後在對應的位置畫下綠色框
}
}
(3)在xml佈局中使用
記得加入命名空間:圖中綠色代碼,或者:xmlns:xjj="http://schemas.android.com/apk/res-auto",這個xjj可以自定義,定義後,代碼中引用屬性的時候就要用這個,即下面的紅色代碼
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:xjj="http://schemas.android.com/apk/res/com.custom.my"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.custom.my.activity.CustomProgressActivity">
<com.custom.my.view.CustomProgressbar
android:layout_centerInParent="true"
xjj:backColor="#50ff0000"
xjj:strokeWidth = "1dp"
xjj:strokeColor = "#50ff00ff"
xjj:progressColor = "#00ffaa"
android:layout_width="100dp"
android:layout_height="5dp" />
</RelativeLayout>
然後這樣就完成了進度條的自定義效果,執行就行了 ,有什麼不懂得可以問我。
對了,如果看了android之自定義View和ViewGroup(一)的人可能會問了,不是說要重寫onMeasure方法測量大小麼?測量大小也是需要分情況的,比如自定義這個進度條,必須給一個固定的寬高值,不然設置成wrap_content也不知改設置多大啊。當然你可以設置爲wrap_content然後在onMeasure方法裏面測量的時候如果是wrap_content就給他一個默認的寬高之類的;由於咋們使用這個進度條肯定是給定了具體的寬高,所以就不需要根據測量模式進行測量了,因爲給了固定寬高,那麼默認測量的結果就是固定寬高的結果,結果正確。只有在沒有設置固定寬高的時候需要測量,比如自定義ImageView那種,設置爲wrap_content就需要測量了,此時ImageView的寬高需要設爲圖片的寬和高才正確。而這個進度條這個你根本不知道內容寬高,所以設爲具體值合理又方便。