說明
上次我們帶大家一起寫了 《自定義View入門--評分控件RatingBar http://www.itdecent.cn/p/2a4d707e1889》,如果沒有看的可以去看下,上節(jié)課的內(nèi)容主要是交互控件,什么是交互控件呢? 就是我們用戶去觸摸該控件,也就是需要我們處理onTouch()事件。我們在項目開發(fā)過程中,肯定會或多或少都會遇到一些城市列表搜索、地區(qū)列表聯(lián)動效果,那么這節(jié)課我們繼續(xù)來寫示例——自定義View入門--字母索引列表。-
效果
圖片.png 思路分析
但凡是這種交互控件,步驟只有兩步:
1>:實現(xiàn)默認效果——即就是剛打開app后進來的效果
用畫筆繪制 "A"——自定義View
復(fù)寫onMeasure()方法:目的就是指定寬高
2>:處理交互手指上面的觸摸效果
效果是:手指觸摸高亮顯示當(dāng)前位置-
代碼中分析如下:
字母索引列表分析.png 代碼如下
/**
* Email 2185134304@qq.com
* Created by JackChen on 2018/1/30.
* Version 1.0
* Description:
*/
public class LetterSideBar extends View {
// 定義26個字母
public static String[] mLetters = {"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 mPaint;
//當(dāng)前觸摸的位置字母
private String mCurrentTouchLetter ;
public LetterSideBar(Context context) {
this(context, null);
}
public LetterSideBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LetterSideBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);
//自定義屬性,顏色、字體大小 這里是寫死的,沒有寫自定義屬性
mPaint.setTextSize(sp2px(16));// 設(shè)置的是像素
//默認顏色
mPaint.setColor(Color.BLUE);
}
// sp 轉(zhuǎn) px
private float sp2px(int sp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
sp, getResources().getDisplayMetrics());
}
/**
* 測量控件的寬高
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//寬度 = 左右的padding + 字母寬度(取決于你的畫筆) 詳情見圖即可
//獲取字母寬度
int textWidth = (int) mPaint.measureText("A");
//寬度 [計算指定寬度]
int width = getPaddingLeft() + getPaddingRight() + textWidth ;
//高度 [可以直接獲取]
int height = MeasureSpec.getSize(heightMeasureSpec) ;
//算出寬高后測量寬高
setMeasuredDimension(width , height);
}
/**
* 畫26個字母
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
// int x = getPaddingLeft() ;
//每一個字母的高度 總共高度/字母總個數(shù)
int itemHeight = (getHeight() - getPaddingTop() - getPaddingBottom()) / mLetters.length ;
for (int i = 0; i < mLetters.length; i++) {
//知道每個字母中心的位置
//第一個字母中心位置是:字母高度的一半,
// 第二個字母中心位置是:字母高度一半[itemHeight/2] + 前邊字母的高度[i*itemHeight]
int letterCenterY = itemHeight/2 + i*itemHeight + getPaddingTop() ;
//基線,基于中心位置 [基線求法為套路,記住即可]
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics() ;
int dy = (int) ((fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);
int baseLine = letterCenterY + dy ;
//獲取字母寬度
int textWidth = (int) mPaint.measureText(mLetters[i]);
int x = getWidth()/2 - textWidth/2 ;
//如果當(dāng)前字母高亮,將畫筆設(shè)置為空色 ;否則設(shè)置為藍色
if (mLetters[i].equals(mCurrentTouchLetter)){
mPaint.setColor(Color.RED);
canvas.drawText(mLetters[i], x, baseLine, mPaint);
}else{
mPaint.setColor(Color.BLUE);
canvas.drawText(mLetters[i] , x , baseLine , mPaint);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
//ACTION_DOWN和ACTION_MOVE效果相同
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
//計算出當(dāng)前的字母,獲取當(dāng)前手指觸摸的位置,然后重新繪制
float currentMoveY = event.getY();
//當(dāng)前位置 = currentMoveY/字母高度 ,
int itemHeight = (getHeight() - getPaddingTop() - getPaddingBottom()) / mLetters.length;
int currentPosition = (int) (currentMoveY/itemHeight);
if (currentPosition < 0){
currentPosition = 0 ;
}
if (currentPosition > mLetters.length- 1){
currentPosition = mLetters.length - 1 ;
}
mCurrentTouchLetter = mLetters[currentPosition] ;
if (mListener != null){
mListener.touch(mCurrentTouchLetter , true);
}
//重新繪制
invalidate();
break;
case MotionEvent.ACTION_UP:
if (mListener != null){
mListener.touch(mCurrentTouchLetter , false);
}
break;
}
return true;
}
private LetterTouchListener mListener ;
public interface LetterTouchListener{
void touch(CharSequence letter , boolean isTouch) ;
}
public void setonLetterTouchListener(LetterTouchListener listener){
this.mListener = listener ;
}
}
public class MainActivity extends AppCompatActivity implements LetterSideBar.LetterTouchListener{
private TextView letter_tv;
private LetterSideBar letter_side_bar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
letter_tv = (TextView) findViewById(R.id.letter_tv);
letter_side_bar = (LetterSideBar) findViewById(R.id.letter_side_bar);
letter_side_bar.setonLetterTouchListener(this);
}
@Override
public void touch(CharSequence letter , boolean isTouch) {
if (isTouch){ //isTouch為true表示移動手指,讓中間的文字顯示,并且設(shè)置文字
letter_tv.setVisibility(View.VISIBLE);
letter_tv.setText(letter);
}else{ //isTouch為false表示手指抬起,讓中間的文字隱藏
letter_tv.setVisibility(View.GONE);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.view.demo7.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/letter_tv"
android:text="A"
android:textSize="22sp"
android:textColor="#FF0000"
android:layout_centerInParent="true"
/>
<com.view.demo7.LetterSideBar
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_alignParentRight="true"
android:id="@+id/letter_side_bar"
/>
</RelativeLayout>
具體代碼已上傳至github,里邊已含相關(guān)注釋:
https://github.com/shuai999/View_day07

