自定義View入門--字母索引列表

  1. 說明
    上次我們帶大家一起寫了 《自定義View入門--評分控件RatingBar http://www.itdecent.cn/p/2a4d707e1889》,如果沒有看的可以去看下,上節(jié)課的內(nèi)容主要是交互控件,什么是交互控件呢? 就是我們用戶去觸摸該控件,也就是需要我們處理onTouch()事件。我們在項目開發(fā)過程中,肯定會或多或少都會遇到一些城市列表搜索、地區(qū)列表聯(lián)動效果,那么這節(jié)課我們繼續(xù)來寫示例——自定義View入門--字母索引列表。

  2. 效果


    圖片.png
  3. 思路分析
    但凡是這種交互控件,步驟只有兩步:
    1>:實現(xiàn)默認效果——即就是剛打開app后進來的效果
    用畫筆繪制 "A"——自定義View
    復(fù)寫onMeasure()方法:目的就是指定寬高
    2>:處理交互手指上面的觸摸效果
    效果是:手指觸摸高亮顯示當(dāng)前位置

  4. 代碼中分析如下:


    字母索引列表分析.png
  5. 代碼如下

/**
 * 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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容