通訊錄粘性頭布局

一、效果圖展示

無(wú)圖不BB仙畦,先上圖

image

二、功能與準(zhǔn)備

2.1 功能

  1. 按照拼音順序?qū)糜堰M(jìn)行排序音婶,英文數(shù)字符號(hào)歸為#
  2. 右側(cè)字母導(dǎo)航條慨畸,既可拖動(dòng)也可點(diǎn)擊
  3. 粘性頭布局
  4. 搜索(全拼+簡(jiǎn)拼)

2.2 準(zhǔn)備

需要導(dǎo)入文字轉(zhuǎn)拼音的庫(kù)
com.belerweb:pinyin4j:2.5.1'

三、開(kāi)工

3.1 右側(cè)字母的索引

  1. 字母的繪畫
   private static final String[] DEFAULT_INDEX_ITEMS = {"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", "#"};

  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        String index;
        //y的位置是baseline線的位置 加上mTopMargin
        //循環(huán)畫上所有的字母
        for (int i = 0; i < mIndexItems.size(); i++) {
            index = mIndexItems.get(i);
            Paint.FontMetrics fm = mPaint.getFontMetrics();
            canvas.drawText(index,
                    (mWidth - mPaint.measureText(index)) / 2,
                    mItemHeight / 2 + (fm.bottom - fm.top) / 2 - fm.bottom + mItemHeight * i + mTopMargin,
                    i == mCurrentIndex ? mTouchedPaint : mPaint);
        }
    }

  1. 字母列表的觸摸效果
 @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                float y = event.getY();
                //得到字母?jìng)€(gè)數(shù)
                int indexSize = mIndexItems.size();
                //計(jì)算按壓的位置
                int touchIndex = (int) (y / mItemHeight);
                //小于0的話那就默認(rèn)第一個(gè)衣式,大于他的個(gè)數(shù)就是最后一個(gè)
                if (touchIndex < 0) {
                    touchIndex = 0;
                } else if (touchIndex >= indexSize) {
                    touchIndex = indexSize - 1;
                }

                if (mOnIndexChangedListener != null && touchIndex >= 0 && touchIndex < indexSize) {

                    if (touchIndex != mCurrentIndex) {
                        mCurrentIndex = touchIndex;
                        if (mCenterTextView!=null){
                            mCenterTextView.setText(mIndexItems.get(touchIndex));
                            mCenterTextView.setVisibility(VISIBLE);
                        }
                        mOnIndexChangedListener.onIndexChanged(mIndexItems.get(touchIndex), touchIndex);
                        invalidate();
                    }

                }

                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:

                if (mCenterTextView!=null){
                    mCenterTextView.setVisibility(VISIBLE);
                }

                mCurrentIndex=-1;

                invalidate();

                break;

        }
        return true;
    }

    //顯示居中的字母View
    public SideIndexBar setCenterTextView(TextView view){
        this.mCenterTextView = view;
        return this;
    }

    public SideIndexBar setOnIndexChangedListener(OnIndexTouchedChangedListener listener) {
        this.mOnIndexChangedListener = listener;
        return this;
    }
    //改變位置的接口
    public interface OnIndexTouchedChangedListener {
        void onIndexChanged(String index, int position);
    }

3.2寸士、通訊錄分組

  1. 首先先判斷是否是同組的第一個(gè)
//判斷該是否是同組的第一個(gè)
    private boolean isFirst(int position) {
        if (mStrings == null) {
            return false;
        }
        if (mStrings.isEmpty()) {
            return false;
        }
        if (position <= 0) {
            return true;
        } else {
            return !mStrings.get(position).getSection().equals(mStrings.get(position - 1).getSection());
        }

    }

  1. 再繪制子Item之間的距離
    //getItemOffsets 可以實(shí)現(xiàn)類似于padding的效果
    //
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
        //mSectionHeight是粘性頭部的高度,是同組的第一個(gè)就設(shè)置top
        if (isFirst(position)) {
            outRect.top = mSectionHeight;
        } else {
            outRect.top = 0;
        }

    }

  1. 繪制每組頭部的背景和文字
    //實(shí)現(xiàn)類似繪制背景的效果碴卧,內(nèi)容在上面
    //繪制背景和文字
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);

        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            int position = params.getViewLayoutPosition();
            //是第一個(gè)就繪制背景以及文字
            if (isFirst(position)) {
                String name = mStrings.get(position).getSection();

                c.drawRect(left, child.getTop() - params.topMargin - mSectionHeight, right, child.getTop() - params.topMargin, mBgPaint);
                Paint.FontMetrics fm = mTextPaint.getFontMetrics();
                c.drawText(name, child.getPaddingLeft(), (child.getTop() - (mSectionHeight / 2 - (fm.descent - fm.ascent) / 2 + fm.descent) - params.topMargin), mTextPaint);
            }
        }

    }

  1. 繪制粘性頭部弱卡,粘性頭部就是滑動(dòng)范圍還在該組時(shí),在最上方顯示該組頭部住册,其實(shí)就是頭部覆蓋在內(nèi)容上婶博。

    //onDrawOver 繪制在內(nèi)容的上面,覆蓋內(nèi)容
    //這個(gè)是實(shí)現(xiàn)粘性頭部的關(guān)鍵
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        //拿到屏幕上顯示的第一個(gè)item的位置
        int pos = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();
        if (pos < 0) return;
        if (mStrings == null || mStrings.isEmpty()) return;
        String section =mStrings.get(pos).getSection();
        View child = parent.findViewHolderForLayoutPosition(pos).itemView;

        boolean flag = false;
        //添加一個(gè)平移替換效果
        if ((pos + 1) < mStrings.size()) {
            if (null != section && !section.equals(mStrings.get(pos + 1).getSection())) {
                //如果子item的高度+加距離頂部的距離 小于 section的高度荧飞,則進(jìn)行平移
                if (child.getHeight() + child.getTop() < mSectionHeight) {
                    c.save();
                    flag = true;
                    c.translate(0, child.getHeight() + child.getTop() - mSectionHeight);
                }
            }
        }
        c.drawRect(parent.getPaddingLeft(),
                parent.getPaddingTop(),
                parent.getRight() - parent.getPaddingRight(),
                parent.getPaddingTop() + mSectionHeight, mBgPaint);
        Paint.FontMetrics fm = mTextPaint.getFontMetrics();
        c.drawText(section,
                child.getPaddingLeft(),
                parent.getPaddingTop() + mSectionHeight -(mSectionHeight / 2 - (fm.descent - fm.ascent) / 2 + fm.descent),
                mTextPaint);
        if (flag)
            c.restore();
    }

3.3 數(shù)據(jù)整理排序

  1. 先看Bean類
public class Star implements Comparable<Star> {

    private String name;
    private String pinyin; //拼音
    private String jianpin;//簡(jiǎn)拼

    /***
     * 獲取懸浮欄文本凡人,(#、定位垢箕、熱門 需要特殊處理)
     * @return
     */
    public String getSection() {
        String s= pinyin;
        if (TextUtils.isEmpty(s)) {
            return "#";
        } else {
            String c = s.substring(0, 1);
            Pattern p = Pattern.compile("[a-zA-Z]");
            Matcher m = p.matcher(c);
            if (m.matches()) {
                return c.toUpperCase();
            } else {
                return "#";
            }

        }
    }
    //排序 #都往后放
    @Override
    public int compareTo(@NonNull Star o) {
        if (getSection().equals("#")&&!o.getSection().equals("#")){
            return 1;
        }else if (!getSection().equals("#")&&o.getSection().equals("#")){
            return -1;
        }else {
            return getSection().compareToIgnoreCase(o.getSection());
        }
    }

//省略get···set方法
}

  1. 簡(jiǎn)拼和全拼的獲取時(shí)通過(guò)
 //獲取全拼
    public String getPinYi(String chines) {

        sb.setLength(0);
        char[] nameChar = chines.toCharArray();
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        for (int i = 0; i < nameChar.length; i++) {
            if (nameChar[i] > 128) {
                try {
                    sb.append(PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0]);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                sb.append(nameChar[I]);
            }
        }
        return sb.toString();
    }

    //獲取簡(jiǎn)拼
    public String getPinYinHeadChar(String chines) {

        sb.setLength(0);
        char[] chars = chines.toCharArray();
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] > 128) {
                try {
                    sb.append(PinyinHelper.toHanyuPinyinStringArray(chars[i], defaultFormat)[0].charAt(0));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                sb.append(chars[I]);
            }
        }
        return sb.toString();
    }

3.4 搜索結(jié)果處理

    @Override
    public void afterTextChanged(Editable s) {
        String keyword = s.toString();
        if (TextUtils.isEmpty(keyword)) {
            mClearAllBtn.setVisibility(View.GONE);
            mEmptyView.setVisibility(View.GONE);
            mResults = mAllCities;
            ((SectionDividerDecoration) (mRecyclerView.getItemDecorationAt(0))).setData(mResults);
            mAdapter.updateData(mResults);
        } else {
            mClearAllBtn.setVisibility(View.VISIBLE);
            //search是匹配結(jié)果拧粪,下面顯示
            mResults = search(keyword, mAllCities);
            ((SectionDividerDecoration) (mRecyclerView.getItemDecorationAt(0))).setData(mResults);
            if (mResults == null || mResults.isEmpty()) {
                mEmptyView.setVisibility(View.VISIBLE);
            } else {
                mEmptyView.setVisibility(View.GONE);
                mAdapter.updateData(mResults);
            }
        }
        mRecyclerView.scrollToPosition(0);
    }

   public List search(String name, List<Star> list) {
        List results = new ArrayList();

        String patten = Pattern.quote(name);
        Pattern pattern = Pattern.compile(patten, Pattern.CASE_INSENSITIVE);
        for (int i = 0; i < list.size(); i++) {
            //根據(jù)拼音
            Matcher matcherPin = pattern.matcher((list.get(i)).getPinyin());
            //根據(jù)簡(jiǎn)拼
            Matcher jianPin = pattern.matcher((list.get(i)).getJianpin());
            //根據(jù)名字
            Matcher matcherName = pattern.matcher((list.get(i)).getName());
            if (matcherPin.find() || matcherName.find() || jianPin.find()) {

                results.add(list.get(i));
            }
        }
        return results;
    }

3.5 列表跟隨索引移動(dòng)

在Fragment中實(shí)現(xiàn)索引的接口
在實(shí)現(xiàn)里寫上

  /**
    * 滾動(dòng)RecyclerView到索引位置
    *
    * @param index
    */
   public void scrollToSection(String index) {
       if (mData == null || mData.isEmpty()) return;
       if (TextUtils.isEmpty(index)) return;
       int size = mData.size();
       for (int i = 0; i < size; i++) {
           if (TextUtils.equals(index.substring(0, 1), mData.get(i).getSection().substring(0, 1))) {
               if (mLayoutManager != null) {
                   mLayoutManager.scrollToPositionWithOffset(i, 0);
                   return;
               }
           }
       }
   }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市胡本,隨后出現(xiàn)的幾起案子殊霞,更是在濱河造成了極大的恐慌,老刑警劉巖帅掘,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件委煤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡修档,警方通過(guò)查閱死者的電腦和手機(jī)碧绞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吱窝,“玉大人讥邻,你說(shuō)我怎么就攤上這事≡合浚” “怎么了兴使?”我有些...
    開(kāi)封第一講書人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)照激。 經(jīng)常有香客問(wèn)我发魄,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任励幼,我火速辦了婚禮汰寓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苹粟。我一直安慰自己有滑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布六水。 她就那樣靜靜地躺著俺孙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掷贾。 梳的紋絲不亂的頭發(fā)上睛榄,一...
    開(kāi)封第一講書人閱讀 52,785評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音想帅,去河邊找鬼场靴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛港准,可吹牛的內(nèi)容都是我干的旨剥。 我是一名探鬼主播,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼浅缸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼轨帜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起衩椒,我...
    開(kāi)封第一講書人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蚌父,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后毛萌,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體苟弛,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年阁将,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了膏秫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡做盅,死狀恐怖缤削,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吹榴,我是刑警寧澤僻他,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站腊尚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏满哪。R本人自食惡果不足惜婿斥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一劝篷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧民宿,春花似錦娇妓、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至志群,卻和暖如春着绷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锌云。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工荠医, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桑涎。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓彬向,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親攻冷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子娃胆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

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