1、概述
多看多學漲姿勢, github真是個寶庫
這個項目主要是實作數字進度條效果
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI1ADMwETNwEjMwITMwYTMwIzLcRXZu5ibkN3Yuc2bsJmLn1Wavw1LcpDc0RHaiojIsJye.jpg)
感謝開源作者!
梳理主要知識點:
【1】熟悉自定義view的流程
【2】實作原理
【4】onmeasure優雅的方法書寫
【5】canvas中drawtext方法注意點
【6】代碼的可讀性非常強
2、項目要點分析
【熟悉自定義view的流程】
【本項目實作原理】
該項目比較基礎,适合作為入門學習項目,作者主要将自定義控件分為3大區域
mreachedrectf——text區域(可以選擇沒有)——munreachedrectf
(該控件支援沒有text區域),主要是通過控制mreachedrectf和munreachedrectf的坐标來不斷地重新整理ui來實作移動效果,沒有使用到動畫
自定義view 步驟 之擷取自定義屬性
這裡作者直接寫到其中一個構造方法中
public numberprogressbar(context context, attributeset attrs, int defstyleattr) {
super(context, attrs, defstyleattr);
default_reached_bar_height = dp2px(1.5f);
default_unreached_bar_height = dp2px(1.0f);
default_text_size = sp2px(10);
default_progress_text_offset = dp2px(3.0f);
//load styled attributes.
final typedarray attributes = context.gettheme().obtainstyledattributes(attrs, r.styleable.numberprogressbar,
defstyleattr, 0);
mreachedbarcolor = attributes.getcolor(r.styleable.numberprogressbar_progress_reached_color, default_reached_color);
munreachedbarcolor = attributes.getcolor(r.styleable.numberprogressbar_progress_unreached_color, default_unreached_color);
mtextcolor = attributes.getcolor(r.styleable.numberprogressbar_progress_text_color, default_text_color);
mtextsize = attributes.getdimension(r.styleable.numberprogressbar_progress_text_size, default_text_size);
mreachedbarheight = attributes.getdimension(r.styleable.numberprogressbar_progress_reached_bar_height, default_reached_bar_height);
munreachedbarheight = attributes.getdimension(r.styleable.numberprogressbar_progress_unreached_bar_height, default_unreached_bar_height);
moffset = attributes.getdimension(r.styleable.numberprogressbar_progress_text_offset, default_progress_text_offset);
int textvisible = attributes.getint(r.styleable.numberprogressbar_progress_text_visibility, progress_text_visible);
if (textvisible != progress_text_visible) {
mifdrawtext = false;
}
setprogress(attributes.getint(r.styleable.numberprogressbar_progress_current, 0));
setmax(attributes.getint(r.styleable.numberprogressbar_progress_max, 100));
attributes.recycle();
initializepainters();
}
這裡作者開始初始化自定義的屬性,通常我們可以單獨使用一個函數放在每個構造器下面
然後就是onmeasure,這裡比較優雅,
其中exactly主要針對match_parent/具體參數
atmost主要針對wrap_content情況這裡做了處理,有時候我們如果把布局檔案的寬和高寫成wrap_content,若此時父布局也為at_most此時顯示的就是父布局的praentsize
是以我們支援設定wrap_content時候需要重寫onmeasure方法,下面也是做了處理(at_most)
而unspecified往往用于系統内部的測量通常隻需要關注atmost和exactly
@override
protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
setmeasureddimension(measure(widthmeasurespec, true), measure(heightmeasurespec, false));
}
private int measure(int measurespec, boolean iswidth) {
int result;
int mode = measurespec.getmode(measurespec);
int size = measurespec.getsize(measurespec);
int padding = iswidth ? getpaddingleft() + getpaddingright() : getpaddingtop() + getpaddingbottom();
if (mode == measurespec.exactly) {
result = size;
} else {
result = iswidth ? getsuggestedminimumwidth() : getsuggestedminimumheight();
result += padding;
if (mode == measurespec.at_most) {
if (iswidth) {
result = math.max(result, size);
} else {
result = math.min(result, size);
}
}
}
return result;
ondrawer方法的可讀性更是6666
有時候我們也要注意寫出“可以說話的代碼”,注意函數的封裝
protected void ondraw(canvas canvas) {
if (mifdrawtext) {
calculatedrawrectf();
} else {
calculatedrawrectfwithoutprogresstext();
if (mdrawreachedbar) {
canvas.drawrect(mreachedrectf, mreachedbarpaint);
if (mdrawunreachedbar) {
canvas.drawrect(munreachedrectf, munreachedbarpaint);
if (mifdrawtext)
canvas.drawtext(mcurrentdrawtext, mdrawtextstart, mdrawtextend, mtextpaint);
這段代碼我們可以清晰的看出作者的邏輯 要是設定了數字顯示執行calculatedrawrectf()否則執行calculatedrawrectfwithoutprogresstext()
這倆個函數就是開始處理前文提到的mreachedrectf和munreachedrectf倆個矩形的位置變化,這裡需要熟悉一下android中的坐标系和點選位置的擷取
看一下有文字時候計算倆個區域的情況主要集中處理了開始階段和最終階段的文字未知的特殊情況
private void calculatedrawrectf() {
mcurrentdrawtext = string.format("%d", getprogress() * 100 / getmax());
mcurrentdrawtext = mprefix + mcurrentdrawtext + msuffix;//轉換成字元串
mdrawtextwidth = mtextpaint.measuretext(mcurrentdrawtext);//測量出文字的長度
if (getprogress() == 0) {
mdrawreachedbar = false;
mdrawtextstart = getpaddingleft();//起始位置(右)
} else {
mdrawreachedbar = true;
mreachedrectf.left = getpaddingleft();
mreachedrectf.top = getheight() / 2.0f - mreachedbarheight / 2.0f;
mreachedrectf.right = (getwidth() - getpaddingleft() - getpaddingright()) / (getmax() * 1.0f) * getprogress() - moffset + getpaddingleft();
mreachedrectf.bottom = getheight() / 2.0f + mreachedbarheight / 2.0f;
mdrawtextstart = (mreachedrectf.right + moffset);//實際中右位置
}
//mtextpaint.descent() + mtextpaint.ascent() baseline到字型最高+baseline到字型最低=實際字型高度
mdrawtextend = (int) ((getheight() / 2.0f) - ((mtextpaint.descent() + mtextpaint.ascent()) / 2.0f));
if ((mdrawtextstart + mdrawtextwidth) >= getwidth() - getpaddingright()) {
//文字終點位置重置文字起始位置和mreachedrectf矩形的right
mdrawtextstart = getwidth() - getpaddingright() - mdrawtextwidth;
mreachedrectf.right = mdrawtextstart - moffset;
float unreachedbarstart = mdrawtextstart + mdrawtextwidth + moffset;
if (unreachedbarstart >= getwidth() - getpaddingright()) {
//沒有到最終點
mdrawunreachedbar = false;
mdrawunreachedbar = true;
munreachedrectf.left = unreachedbarstart;
munreachedrectf.top = getheight() / 2.0f -munreachedbarheight / 2.0f;
munreachedrectf.right = getwidth() - getpaddingright();
munreachedrectf.bottom = getheight() / 2.0f + munreachedbarheight / 2.0f;
}
這裡主要提下mtextpaint.descent() + mtextpaint.ascent()的這裡參照的距離都是baseline,這樣可以計算出整個字型的高度,具體可以參考
還有canvas.drawtext方法中xy坐标其實是baseline的位置,這在校準字型位置的時候很有用!
轉載:http://blog.csdn.net/xsf50717/article/details/50550895