Android 使用ListView的A-Z字母排序功能實現(xiàn)聯(lián)系人模塊

在上一篇文章當(dāng)中,主要學(xué)習(xí)了ListView的A-Z字母排序功能以及根據(jù)輸入框的輸入值改變來過濾搜索結(jié)果,如果輸入框里面的值為空,更新為原來的列表遗遵,否則為過濾數(shù)據(jù)列表,包括漢字轉(zhuǎn)成拼音的功能逸嘀,如果你還沒看過的話上一篇文章的話车要,可以點擊:Android 仿美團網(wǎng),探索ListView的A-Z字母排序功能實現(xiàn)選擇城市。其實對聯(lián)系人和城市列表等實現(xiàn)A-Z的排序的實現(xiàn)原理都差不多崭倘。我只是在上一篇文章的基礎(chǔ)上做了一些修改翼岁。
按照慣例先來看一下最終效果圖:

這里寫圖片描述

現(xiàn)在我們來看下項目結(jié)構(gòu)圖
這里寫圖片描述

其實很多都是和上一篇文章一樣的,我在這里就不多說啦司光,不是很明白的地方琅坡,可以看一下上一篇文章
我還是按照項目中類的順序把代碼貼出來吧
1.ContactSortModel

package com.adan.contactsdome.view;

public class ContactSortModel {

    private String name;//顯示的數(shù)據(jù)
    private String sortLetters;//顯示數(shù)據(jù)拼音的首字母

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSortLetters() {
        return sortLetters;
    }

    public void setSortLetters(String sortLetters) {
        this.sortLetters = sortLetters;
    }
}

2.還是一樣EditTextWithDel類就不貼上代碼了Android 帶清除功能的輸入框控件EditTextWithDel
3.PinyinComparator

package com.adan.contactsdome.view;

import java.util.Comparator;

/**
 * 用來對ListView中的數(shù)據(jù)根據(jù)A-Z進行排序,前面兩個if判斷主要是將不是以漢字開頭的數(shù)據(jù)放在后面
 */
public class PinyinComparator implements Comparator<ContactSortModel> {

    public int compare(ContactSortModel o1, ContactSortModel o2) {
        //這里主要是用來對ListView里面的數(shù)據(jù)根據(jù)ABCDEFG...來排序
        if (o1.getSortLetters().equals("@")
                || o2.getSortLetters().equals("#")) {
            return -1;
        } else if (o1.getSortLetters().equals("#")
                || o2.getSortLetters().equals("@")) {
            return 1;
        } else {
            return o1.getSortLetters().compareTo(o2.getSortLetters());
        }
    }
}

4.PinyinUtils類残家,就是第一點所講的PinYin4j.jar用于將漢字轉(zhuǎn)換為拼音啦榆俺,這里就不粘貼代碼啦,探索PinYin4j.jar將漢字轉(zhuǎn)換為拼音的基本用法
5.SideBar類就是ListView右側(cè)的字母索引View

package com.adan.contactsdome.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

import com.adan.contactsdome.R;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * ListView右側(cè)的字母索引View
 */
public class SideBar extends View {

    public static String[] INDEX_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"};

    private OnTouchingLetterChangedListener onTouchingLetterChangedListener;
    private List<String> letterList;
    private int choose = -1;
    private Paint paint = new Paint();
    private TextView mTextDialog;

    public SideBar(Context context) {
        this(context, null);
    }

    public SideBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SideBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        setBackgroundColor(Color.parseColor("#FFFFFF"));
        letterList = Arrays.asList(INDEX_STRING);
    }

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int height = getHeight();// 獲取對應(yīng)高度
        int width = getWidth();// 獲取對應(yīng)寬度
        int singleHeight = height / letterList.size();// 獲取每一個字母的高度
        for (int i = 0; i < letterList.size(); i++) {
            paint.setColor(Color.parseColor("#606060"));
            paint.setTypeface(Typeface.DEFAULT_BOLD);
            paint.setAntiAlias(true);
            paint.setTextSize(20);
            // 選中的狀態(tài)
            if (i == choose) {
                paint.setColor(Color.parseColor("#4F41FD"));
                paint.setFakeBoldText(true);
            }
            // x坐標(biāo)等于中間-字符串寬度的一半.
            float xPos = width / 2 - paint.measureText(letterList.get(i)) / 2;
            float yPos = singleHeight * i + singleHeight / 2;
            canvas.drawText(letterList.get(i), xPos, yPos, paint);
            paint.reset();// 重置畫筆
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        final int action = event.getAction();
        final float y = event.getY();// 點擊y坐標(biāo)
        final int oldChoose = choose;
        final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
        final int c = (int) (y / getHeight() * letterList.size());// 點擊y坐標(biāo)所占總高度的比例*b數(shù)組的長度就等于點擊b中的個數(shù).

        switch (action) {
            case MotionEvent.ACTION_UP:
                setBackgroundColor(Color.parseColor("#FFFFFF"));
                choose = -1;
                invalidate();
                if (mTextDialog != null) {
                    mTextDialog.setVisibility(View.GONE);
                }
                break;
            default:
                setBackgroundResource(R.drawable.bg_sidebar);
                if (oldChoose != c) {
                    if (c >= 0 && c < letterList.size()) {
                        if (listener != null) {
                            listener.onTouchingLetterChanged(letterList.get(c));
                        }
                        if (mTextDialog != null) {
                            mTextDialog.setText(letterList.get(c));
                            mTextDialog.setVisibility(View.VISIBLE);
                        }
                        choose = c;
                        invalidate();
                    }
                }
                break;
        }
        return true;
    }

    public void setIndexText(ArrayList<String> indexStrings) {
        this.letterList = indexStrings;
        invalidate();
    }

    /**
     * 為SideBar設(shè)置顯示當(dāng)前按下的字母的TextView
     *
     * @param mTextDialog
     */
    public void setTextView(TextView mTextDialog) {
        this.mTextDialog = mTextDialog;
    }

    /**
     * 向外公開的方法
     *
     * @param onTouchingLetterChangedListener
     */
    public void setOnTouchingLetterChangedListener(
            OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
        this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
    }

    /**
     * 接口
     */
    public interface OnTouchingLetterChangedListener {
        void onTouchingLetterChanged(String s);
    }
}

6.SortAdapter數(shù)據(jù)的適配器類坞淮,這里我們需要用到的就是SectionIndexer接口茴晋,它能夠有效地幫助我們對分組進行控制。使用SectionIndexer接口需要實現(xiàn)三個方法:getSectionForPosition(int position)回窘,getPositionForSection(int section)晃跺,getSections(),我們只需要自行實現(xiàn)前面兩個方法:
(一)getSectionForPosition(int position)是根據(jù)ListView的position來找出當(dāng)前位置所在的分組
(二)getPositionForSection(int section)就是根據(jù)首字母的Char值來獲取在該ListView中第一次出現(xiàn)該首字母的位置,也就是當(dāng)前分組所在的位置

package com.adan.contactsdome;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.SectionIndexer;
import android.widget.TextView;

import com.adan.contactsdome.view.ContactSortModel;

import java.util.List;

public class SortAdapter extends BaseAdapter implements SectionIndexer {
    private List<ContactSortModel> list = null;
    private Context mContext;

    public SortAdapter(Context mContext, List<ContactSortModel> list) {
        this.mContext = mContext;
        this.list = list;
    }

    /**
     * 當(dāng)ListView數(shù)據(jù)發(fā)生變化時,調(diào)用此方法來更新ListView
     *
     * @param list
     */
    public void updateListView(List<ContactSortModel> list) {
        this.list = list;
        notifyDataSetChanged();
    }

    public int getCount() {
        return this.list.size();
    }

    public Object getItem(int position) {
        return list.get(position);
    }

    public long getItemId(int position) {
        return position;
    }

    public View getView(final int position, View view, ViewGroup arg2) {
        ViewHolder viewHolder = null;
        final ContactSortModel mContent = list.get(position);
        if (view == null) {
            viewHolder = new ViewHolder();
            view = LayoutInflater.from(mContext).inflate(R.layout.item_contact, null);
            viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_city_name);
            view.setTag(viewHolder);
            viewHolder.tvLetter = (TextView) view.findViewById(R.id.tv_catagory);
        } else {
            viewHolder = (ViewHolder) view.getTag();
        }

        int section = getSectionForPosition(position);

        if (position == getPositionForSection(section)) {
            viewHolder.tvLetter.setVisibility(View.VISIBLE);
            viewHolder.tvLetter.setText(mContent.getSortLetters());
        } else {
            viewHolder.tvLetter.setVisibility(View.GONE);
        }

        viewHolder.tvTitle.setText(this.list.get(position).getName());

        return view;

    }


    final static class ViewHolder {
        TextView tvLetter;
        TextView tvTitle;
    }

    public int getSectionForPosition(int position) {
        return list.get(position).getSortLetters().charAt(0);
    }

    public int getPositionForSection(int section) {
        for (int i = 0; i < getCount(); i++) {
            String sortStr = list.get(i).getSortLetters();
            char firstChar = sortStr.toUpperCase().charAt(0);
            if (firstChar == section) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public Object[] getSections() {
        return null;
    }
}

7.MainActivity 對EditTextWithDel設(shè)置addTextChangedListener監(jiān)聽毫玖,當(dāng)輸入框內(nèi)容發(fā)生變化根據(jù)里面的值過濾ListView,里面的值為空顯示原來的列表和給ListView添加表頭等

package com.adan.contactsdome;

import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.adan.contactsdome.view.ContactSortModel;
import com.adan.contactsdome.view.EditTextWithDel;
import com.adan.contactsdome.view.PinyinComparator;
import com.adan.contactsdome.view.PinyinUtils;
import com.adan.contactsdome.view.SideBar;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MainActivity extends Activity {
    private ListView sortListView;
    private SideBar sideBar;
    private TextView dialog, mTvTitle;
    private SortAdapter adapter;
    private EditTextWithDel mEtSearchName;
    private List<ContactSortModel> SourceDateList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
    }

    private void initViews() {
        mEtSearchName = (EditTextWithDel) findViewById(R.id.et_search);
        sideBar = (SideBar) findViewById(R.id.sidrbar);
        dialog = (TextView) findViewById(R.id.dialog);
        mTvTitle = (TextView) findViewById(R.id.tv_title);
        sortListView = (ListView) findViewById(R.id.lv_contact);
        initDatas();
        initEvents();
        setAdapter();
    }

    private void setAdapter() {
        SourceDateList = filledData(getResources().getStringArray(R.array.contacts));
        Collections.sort(SourceDateList, new PinyinComparator());
        adapter = new SortAdapter(this, SourceDateList);
        sortListView.setAdapter(adapter);
    }

    private void initEvents() {
        //設(shè)置右側(cè)觸摸監(jiān)聽
        sideBar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() {
            @Override
            public void onTouchingLetterChanged(String s) {
                //該字母首次出現(xiàn)的位置
                int position = adapter.getPositionForSection(s.charAt(0));
                if (position != -1) {
                    sortListView.setSelection(position + 1);
                }
            }
        });

        //ListView的點擊事件
        sortListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                                    int position, long id) {
                mTvTitle.setText(((ContactSortModel) adapter.getItem(position - 1)).getName());
                Toast.makeText(getApplication(), ((ContactSortModel) adapter.getItem(position)).getName(), Toast.LENGTH_SHORT).show();
            }
        });

        //根據(jù)輸入框輸入值的改變來過濾搜索
        mEtSearchName.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                //當(dāng)輸入框里面的值為空凌盯,更新為原來的列表付枫,否則為過濾數(shù)據(jù)列表
                filterData(s.toString());
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
    }

    private void initDatas() {
        sideBar.setTextView(dialog);
    }

    /**
     * 根據(jù)輸入框中的值來過濾數(shù)據(jù)并更新ListView
     *
     * @param filterStr
     */
    private void filterData(String filterStr) {
        List<ContactSortModel> mSortList = new ArrayList<>();
        if (TextUtils.isEmpty(filterStr)) {
            mSortList = SourceDateList;
        } else {
            mSortList.clear();
            for (ContactSortModel sortModel : SourceDateList) {
                String name = sortModel.getName();
                if (name.toUpperCase().indexOf(filterStr.toString().toUpperCase()) != -1 || PinyinUtils.getPingYin(name).toUpperCase().startsWith(filterStr.toString().toUpperCase())) {
                    mSortList.add(sortModel);
                }
            }
        }
        // 根據(jù)a-z進行排序
        Collections.sort(mSortList, new PinyinComparator());
        adapter.updateListView(mSortList);
    }

    private List<ContactSortModel> filledData(String[] date) {
        List<ContactSortModel> mSortList = new ArrayList<>();
        ArrayList<String> indexString = new ArrayList<>();

        for (int i = 0; i < date.length; i++) {
            ContactSortModel sortModel = new ContactSortModel();
            sortModel.setName(date[i]);
            String pinyin = PinyinUtils.getPingYin(date[i]);
            String sortString = pinyin.substring(0, 1).toUpperCase();
            if (sortString.matches("[A-Z]")) {
                sortModel.setSortLetters(sortString.toUpperCase());
                if (!indexString.contains(sortString)) {
                    indexString.add(sortString);
                }
            }
            mSortList.add(sortModel);
        }
        Collections.sort(indexString);
        sideBar.setIndexText(indexString);
        return mSortList;
    }
}

布局文件就不貼出來了,現(xiàn)在看來是不是比上一篇還簡單了呢
源代碼請戳這里:Android 使用ListView的A-Z字母排序功能實現(xiàn)聯(lián)系人模塊

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末驰怎,一起剝皮案震驚了整個濱河市阐滩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌县忌,老刑警劉巖掂榔,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件继效,死亡現(xiàn)場離奇詭異,居然都是意外死亡装获,警方通過查閱死者的電腦和手機瑞信,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穴豫,“玉大人凡简,你說我怎么就攤上這事【啵” “怎么了秤涩?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長司抱。 經(jīng)常有香客問我筐眷,道長,這世上最難降的妖魔是什么习柠? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任匀谣,我火速辦了婚禮,結(jié)果婚禮上津畸,老公的妹妹穿的比我還像新娘振定。我一直安慰自己,他們只是感情好肉拓,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布后频。 她就那樣靜靜地躺著,像睡著了一般暖途。 火紅的嫁衣襯著肌膚如雪卑惜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天驻售,我揣著相機與錄音露久,去河邊找鬼。 笑死欺栗,一個胖子當(dāng)著我的面吹牛毫痕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播迟几,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼消请,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了类腮?” 一聲冷哼從身側(cè)響起臊泰,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚜枢,沒想到半個月后缸逃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體针饥,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年需频,在試婚紗的時候發(fā)現(xiàn)自己被綠了丁眼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡贺辰,死狀恐怖户盯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饲化,我是刑警寧澤莽鸭,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站吃靠,受9級特大地震影響硫眨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜巢块,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一礁阁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧族奢,春花似錦姥闭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至廊敌,卻和暖如春铜跑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背骡澈。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工锅纺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肋殴。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓囤锉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親护锤。 傳聞我的和親對象是個殘疾皇子嚼锄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

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