不論天氣如何變化,該擼碼還是要擼的.近期打算好好學習一下自定義View的知識.
這是一個自定義的索引條目,雖然網上很多教程,但是紙上來的終覺淺,還是自己寫一下.體會深刻,也會學到更多知識.
廢話不說,代碼以下
public class SpeedIndexView extends View {
private String[] letters = {"A", "B", "C", "D",
"E", "F", "G", "H",
"I", "J", "K", "L",
"M", "N", "O", "P",
"Q", "R", "S", "T",
"U", "V", "W", "X",
"Y", "Z"};
private Paint paint;//畫筆
private Rect rect;
private int width;
private float letterLength;
private int lastIndex = -1;
private TouchIndexListener onIndexListener;
public SpeedIndexView(Context context) {
this(context, null);
}
public SpeedIndexView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SpeedIndexView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//抗鋸齒
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
//字體
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setTextSize(DimensUtils.dip2px(context, 16));
rect = new Rect();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public SpeedIndexView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//儘量不要做創建對象的操作,防止內存頻繁回收,造成界面卡頓
for (int i = 0; i < letters.length; i++) {
paint.getTextBounds(letters[i], 0, letters[i].length(), rect);
float measureText = paint.measureText(letters[i]);//獲取字符串的寬度
float x = width / 2.0f - measureText / 2.0f;
float y = letterLength + letterLength * i;
canvas.drawText(letters[i], x, y, paint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//View的Size變化時被調用
//在onDraw中無法獲得準確的寬高
// width = getMeasuredWidth();
// height = getMeasuredHeight();
width = w;
Log.d("view", "w" + w + "h" + h + "oldw" + oldw + "oldh" + oldh);
//每個字符串的高度
letterLength = h * 1.0f / letters.length;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
measuerAndWhitch(event);
break;
case MotionEvent.ACTION_MOVE:
measuerAndWhitch(event);
break;
case MotionEvent.ACTION_UP:
lastIndex = -1;
break;
}
return true;
}
private void measuerAndWhitch(MotionEvent event) {
float y = event.getY();
int index = (int) (y / letterLength);
Log.d("view", "" + index);
//與上次觸摸不同
if (lastIndex != index) {
if (index >= 0 && index < letters.length) {
if (onIndexListener != null)
onIndexListener.onTouchindexListener(letters[index]);
}
lastIndex = index;
}
}
public void setOnTouchIndexListener(TouchIndexListener indexListener) {
this.onIndexListener = indexListener;
}
public interface TouchIndexListener {
void onTouchindexListener(String letter);
}
}
1, 繼承View,重寫3個構造方法,第四個構造方法需要api21以上可以使用,具體每個構造方法先不深究.
2, 初始畫筆,設置畫筆
3,在onDraw中,使用canvas.drawText方法,傳入內容,x,y座標,和畫筆.需要注意的是這個x和y是字體左下角的座標,並不是中心點
4,要計算出每個字母的位置,可以使用畫筆去測量這個字符串的寬度.
5,在onDraw中儘量不要創建過多的對象,因爲onDraw過程中創建過多的對象,會造成界面的卡頓.
6,重寫onTouchEvent方法,返回true,攔截事件,處理點擊事件和滑動以及擡起事件.
7,回調接口,將選中的字符串傳遞出去,以便調用時對選中的字符串進行其他處理,比如ListView選項.
Tips:該自定義View可以熟悉自定義View的流程,畫筆,畫布的使用等.還可以經過修改使其動態顯示右側列表選項,但是要注意在數據返回設置給View之前,不能讓View進行顯示.否則會產生Null.