Android控制元件之LabelView
最近研究android控制元件開發,學習無捷徑,逛github,看到一個LabelView的demo,感覺還是挺不錯,也比較實用,所以拿來學習了一下。
附上源專案的地址:https://github.com/linger1216/labelview
效果圖:
再開始學習原始碼之前我們可以先分析build的過程:
label是放到原始標籤之上的,所以label可能是一個單獨的控制元件疊加到view之上。可惜像Button這樣的控制元件 是不支援含有子View的,所以排除了這種可能;那麼另一種可能就是在控制元件的指定位置繪製一個label了。
下面進入原始碼:
我們直接進入builder,
在這之前我們還需要明確 onDraw onMeasure onLayout 三個函式
onDraw:所有繪製view 的動作在這裡
onMeasure:決定了view的大小
onLayout:決定了view 的位置
首先先分析下對與label這個view需要設定什麼樣的屬性:
position:位置(Left_top,Right_top,Left_bottom,Right_bottom)
visible:可見(true,false)
height:label寬度(個人喜好,)
distance:label距角的距離
textSize:字型大小
textColor:字型顏色
background:背景色
以上這些屬性我們需要先設定好預設值,以便於我們通過TypedArray獲取設定的引數。
public static final int RIGHT_TOP = 1;
public static final int LEFT_TOP = 2;
public static final int RIGHT_BOTTOM = 3;
public static final int LEFT_BOTTOM = 4;
public static final boolean DEAFULT_VISIABLE = true;
public static final int DEFAULT_RECT_HEIGHT = 10;
public static final int DEFAULT_RECT_DISTANCE = 20;
public static final int DEFAULT_TEXT_SIZE = 10;
public static final int DEFAULT_TEXT_COLOR = 0xffffffff;
public static final int DEFAULT_BACKGROUND_COLOR = 0x9f27CDC0;
public static final int DEFAULT_GRAVITY = LEFT_TOP;
從xml中的 屬性獲取值
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.labelbuilder,defStyleAttr,0);
builder.setLabelHeight(view,ta.getDimensionPixelSize(R.styleable.labelbuilder_labelHeight, builder.dip2Px(DEFAULT_RECT_HEIGHT)));
builder.setLabelDistance(view,ta.getDimensionPixelSize(R.styleable.labelbuilder_labelDistance, builder.dip2Px(DEFAULT_RECT_DISTANCE)));
builder.setTextSize(view,ta.getDimensionPixelSize(R.styleable.labelbuilder_labelTextSize, builder.dip2Px(DEFAULT_TEXT_SIZE)));
builder.setText(view, ta.getString(R.styleable.labelbuilder_labelText));
builder.setTextColor(view, ta.getColor(R.styleable.labelbuilder_labelTextColor, DEFAULT_TEXT_COLOR));
builder.setBackgoundColor(view, ta.getColor(R.styleable.labelbuilder_labelBackground, DEFAULT_BACKGROUND_COLOR));
builder.setOrientation(view, ta.getInt(R.styleable.labelbuilder_labelGravity, DEFAULT_GRAVITY));
builder.setLabelVisiable(view, ta.getBoolean(R.styleable.labelbuilder_labelVisibility, DEAFULT_VISIABLE));
builder.setAlpha(view, ta.getInteger(R.styleable.labelbuilder_labelAlpha,0));
ta.recycle();
最後一定記得recycle(),
為什麼要呼叫recycle()方法呢,大概的解釋是TypeArray 採用的是 池-單例模式,如果不回收,每次View 的 create都要new 一個物件,然後等個GC(Gabage Collection),對記憶體是不小的開銷,具體可以參考這裡。
http://blog.csdn.net/Monicabg/article/details/45014327
到這裡,我們相關的屬性就設定好了,
接下來就是繪製我們的View 了。
由於我們這個只是一個Label的builder,我們要想讓View 應用我們的Builder,就需要提供我們必要的引數。
Canvas:View的畫布,我們View 的繪製都是在View的這個Canvas上製作的。
MeasuredHeight:
MeasuredWidth:這兩個是控制元件的寬度和高度。我們需要這兩個引數來確定我們要畫的位置。
引數傳進來後我們來確定下畫label的方案(以繪製右上角的Label為例):
繪製的位置受到distance(上邊有提到,是角頂點距label的距離),和height(label寬度影響。
繪製的過程就是繪製一個height寬度的Line,並延該方向繪製Label上的文字。
在繪製前還要做的事就是提前初始化好Pain、Path
rectPaint = new Paint();
rectPaint.setAntiAlias(true);
rectPaint.setDither(true);
rectPaint.setStyle(Paint.Style.STROKE);
rectPaint.setStrokeJoin(Paint.Join.ROUND); //set the type one line join another one
rectPaint.setStrokeCap(Paint.Cap.SQUARE); //set the type of line's begin and end
rectPath = new Path();
textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setDither(true);
textPaint.setStrokeCap(Paint.Cap.SQUARE);
textPaint.setStrokeJoin(Paint.Join.ROUND);
textBound = new Rect();
①、確定Line的起始座標和終止座標。
startX = measuredWidth - 1.4142f*(height/2+distance);
startY = 0;
endX = measuredWidth;
endY = 1.4142*(height/2+distance);
②、繪製路徑
rectPaint.setStrokeWidth(height);//in onDraw()
rectPath.reset();//remember to reset path
rectPath.moveTo(startX, startY);
rectPath.lineTo(endX, endY);
canvas.drawPath(rectPath, rectPaint);
③、如果有文字,我們還要在上面繪製文字
</pre><pre name="code" class="java">if(text!=null&&!text.equals("")) {//in onDraw()
textPaint.setTextSize(textSize);
textPaint.setColor(textColor);
textPaint.getTextBounds(text, 0, text.length(), textBound); //get TextBound
float begin_h_offset = (distance+height/2) - textBound.width() / 2;
if (begin_h_offset < 0) { //如果text寬度大於label的長度 容納不下
begin_h_offset = 0;
}
//在路徑上繪製文字
canvas.drawTextOnPath(text, rectPath, begin_h_offset, textBound.height() / 2, textPaint);
}
通過以上步驟基本上就完成了Label的繪製。
但為了起到Builder的作用,使用於所有的View,我們可以在建構函式中這樣寫
public LabelBuilder(Context context,AttributeSet attrs,int defStyleAttr){//get attrs in builder
...
}
public void onDraw(Canvas canvas,int measuredWidth,int measuredHeight){
...
}
完整原始碼可以在github中檢視clone,也可以留言博主會及時發給你,共同學習。
https://github.com/linger1216/labelview
ps: 博主認為根據github中p出的distance的介紹,label的繪製座標計算感覺在原文中有錯誤,所以本文中的座標計算並非原文的座標計算,如果博主的錯誤,歡迎大家指正。