自定義簡(jiǎn)單字母索引欄

說到字母索引欄纳猫,在聯(lián)系人模塊經(jīng)常會(huì)見到球及。iOS中這種控件是自帶的芋忿,安卓中是沒有的,所以只能自定義解決了绒北。

需求分析

  1. 拖動(dòng)索引欄時(shí)。選中狀態(tài)
    • 字母欄背景加深
    • 文字顏色變白色
    • 懸浮字母提示顯示
  2. 松手時(shí)察署,非選中狀態(tài)
    • 字母欄背景透明
    • 文字顏色變黑色
    • 懸浮字母提示隱藏

需要自定義的屬性

  1. 選中時(shí)字體顏色
  2. 未選中時(shí)字體顏色
  3. 選中時(shí)闷游,索引欄背景顏色
  4. 非選中時(shí),索引欄背景顏色

提供的回調(diào)

選中和未選中時(shí)贴汪,回調(diào)位置和字母文字脐往。

索引條定義步驟(注釋上已經(jīng)寫得很清楚,就不再一步步贅述了)

  • 自定義需要的屬性
<declare-styleable name="SlideBar">
    <!-- 選中時(shí)扳埂,文字顏色 -->
    <attr name="slb_select_txt_color" format="color|reference" />
    <!-- 非選中時(shí)业簿,文字顏色 -->
    <attr name="slb_un_select_txt_color" format="color|reference" />
    <!-- 選中時(shí),滑動(dòng)條背景顏色 -->
    <attr name="slb_select_bg_color" format="color|reference" />
    <!-- 非選中時(shí)阳懂,滑動(dòng)條背景顏色 -->
    <attr name="slb_un_select_bg_color" format="color|reference" />
</declare-styleable>
  • 自定義View梅尤,繼承View,獲取設(shè)置的自定義屬性岩调,配置畫筆等
public class SlideBar extends View {
    /**
     * 默認(rèn)選中時(shí)巷燥,文字顏色
     */
    private final int mDefaultSelectTextColor = Color.parseColor("#FFFFFF");
    /**
     * 默認(rèn)未選中時(shí),文字顏色
     */
    private final int mDefaultUnSelectTextColor = Color.parseColor("#202020");
    /**
     * 默認(rèn)選中時(shí)号枕,滑動(dòng)條背景顏色
     */
    private final int mDefaultSelectBgColor = Color.parseColor("#66202020");
    /**
     * 默認(rèn)未選中時(shí)缰揪,滑動(dòng)條背景顏色
     */
    private final int mDefaultUnSelectBgColor = Color.parseColor("#00000000");
    
     /**
     * 字母表
     */
    private String[] mLetter = new String[]{"#", "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"};
    
    /**
     * 是否拖動(dòng)索引條中
     */
    private boolean mTouched = false;
    
     /**
     * 畫筆
     */
    private Paint mPaint;

    private int mSelectTextColor;
    private int mUnSelectTextColor;
    private int mSelectBgColor;
    private int mUnSelectBgColor;
    
    public SlideBar(Context context) {
        super(context);
        init(context, null);
    }

    public SlideBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public SlideBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, @Nullable AttributeSet attrs) {
        initAttrs(context, attrs);
        initPaint();
    }

    private void initAttrs(Context context, @Nullable AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlideBar);
        //選中時(shí)文字的顏色
        mSelectTextColor = array.getColor(R.styleable.SlideBar_slb_select_txt_color, mDefaultSelectTextColor);
        //未選中時(shí)文字的顏色
        mUnSelectTextColor = array.getColor(R.styleable.SlideBar_slb_un_select_txt_color, mDefaultUnSelectTextColor);
        //選中時(shí),滑動(dòng)條背景顏色
        mSelectBgColor = array.getColor(R.styleable.SlideBar_slb_select_bg_color, mDefaultSelectBgColor);
        //未選中時(shí)葱淳,滑動(dòng)條背景顏色
        mUnSelectBgColor = array.getColor(R.styleable.SlideBar_slb_un_select_bg_color, mDefaultUnSelectBgColor);
        array.recycle();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setTextSize(sp2px(getContext(), 11f));
        if (mTouched) {
            mPaint.setColor(mSelectTextColor);
        } else {
            mPaint.setColor(mUnSelectTextColor);
        }
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mTextRect = new Rect();
    }
}
  • 定義回調(diào)接口
public interface OnSelectItemListener {
    /**
     * 選擇時(shí)回調(diào)
     *
     * @param position     選中的位置
     * @param selectLetter 選中的字母
     */
    void onItemSelect(int position, String selectLetter);

    /**
     * 松手取消選中時(shí)回調(diào)
     */
    void onItemUnSelect();
}

public void setOnSelectItemListener(OnSelectItemListener listener) {
    mListener = listener;
}
  • 配置測(cè)量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    setMeasuredDimension(measureWidth(widthMeasureSpec), heightMeasureSpec);
}

/**
 * 測(cè)量本身的大小邀跃,這里只是測(cè)量寬度
 *
 * @param widthMeaSpec 傳入父View的測(cè)量標(biāo)準(zhǔn)
 * @return 測(cè)量的寬度
 */
private int measureWidth(int widthMeaSpec) {
    /*定義view的寬度*/
    int width;
    /*獲取當(dāng)前 View的測(cè)量模式*/
    int mode = MeasureSpec.getMode(widthMeaSpec);
    /*
     * 獲取當(dāng)前View的測(cè)量值,這里得到的只是初步的值蛙紫,
     * 我們還需根據(jù)測(cè)量模式來確定我們期望的大小
     * */
    int size = MeasureSpec.getSize(widthMeaSpec);
    /*
     * 如果,模式為精確模式
     * 當(dāng)前View的寬度途戒,就是我們的size
     * */
    if (mode == MeasureSpec.EXACTLY) {
        width = size;
    } else {
        /*否則的話我們就需要結(jié)合padding的值來確定*/
        int desire = size + getPaddingLeft() + getPaddingRight();
        if (mode == MeasureSpec.AT_MOST) {
            width = Math.min(desire, size);
        } else {
            width = desire;
        }
    }
    return width;
}
  • 獲取到寬高時(shí)坑傅,計(jì)算每個(gè)字母的高度
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = w;
    //每行的高度,計(jì)算平均分每個(gè)字母占的高度
    mCellHeight = h / mLetter.length;
}
  • 繪制文字和背景喷斋,觸摸和松手時(shí)改變變量來達(dá)到繪制不同的顏色
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //觸摸時(shí)改變背景顏色
    if (mTouched) {
        canvas.drawColor(mSelectBgColor);
        mPaint.setColor(mSelectTextColor);
    } else {
        canvas.drawColor(mUnSelectBgColor);
        mPaint.setColor(mUnSelectTextColor);
    }
    for (int i = 0; i < mLetter.length; i++) {
        String text = mLetter[i];
        //測(cè)量文字寬高
        mPaint.getTextBounds(text, 0, text.length(), mTextRect);
        int textWidth = mTextRect.width();
        int textHeight = mTextRect.height();

        //文字一半的寬度
        float textHalfWidth = textWidth / 2.0f;
        //字母文字的起點(diǎn)X坐標(biāo)唁毒,控件的寬度的一半再減去文字的一半
        float x = (mWidth / 2.0f) - textHalfWidth;
        //起點(diǎn)文字的Y坐標(biāo)
        float y = (mCellHeight / 2.0f + textHeight / 2.0f + mCellHeight * i);
        //畫文字
        canvas.drawText(mLetter[i], x, y, mPaint);
    }
}
  • 處理觸摸和松手判斷,并進(jìn)行回調(diào)
@Override
public boolean onTouchEvent(MotionEvent event) {
    float y = event.getY();
    //計(jì)算當(dāng)前觸摸的字母的位置
    int index = (int) (y / mCellHeight);
    //觸摸
    if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
        mTouched = true;
        if (index >= 0 && index < mLetter.length) {
            if (mListener != null) {
                mListener.onItemSelect(index, mLetter[index]);
            }
        } else {
            return super.onTouchEvent(event);
        }
    } else {
        //松手
        mTouched = false;
        if (mListener != null) {
            mListener.onItemUnSelect();
        }
    }
    //觸摸改變時(shí)星爪,不斷通知重繪來繪制索引條
    invalidate();
    return true;
}

界面布局

  • RecyclerView列表
  • 索引欄
  • 字母提示(我們直接用個(gè)TextView浆西,設(shè)置背景,顯示隱藏即可)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/refresh_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <me.zh.indexslidebar.SlideBar
        android:id="@+id/slide_bar"
        android:layout_width="24dp"
        android:layout_height="match_parent"
        android:layout_gravity="end"
        android:visibility="visible"
        app:slb_select_bg_color="@android:color/darker_gray"
        app:slb_select_txt_color="@android:color/white"
        app:slb_un_select_bg_color="@android:color/transparent"
        app:slb_un_select_txt_color="@color/colorPrimary" />

    <TextView
        android:id="@+id/check_letter"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:background="@drawable/bg_contact_letter_flow"
        android:gravity="center"
        android:textColor="#FFFFFF"
        android:textSize="40sp"
        android:visibility="gone"
        tools:text="A"
        tools:visibility="visible" />
</FrameLayout>

具體使用

  • 提供一組聯(lián)系人名字
/**
 * 聯(lián)系人數(shù)組
 */
public static String[] mNames = new String[]{"宋江", "盧俊義", "吳用",
        "公孫勝", "關(guān)勝", "林沖", "秦明", "呼延灼", "花榮", "柴進(jìn)", "李應(yīng)", "朱仝", "魯智深",
        "武松", "董平", "張清", "楊志", "徐寧", "索超", "戴宗", "劉唐", "李逵", "史進(jìn)", "穆弘",
        "雷橫", "李俊", "阮小二", "張橫", "阮小五", "張順", "阮小七", "楊雄", "石秀", "解珍",
        "解寶", "燕青", "朱武", "黃信", "孫立", "宣贊", "郝思文", "韓滔", "彭玘", "單廷珪",
        "魏定國(guó)", "蕭讓", "裴宣", "歐鵬", "鄧飛", "燕順", "楊林", "凌振", "蔣敬", "呂方",
        "郭 盛", "安道全", "皇甫端", "王英", "扈三娘", "鮑旭", "樊瑞", "孔明", "孔亮", "項(xiàng)充",
        "李袞", "金大堅(jiān)", "馬麟", "童威", "童猛", "孟康", "侯健", "陳達(dá)", "楊春", "鄭天壽",
        "陶宗旺", "宋清", "樂和", "龔?fù)?, "丁得孫", "穆春", "曹正", "宋萬", "杜遷", "薛永", "施恩",
        "周通", "李忠", "杜興", "湯隆", "鄒淵", "鄒潤(rùn)", "朱富", "朱貴", "蔡福", "蔡慶", "李立",
        "李云", "焦挺", "石勇", "孫新", "顧大嫂", "張青", "孫二娘", "王定六", "郁保四", "白勝",
        "時(shí)遷", "段景柱", "&張三", "11級(jí)李四", "12級(jí)小明"};
}
  1. 字母和字母條的位置映射
/**
 * 用于保存聯(lián)系人首字母在列表的位置
 */
private HashMap<String, Integer> mLetterPositionMap = new HashMap<>();
  1. 配置好3個(gè)View(Rv使用自己熟悉的框架即可顽腾,這里使用的是MultiType)
public class MainActivity extends AppCompatActivity {
    private RecyclerView vRefreshList;
    /**
     * 字母?jìng)?cè)滑欄
     */
    private SlideBar mSlideBar;
    /**
     * 提示View
     */
    private TextView vCheckLetterView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findView();
        bindView();
        setData();
    }
    
    private void findView() {
        mSlideBar = findViewById(R.id.slide_bar);
        vRefreshList = findViewById(R.id.refresh_list);
        vCheckLetterView = findViewById(R.id.check_letter);
    }

    private void bindView() {
        //配置列表
        vRefreshList.setLayoutManager(new LinearLayoutManager(this));
        mListItems = new Items();
        mListAdapter = new MultiTypeAdapter(mListItems);
        //聯(lián)系人字母條目
        mListAdapter.register(ContactLetterModel.class, new ContactLetterViewBinder());
        //聯(lián)系人條目
        mListAdapter.register(ContactModel.class, new ContactViewBinder());
        vRefreshList.setAdapter(mListAdapter);
    }
  1. 配置索引條回調(diào)近零,在選中時(shí)诺核,將列表滾動(dòng)到記錄的位置,有些字母在我們的字母表里面可能沒有對(duì)應(yīng)的久信,會(huì)找不到窖杀,所以需要判空
//配置索引條
mSlideBar.setOnSelectItemListener(new SlideBar.OnSelectItemListener() {
    @Override
    public void onItemSelect(int position, String selectLetter) {
        if (vCheckLetterView.getVisibility() != View.VISIBLE) {
            vCheckLetterView.setVisibility(View.VISIBLE);
        }
        vCheckLetterView.setText(selectLetter);
        Integer letterStickyPosition = mLetterPositionMap.get(selectLetter);
        //這里可能拿不到,因?yàn)椴⒉皇撬械淖帜嘎?lián)系人名字上都有
        if (letterStickyPosition != null) {
            vRefreshList.scrollToPosition(letterStickyPosition);
        }
    }

    @Override
    public void onItemUnSelect() {
        vCheckLetterView.setVisibility(View.GONE);
    }
});
  1. 根據(jù)姓名表裙士,構(gòu)造聯(lián)系人列表和記錄字母條位置條目入客,這里需要將姓名的第一個(gè)子的首字母,用到了一個(gè)拼音庫腿椎。
implementation 'com.github.promeg:tinypinyin:2.0.3'

先來將姓名表按姓名的第一個(gè)字的英文字母來排序桌硫,這樣是為了后面遍歷判斷字母,對(duì)同一組字母的條目的第一個(gè)條目插入一個(gè)字母條目啃炸。

List<String> nameList = Arrays.asList(mNames);
Collections.sort(nameList, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        char letter = Character.toUpperCase(Pinyin.toPinyin(o1.charAt(0)).charAt(0));
        char nextLetter = Character.toUpperCase(Pinyin.toPinyin(o2.charAt(0)).charAt(0));
        if (letter == nextLetter) {
            return 0;
        } else {
            return letter - nextLetter;
        }
    }
});
  1. 遍歷姓名列表铆隘,構(gòu)造條目模型。怎么讓多個(gè)同英文字母的姓名分組前插入一個(gè)字母條目呢肮帐?很簡(jiǎn)單咖驮,每次遍歷時(shí)判斷是不是和上一個(gè)字母一致,不一致才添加一個(gè)训枢。記錄字母條目的位置托修,其實(shí)就是添加字母條目時(shí),獲取自己在列表數(shù)據(jù)集的位置恒界,因?yàn)槊看味继砑拥轿膊磕廊校灾苯尤×斜頂?shù)據(jù)集的最后一位的位置即可。
//字母
char letter = 0;
for (int i = 0; i < nameList.size(); i++) {
    if (i == 0) {
        //獲取文字第一個(gè)字母
        String letterPinyin = Pinyin.toPinyin(nameList.get(i).charAt(0));
        letter = Character.toUpperCase(letterPinyin.charAt(0));
        mListItems.add(new ContactLetterModel(String.valueOf(letter)));
    } else {
        //如果下一個(gè)條目的首字母和上一個(gè)的不一樣十酣,則插入一條新的字母條目
        String letterPinyin = Pinyin.toPinyin(nameList.get(i).charAt(0));
        char nextLetter = Character.toUpperCase(letterPinyin.charAt(0));
        if (nextLetter != letter) {
            letter = nextLetter;
            mListItems.add(new ContactLetterModel(String.valueOf(letter)));
            //記錄字母條目的位置涩拙,后續(xù)拉動(dòng)字母選擇條時(shí)跳轉(zhuǎn)位置
            mLetterPositionMap.put(String.valueOf(letter).toUpperCase(), mListItems.size() - 1);
        }
    }
    //添加聯(lián)系人條目
    mListItems.add(new ContactModel(nameList.get(i)));
}
mListAdapter.notifyDataSetChanged();

完整代碼

public class SlideBar extends View {
    /**
     * 默認(rèn)選中時(shí),文字顏色
     */
    private final int mDefaultSelectTextColor = Color.parseColor("#FFFFFF");
    /**
     * 默認(rèn)未選中時(shí)耸采,文字顏色
     */
    private final int mDefaultUnSelectTextColor = Color.parseColor("#202020");
    /**
     * 默認(rèn)選中時(shí)兴泥,滑動(dòng)條背景顏色
     */
    private final int mDefaultSelectBgColor = Color.parseColor("#66202020");
    /**
     * 默認(rèn)未選中時(shí),滑動(dòng)條背景顏色
     */
    private final int mDefaultUnSelectBgColor = Color.parseColor("#00000000");
    /**
     * 索引條寬度
     */
    private int mWidth;
    /**
     * 字母表
     */
    private String[] mLetter = new String[]{"#", "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"};
    /**
     * 文字區(qū)域虾宇,保存為成員變量是為了復(fù)用
     */
    private Rect mTextRect;
    /**
     * 每個(gè)字母的高度
     */
    private int mCellHeight;
    /**
     * 是否拖動(dòng)索引條中
     */
    private boolean mTouched = false;
    /**
     * 選擇回調(diào)
     */
    private OnSelectItemListener mListener;
    /**
     * 畫筆
     */
    private Paint mPaint;

    private int mSelectTextColor;
    private int mUnSelectTextColor;
    private int mSelectBgColor;
    private int mUnSelectBgColor;

    public SlideBar(Context context) {
        super(context);
        init(context, null);
    }

    public SlideBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public SlideBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, @Nullable AttributeSet attrs) {
        initAttrs(context, attrs);
        initPaint();
    }

    private void initAttrs(Context context, @Nullable AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlideBar);
        //選中時(shí)文字的顏色
        mSelectTextColor = array.getColor(R.styleable.SlideBar_slb_select_txt_color, mDefaultSelectTextColor);
        //未選中時(shí)文字的顏色
        mUnSelectTextColor = array.getColor(R.styleable.SlideBar_slb_un_select_txt_color, mDefaultUnSelectTextColor);
        //選中時(shí)搓彻,滑動(dòng)條背景顏色
        mSelectBgColor = array.getColor(R.styleable.SlideBar_slb_select_bg_color, mDefaultSelectBgColor);
        //未選中時(shí),滑動(dòng)條背景顏色
        mUnSelectBgColor = array.getColor(R.styleable.SlideBar_slb_un_select_bg_color, mDefaultUnSelectBgColor);
        array.recycle();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setTextSize(sp2px(getContext(), 11f));
        if (mTouched) {
            mPaint.setColor(mSelectTextColor);
        } else {
            mPaint.setColor(mUnSelectTextColor);
        }
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
        mTextRect = new Rect();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        //每行的高度嘱朽,計(jì)算平均分每個(gè)字母占的高度
        mCellHeight = h / mLetter.length;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureWidth(widthMeasureSpec), heightMeasureSpec);
    }

    /**
     * 測(cè)量本身的大小旭贬,這里只是測(cè)量寬度
     *
     * @param widthMeaSpec 傳入父View的測(cè)量標(biāo)準(zhǔn)
     * @return 測(cè)量的寬度
     */
    private int measureWidth(int widthMeaSpec) {
        /*定義view的寬度*/
        int width;
        /*獲取當(dāng)前 View的測(cè)量模式*/
        int mode = MeasureSpec.getMode(widthMeaSpec);
        /*
         * 獲取當(dāng)前View的測(cè)量值,這里得到的只是初步的值搪泳,
         * 我們還需根據(jù)測(cè)量模式來確定我們期望的大小
         * */
        int size = MeasureSpec.getSize(widthMeaSpec);
        /*
         * 如果稀轨,模式為精確模式
         * 當(dāng)前View的寬度,就是我們的size
         * */
        if (mode == MeasureSpec.EXACTLY) {
            width = size;
        } else {
            /*否則的話我們就需要結(jié)合padding的值來確定*/
            int desire = size + getPaddingLeft() + getPaddingRight();
            if (mode == MeasureSpec.AT_MOST) {
                width = Math.min(desire, size);
            } else {
                width = desire;
            }
        }
        return width;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //觸摸時(shí)改變背景顏色
        if (mTouched) {
            canvas.drawColor(mSelectBgColor);
            mPaint.setColor(mSelectTextColor);
        } else {
            canvas.drawColor(mUnSelectBgColor);
            mPaint.setColor(mUnSelectTextColor);
        }
        for (int i = 0; i < mLetter.length; i++) {
            String text = mLetter[i];
            //測(cè)量文字寬高
            mPaint.getTextBounds(text, 0, text.length(), mTextRect);
            int textWidth = mTextRect.width();
            int textHeight = mTextRect.height();

            //文字一半的寬度
            float textHalfWidth = textWidth / 2.0f;
            //字母文字的起點(diǎn)X坐標(biāo)岸军,控件的寬度的一半再減去文字的一半
            float x = (mWidth / 2.0f) - textHalfWidth;
            //起點(diǎn)文字的Y坐標(biāo)
            float y = (mCellHeight / 2.0f + textHeight / 2.0f + mCellHeight * i);
            //畫文字
            canvas.drawText(mLetter[i], x, y, mPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float y = event.getY();
        //計(jì)算當(dāng)前觸摸的字母的位置
        int index = (int) (y / mCellHeight);
        //觸摸
        if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
            mTouched = true;
            if (index >= 0 && index < mLetter.length) {
                if (mListener != null) {
                    mListener.onItemSelect(index, mLetter[index]);
                }
            } else {
                return super.onTouchEvent(event);
            }
        } else {
            //松手
            mTouched = false;
            if (mListener != null) {
                mListener.onItemUnSelect();
            }
        }
        //觸摸改變時(shí)奋刽,不斷通知重繪來繪制索引條
        invalidate();
        return true;
    }

    public interface OnSelectItemListener {
        /**
         * 選擇時(shí)回調(diào)
         *
         * @param position     選中的位置
         * @param selectLetter 選中的字母
         */
        void onItemSelect(int position, String selectLetter);

        /**
         * 松手取消選中時(shí)回調(diào)
         */
        void onItemUnSelect();
    }

    public void setOnSelectItemListener(OnSelectItemListener listener) {
        mListener = listener;
    }

    public static int dip2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    public static int px2dp(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    private int sp2px(Context context, float spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                spVal, context.getResources().getDisplayMetrics());
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瓦侮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子杨名,更是在濱河造成了極大的恐慌脏榆,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件台谍,死亡現(xiàn)場(chǎng)離奇詭異须喂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)趁蕊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門坞生,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人掷伙,你說我怎么就攤上這事是己。” “怎么了任柜?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵卒废,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我宙地,道長(zhǎng)摔认,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任宅粥,我火速辦了婚禮参袱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘秽梅。我一直安慰自己抹蚀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布企垦。 她就那樣靜靜地躺著环壤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钞诡。 梳的紋絲不亂的頭發(fā)上郑现,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音臭增,去河邊找鬼。 笑死竹习,一個(gè)胖子當(dāng)著我的面吹牛誊抛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播整陌,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼拗窃,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼瞎领!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起随夸,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤九默,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后宾毒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驼修,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年诈铛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乙各。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡幢竹,死狀恐怖耳峦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情焕毫,我是刑警寧澤蹲坷,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站邑飒,受9級(jí)特大地震影響循签,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜幸乒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一懦底、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧罕扎,春花似錦聚唐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至臀蛛,卻和暖如春亲桦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浊仆。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工客峭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抡柿。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓舔琅,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親洲劣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子备蚓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 聽音樂一直用的是網(wǎng)易云音樂课蔬,原來我就吐槽為什么列表沒有字母索引,導(dǎo)致找一首歌很麻煩郊尝。不過最近滑動(dòng)列表時(shí)二跋,發(fā)現(xiàn)右邊有...
    m1Ku閱讀 981評(píng)論 3 17
  • 效果圖: 自定義view的流程,具體請(qǐng)點(diǎn)此查看:自定義view套路我們先重寫構(gòu)造器流昏,然后重寫onMeasure函數(shù)...
    曾大穩(wěn)丶閱讀 277評(píng)論 0 1
  • 寫在開頭 這是自定義View的第三篇文章扎即,第一篇是Android drawPath實(shí)現(xiàn)QQ拖拽泡泡,主要實(shí)現(xiàn)的是題...
    lovejjfg閱讀 4,002評(píng)論 1 35
  • 快速索引在應(yīng)用中很常見,在聯(lián)系人横缔,微信铺遂,省市列表,應(yīng)用管理茎刚,文件管理等應(yīng)用場(chǎng)景都可以看到快速索引的身影襟锐,本篇博客將...
    JackChen1024閱讀 397評(píng)論 0 0
  • 效果圖: 字母索引這個(gè)功能還是很常見的,例如:聯(lián)系人膛锭,城市選擇等一些功能都會(huì)用到粮坞,其實(shí)這個(gè)功能還是很簡(jiǎn)單的,現(xiàn)在我...
    波波維奇c閱讀 3,658評(píng)論 0 0