天天看點

Android仿微信添加聯系人清單,内附有截圖和demo源碼分享一個Android仿微信選擇聯系人頁面

最新demo位址,仿微信添加聯系人WXAddPersonDemo

分享一個Android仿微信選擇聯系人頁面

之前做的App主要是工具類的,而且公司的産品經理也喜歡在App裡設計很多自定義控件,是以比一般市場上的App要麻煩一些,今天先介紹一個仿微信選擇聯系人的頁面,涉及的功能較多

頁面實作了哪些功能

  1. 将聯系人名字首字母進行a-z排序,并且每一個字母都有一個頭部,沒有擷取首字母的則放入#分類中,并且在a前面加入一個常用聯系人展示
  2. 在清單右邊有一列從 常、a-z、#可以快速索引
  3. 搜尋功能,這裡是搜尋本地存儲的聯系人資料,而不是通過網絡接口搜尋
  4. 聯系人清單可選中,并且反選可取消選中,選中的聯系人頭像顯示搜尋框中,點選框中頭像取消選中
  5. 本地搜尋支援對标題(聯系人名字)和内容,漢字(或數字),首字母,全拼音,簡拼音搜尋
  6. 搜尋清單和聯系人清單界面類似,搜尋清單對對搜尋内容高亮顯示,選中也加入頭像到搜素框顯示
  7. 搜尋框動态處理了頭部的寬度,當達到一定寬度時可滑動,實作效果在本文最後

有不懂的地方可以在下方評論區評論

實作效果如下

Android仿微信添加聯系人清單,内附有截圖和demo源碼分享一個Android仿微信選擇聯系人頁面
Android仿微信添加聯系人清單,内附有截圖和demo源碼分享一個Android仿微信選擇聯系人頁面

實作原理

1、快速索引欄,通過畫步和畫筆實作,裡面、常、a-z、#是可以固定在裡面也可以在擷取背景資料排好序之後動态顯示,例如排序之後沒有常用聯系人,可不顯示常。通過重寫view的dispatchTouchEvent方法控制觸摸事件,并回調給頁面顯示。

2、首先我們需要将從背景(我這裡聯系人是從背景擷取的,也可取本地)獲得的聯系人name漢字轉換為拼音,并且取首字母進行分類,這步是為了展示清單a-z。

3、這步是關鍵的一步,将第2步擷取到的list,将list中所有的拼音進行初始化,這是耗時操作,考慮在子線程中運作,使用線程或intentService處理均可行,将name和content的,漢字首字母和所有漢字的首字母和所有漢字拼音和拼音總長度都處理出來,加入到新的集合,此集合中就包括剛剛我們所取得的四個屬性,這裡處理的作用是為了之後按漢字,首字母,全拼搜尋時會用到。

4、将第2部取到的list,a-z排序規則,利用Collections.sort(sourceDateList, pinyinComparator);進行排序,在adapter中根據首字母ascii值取第一次出現首字母的位置排序清單,右側快速導航欄,滑動時根據ASCII值判斷首字母第一次出現的位置設定listview的位置。

5、當有條目選中時,搜尋框頭像使用recyclerView展示,我這裡控制頭像的寬度為螢幕寬度的1/8,讓後根據recyclerView的條目數動态控制recyclerView的寬度,當條目數大于等于6時,則recyclerView寬度達到最大,可滑動。

6、監聽搜尋框,有資料輸入時,一個新清單或無資料頁面将覆寫之前聯系人清單。搜尋的内容傳入CNPinyinIndexFactory中,也在子線程中運作将搜尋的結果給頁面,CNPinyinIndexFactory中根據關鍵字根據先比對中文,再比對首字母,再比對全拼音的順序進行比對,沒有就是無資料頁面。

7、由于這裡多加對content的比對,是以比對先比對name,再比對content,比對完成之後傳回兩個字段的下标用于搜尋清單高亮顯示。這裡的做法較為複雜,尤其是比對全拼音的時候,是以這裡建議用安卓資料庫來實作,orlmite做本地搜尋不錯,可支援模糊,時間區段,或者自己寫sql語句進行查詢。

下面是代碼塊

首頁(布局)、聯系人清單、搜尋清單,頭部清單

package com.pcjz.lems.ui.workrealname.manageequipment;

import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.Html;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.pcjz.lems.R;
import com.pcjz.lems.business.base.BaseActivity;
import com.pcjz.lems.business.base.basebean.BaseData;
import com.pcjz.lems.business.base.basebean.BaseListData;
import com.pcjz.lems.business.common.utils.StringUtils;
import com.pcjz.lems.business.common.utils.TLog;
import com.pcjz.lems.business.common.view.SideBar;
import com.pcjz.lems.business.common.view.personlist.CharacterParser;
import com.pcjz.lems.business.common.view.personlist.PinyinComparator;
import com.pcjz.lems.business.config.AppConfig;
import com.pcjz.lems.business.constant.ResultStatus;
import com.pcjz.lems.business.constant.SysCode;
import com.pcjz.lems.business.context.AppContext;
import com.pcjz.lems.business.entity.PersonInfoEntity;
import com.pcjz.lems.business.storage.SharedPreferencesManager;
import com.pcjz.lems.business.webapi.MainApi;
import com.pcjz.lems.business.widget.contractlist.CNPinyin;
import com.pcjz.lems.business.widget.contractlist.CNPinyinFactory;
import com.pcjz.lems.business.widget.contractlist.CNPinyinIndex;
import com.pcjz.lems.business.widget.contractlist.CNPinyinIndexFactory;
import com.pcjz.lems.ui.workrealname.manageequipment.adapter.ContactsSortAdapter;
import com.pcjz.lems.ui.workrealname.manageequipment.adapter.SearchContractsAdapter;
import com.pcjz.lems.ui.workrealname.manageequipment.adapter.SearchHeaderAdapter;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.entity.StringEntity;
import org.json.JSONObject;

import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * created by yezhengyu on 2017/9/19 11:21
 */

public class ChangePersonListActivity extends BaseActivity implements View.OnClickListener {

    private SideBar sideBar;
    private TextView dialog;
    ListView mListView;
    private ContactsSortAdapter contactsSortadapter;
    public static final int MSG_INITS_RESULTS = 0x0000;
    public static final int MSG_SEARCH_RESULTS = 0x0001;

    /**
     * 漢字轉換成拼音的類
     */
    private CharacterParser characterParser;
    private List<PersonInfoEntity> sourceDateList;
    /**
     * 根據拼音來排列ListView裡面的資料類
     */
    private PinyinComparator pinyinComparator;
    private EditText mEtSearch;
    private List<CNPinyinIndex<PersonInfoEntity>> filterDataList = new ArrayList<>();
    private ListView mSearchListView;
    private TextView mTvNoData;
    private SearchContractsAdapter searchContractsAdapter;
    private ArrayList<CNPinyin<PersonInfoEntity>> mCnPinyinArrayList;
    private LinearLayout mLlSearchResults;
    private String edittext;
    private List<PersonInfoEntity> mSearchHeaderList = new ArrayList<>();
    private RecyclerView mRecyclerView;
    private ImageView mIvSearchIcon;
    private RelativeLayout rlNoData;
    private ImageView ivNoData;
    private TextView tvNoData;
    private SearchHeaderAdapter searchHeaderAdapter;
    private int width;
    private int height;
    private String mLargeDeviceId;

    private String mUserId;
    private String mProjectId;
    private ArrayList<PersonInfoEntity> mPersonDatas;
    private LinearLayout mLayout;
    private RelativeLayout mRlBack;
    private TextView mTvTitleBarRight;

    @Override
    public void setView() {
        setContentView(R.layout.activity_change_person);
        setTitle(AppContext.mResource.getString(R.string.modify_person_list));
        DisplayMetrics dm = getResources().getDisplayMetrics();
        width = dm.widthPixels;
        height = dm.heightPixels;
    }

    @Override
    protected void setBack() {
        mRlBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                closeKeyboard();
                finish();
            }
        });
    }

    @Override
    protected void initView() {
        mRlBack = (RelativeLayout) findViewById(R.id.rl_back);
        mTvTitleBarRight = (TextView) findViewById(R.id.tv_titlebar_right);
        mTvTitleBarRight.setText(R.string.modify_person_finish);
        RelativeLayout rlFinish = (RelativeLayout) findViewById(R.id.rl_my_download);
        rlFinish.setOnClickListener(this);
        mLargeDeviceId = getIntent().getExtras().getString("largeDeviceId");
        mPersonDatas = (ArrayList<PersonInfoEntity>) getIntent().getSerializableExtra("personDatas");
        sideBar = (SideBar) findViewById(R.id.sidrbar);
        dialog = (TextView) findViewById(R.id.dialog);
        sideBar.setTextView(dialog);
        mUserId = SharedPreferencesManager.getString(ResultStatus.USER_ID);
        mProjectId = SharedPreferencesManager.getString(mUserId + SysCode.PROJECTPERIODID);
        mListView = (ListView) findViewById(R.id.lv_contacts);
        //搜尋圖示
        mIvSearchIcon = (ImageView) findViewById(R.id.iv_search_icon);
        //搜尋recyclerView
        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview_header);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        mRecyclerView.setLayoutManager(layoutManager);
        //搜尋editText
        mEtSearch = (EditText) findViewById(R.id.et_search_person);
        //搜尋記錄布局
        mLlSearchResults = (LinearLayout) findViewById(R.id.ll_search_results);
        //搜尋記錄
        mSearchListView = (ListView) findViewById(R.id.lv_search_contacts);

        //無資料頁面
        mLayout = (LinearLayout) findViewById(R.id.ll_content);
        mLayout.setVisibility(View.GONE);
        //沒有搜尋記錄時
        mTvNoData = (TextView) findViewById(R.id.tv_no_search_data);
        rlNoData = (RelativeLayout) findViewById(R.id.rl_no_data);
        ivNoData = (ImageView) findViewById(R.id.iv_no_data);
        tvNoData = (TextView) findViewById(R.id.tv_no_data);
        initNoDataView();

        //執行個體化漢字轉拼音類
        characterParser = CharacterParser.getInstance();
        pinyinComparator = new PinyinComparator();

        //搜尋記錄listView
        searchContractsAdapter = new SearchContractsAdapter(this, filterDataList);
        mSearchListView.setAdapter(searchContractsAdapter);
        //搜尋頭部添加recyclerView
        searchHeaderAdapter = new SearchHeaderAdapter(this, mSearchHeaderList, width);
        mRecyclerView.setAdapter(searchHeaderAdapter);

        requestContractData();
    }

    private void requestContractData() {
        initLoading("");
        JSONObject obj = new JSONObject();
        HttpEntity entity = null;
        try {
            JSONObject obj0 = new JSONObject();
            obj.put("projectId", mProjectId);
            obj.put("largeDeviceId", mLargeDeviceId);
            obj0.put("params", obj);
            entity = new StringEntity(obj0.toString(), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        MainApi.requestCommon(this, AppConfig.GET_LARGE_EQUIPMENT_BIND_WORK_MECHINE_PERSONS, entity, new AsyncHttpResponseHandler() {
            @Override
            public void onSuccess(int i, Header[] headers, byte[] bytes) {
                hideLoading();
                try {
                    String httpResult = new String(bytes);
                    TLog.log("getChangePersonList : " + httpResult);
                    Type type = new TypeToken<BaseListData<PersonInfoEntity>>() {
                    }.getType();
                    BaseListData<PersonInfoEntity> datas = new Gson().fromJson(httpResult, type);
                    if (StringUtils.equals(datas.getCode(), ResultStatus.SUCCESS)) {
                        List<PersonInfoEntity> personList = datas.getData();
                        if (personList != null && personList.size() != 0) {
                            if (mTvTitleBarRight.getVisibility() != View.VISIBLE) {
                                mTvTitleBarRight.setVisibility(View.VISIBLE);
                            }
                            if (mLayout != null && mLayout.getVisibility() != View.VISIBLE) {
                                mLayout.setVisibility(View.VISIBLE);
                            }
                            if (rlNoData != null && rlNoData.getVisibility() == View.VISIBLE) {
                                rlNoData.setVisibility(View.GONE);
                            }
                            sourceDateList = filledData(personList);
                            //集合中所有拼音的初始化,考慮在子線程中運作
                            CNPinyinFactory.createCNPinyinList(sourceDateList, MSG_INITS_RESULTS, mHandler);
                            // 根據a-z進行排序源資料
                            Collections.sort(sourceDateList, pinyinComparator);
                            mPersonDatas.remove(mPersonDatas.size()-1);
                            //清單listView
                            contactsSortadapter = new ContactsSortAdapter(ChangePersonListActivity.this, sourceDateList, mPersonDatas);
                            mListView.setAdapter(contactsSortadapter);
                            initHeadList();
                        } else {
                            initNoData();
                        }
                        return;
                    } else {
                        initNoData();
                        AppContext.showToast(datas.getMsg());
                        return;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                initNoData();
            }

            @Override
            public void onFailure(int i, Header[] headers, byte[] bytes, Throwable throwable) {
                hideLoading();
                initNoInternetView();
            }
        });
    }

    //有網無資料
    private void initNoDataView() {
        if (ivNoData == null) return;
        rlNoData.setBackground(ContextCompat.getDrawable(this, R.color.white));
        ivNoData.setImageResource(R.drawable.pop_window_choose_no_content);
        tvNoData.setText(AppContext.mResource.getString(R.string.person_list_no_data));
    }

    //網絡異常
    private void initNoInternetView() {
        if (ivNoData == null) return;
        ivNoData.setImageResource(R.drawable.no_internet_icon);
        tvNoData.setText(AppContext.mResource.getString(R.string.please_check_network));
        if (mTvTitleBarRight.getVisibility() == View.VISIBLE) {
            mTvTitleBarRight.setVisibility(View.GONE);
        }
    }

    private void initNoData() {
        if (rlNoData == null) return;
        if (rlNoData != null && rlNoData.getVisibility() != View.VISIBLE) {
            rlNoData.setVisibility(View.VISIBLE);
        }
        if (mLayout != null && mLayout.getVisibility() == View.VISIBLE) {
            mLayout.setVisibility(View.GONE);
        }
        if (mTvTitleBarRight.getVisibility() == View.VISIBLE) {
            mTvTitleBarRight.setVisibility(View.GONE);
        }
    }

    @Override
    public void setListener() {
        // 設定右側[A-Z]快速導航欄觸摸監聽
        sideBar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() {

            @Override
            public void onTouchingLetterChanged(String s) {
                if (contactsSortadapter != null && mListView != null) {
                    //by yezhengyu
                    /*if (StringUtils.equals(s, "#")) {
                        mListView.setSelection(sourceDateList.size() - 1);
                        return;
                    }*/

                    // 該字母首次出現的位置
                    int position = contactsSortadapter.getPositionForSection(s.charAt(0));
                    if (position != -1) {
                        mListView.setSelection(position);
                    }
                }
            }
        });
        // item事件
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long arg3) {
                ContactsSortAdapter.ViewHolder viewHolder = (ContactsSortAdapter.ViewHolder) view.getTag();
                viewHolder.cbChecked.performClick();
                contactsSortadapter.toggleChecked(position);
                initHeadList();
            }
        });

        mEtSearch.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) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                if (s.length() > 0) {
                    edittext = s.toString();
                    mLlSearchResults.setVisibility(View.VISIBLE);
                    //搜尋考慮在子線程中運作
                    CNPinyinIndexFactory.indexList(mCnPinyinArrayList, s.toString(), MSG_SEARCH_RESULTS, mHandler);
                } else {
                    mLlSearchResults.setVisibility(View.GONE);
                }
            }
        });

        mSearchListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                SearchContractsAdapter.ViewHolder viewHolder = (SearchContractsAdapter.ViewHolder) view.getTag();
                viewHolder.cbChecked.performClick();
                searchContractsAdapter.toggleChecked(position);
                List<PersonInfoEntity> searchSelectedList = searchContractsAdapter.getSelectedList();
                contactsSortadapter.updateListView(searchSelectedList);
                mLlSearchResults.setVisibility(View.GONE);
                mEtSearch.setText("");
                initHeadList();
            }
        });

        //頭像點選删除
        searchHeaderAdapter.setOnItemClickListener(new SearchHeaderAdapter.OnClickLisenerI() {
            @Override
            public void setOnClickListener(View view, int position) {
                mSearchHeaderList.remove(position);
                contactsSortadapter.updateListView(mSearchHeaderList);
                initHeadList();
                if (mLlSearchResults != null && mLlSearchResults.getVisibility() == View.VISIBLE) {
                    if (mSearchListView != null && mSearchListView.getVisibility() == View.VISIBLE) {
                        searchContractsAdapter.updateListView2(mSearchHeaderList);
                    }
                }
            }
        });
    }

    private void initHeadList() {
        List<PersonInfoEntity> contactsSelectedList = contactsSortadapter.getSelectedList();
        if (contactsSelectedList.size() > 0) {
            mIvSearchIcon.setVisibility(View.GONE);
            mRecyclerView.setVisibility(View.VISIBLE);
            mSearchHeaderList.clear();
            mSearchHeaderList.addAll(contactsSelectedList);
            searchHeaderAdapter.notifyDataSetChanged();
            int itemCount = searchHeaderAdapter.getItemCount();
            ViewGroup.LayoutParams layoutParams = mRecyclerView.getLayoutParams();
            if (itemCount >= 6) {
                layoutParams.width = 3 * width / 4;
            } else {
                layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT;
            }
            mRecyclerView.setLayoutParams(layoutParams);
            mRecyclerView.scrollToPosition(itemCount - 1);
        } else {
            mIvSearchIcon.setVisibility(View.VISIBLE);
            mRecyclerView.setVisibility(View.GONE);
        }
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            int what = msg.what;
            switch (what) {
                case MSG_INITS_RESULTS:
                    mCnPinyinArrayList = (ArrayList<CNPinyin<PersonInfoEntity>>) msg.obj;
                    break;
                case MSG_SEARCH_RESULTS:
                    ArrayList<CNPinyinIndex<PersonInfoEntity>> cnPinyinIndexArrayList = (ArrayList<CNPinyinIndex<PersonInfoEntity>>) msg.obj;
                    if (cnPinyinIndexArrayList != null && cnPinyinIndexArrayList.size() != 0) {
                        if (mSearchListView != null && mSearchListView.getVisibility() == View.GONE) {
                            mSearchListView.setVisibility(View.VISIBLE);
                        }
                        if (mTvNoData != null && mTvNoData.getVisibility() == View.VISIBLE) {
                            mTvNoData.setVisibility(View.GONE);
                        }

                        List<PersonInfoEntity> contactSelectedList = contactsSortadapter.getSelectedList();
                        searchContractsAdapter.updateListView(cnPinyinIndexArrayList, contactSelectedList);
                    } else {
                        if (mSearchListView != null && mSearchListView.getVisibility() == View.VISIBLE) {
                            mSearchListView.setVisibility(View.GONE);
                        }
                        if (mTvNoData != null && mTvNoData.getVisibility() == View.GONE) {
                            mTvNoData.setVisibility(View.VISIBLE);
                        }
                        mTvNoData.setText(Html.fromHtml("沒有找到“" + "<font color='#FD9426' size='20'>" + edittext + "</font>" + "”相關的結果"));
                    }
                    break;
            }
        }
    };


    /**
     * 為ListView填充資料
     * @return
     */
    private List<PersonInfoEntity> filledData(List<PersonInfoEntity> personList) {
        List<PersonInfoEntity> mSortList = new ArrayList<PersonInfoEntity>();
        for (int i = 0; i < personList.size(); i++) {
            PersonInfoEntity sortModel = new PersonInfoEntity();
            PersonInfoEntity personInfo = personList.get(i);
            sortModel.setEmpName(personInfo.getEmpName());
            sortModel.setJobName(personInfo.getJobName());
            sortModel.setFacephoto(personInfo.getFacephoto());
            sortModel.setPersonId(personInfo.getPersonId());
            sortModel.setProjectPersonId(personInfo.getProjectPersonId());
            sortModel.setBackGroundColor(personInfo.getBackGroundColor());
            // 漢字轉換成拼音
            String pinyin = characterParser.getSelling(personList.get(i).getEmpName());
            String sortString = pinyin.substring(0, 1).toUpperCase();

            // 正規表達式,判斷首字母是否是英文字母
            if (sortString.matches("[A-Z]")) {
                sortModel.setSortLetters(sortString.toUpperCase());
            } else {
                sortModel.setSortLetters("#");
            }
            mSortList.add(sortModel);
        }
        return mSortList;
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id) {
            case R.id.rl_my_download:
                List<PersonInfoEntity> contactsSelectedList = contactsSortadapter.getSelectedList();
                Intent intent = new Intent();
                intent.putExtra("personDatas", (Serializable)contactsSelectedList);
                this.setResult(1, intent);
                closeKeyboard();
                finish();
                break;
        }
    }

    @Override
    protected boolean hasLoading() {
        return true;
    }

    private void closeKeyboard() {
        View view = getWindow().peekDecorView();
        if (view != null) {
            InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
    }
}

           
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:orientation="vertical">

        <include layout="@layout/layout_titlebar_application" />

        <LinearLayout
            android:id="@+id/ll_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:orientation="horizontal">

                <ImageView
                    android:id="@+id/iv_search_icon"
                    android:layout_width="30dp"
                    android:layout_height="match_parent"
                    android:paddingBottom="20dp"
                    android:paddingLeft="10dp"
                    android:paddingTop="20dp"
                    android:src="@drawable/tz_search_icon" />

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/recyclerview_header"
                    android:layout_width="wrap_content"
                    android:layout_height="60dp"
                    android:layout_centerVertical="true"
                    android:scrollbars="horizontal"
                    android:visibility="gone" />

                <EditText
                    android:id="@+id/et_search_person"
                    android:layout_width="match_parent"
                    android:layout_height="60dp"
                    android:background="@null"
                    android:hint="搜尋"
                    android:paddingLeft="7dp"
                    android:textColorHint="@color/txt_gray_shallow"
                    android:textSize="15sp" />
            </LinearLayout>

            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:background="@color/txt_gray_divider" />

            <FrameLayout
                android:layout_width="fill_parent"
                android:layout_height="fill_parent">

                <ListView
                    android:id="@+id/lv_contacts"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:divider="@null"
                    android:scrollbars="none"
                    android:visibility="visible" />

                <TextView
                    android:id="@+id/dialog"
                    android:layout_width="80dp"
                    android:layout_height="80dp"
                    android:layout_gravity="center"
                    android:background="@drawable/shape_person_text_dialog"
                    android:gravity="center"
                    android:text=""
                    android:textColor="@color/white"
                    android:textSize="50dp"
                    android:visibility="invisible" />

                <com.pcjz.lems.business.common.view.SideBar
                    android:id="@+id/sidrbar"
                    android:layout_width="30dp"
                    android:layout_height="match_parent"
                    android:layout_gravity="right|center"
                    android:layout_marginBottom="20dp"
                    android:layout_marginTop="20dp" />

                <LinearLayout
                    android:id="@+id/ll_search_results"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:background="@color/white"
                    android:orientation="vertical"
                    android:visibility="gone">

                    <ListView
                        android:id="@+id/lv_search_contacts"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:divider="@null"
                        android:scrollbars="none" />

                    <TextView
                        android:id="@+id/tv_no_search_data"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="32dp"
                        android:gravity="center"
                        android:text=""
                        android:textColor="@color/txt_black"
                        android:textSize="14sp"
                        android:visibility="gone" />
                </LinearLayout>
            </FrameLayout>
        </LinearLayout>
        <include layout="@layout/layout_no_data" />
    </LinearLayout>

    <include layout="@layout/layout_loading" />
</RelativeLayout>
           
package com.pcjz.lems.ui.workrealname.manageequipment.adapter;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.SectionIndexer;
import android.widget.TextView;

import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.pcjz.lems.R;
import com.pcjz.lems.business.common.utils.StringUtils;
import com.pcjz.lems.business.common.view.CircleImageView;
import com.pcjz.lems.business.config.AppConfig;
import com.pcjz.lems.business.entity.PersonInfoEntity;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;


public class ContactsSortAdapter extends BaseAdapter implements SectionIndexer {
    private List<PersonInfoEntity> mList;
    private List<PersonInfoEntity> mSelectedList;
    private Context mContext;
    LayoutInflater mInflater;

    private DisplayImageOptions mOption = new DisplayImageOptions.Builder()
            .showImageOnLoading(R.drawable.load_pho_default_image)
            .showImageForEmptyUri(R.drawable.load_pho_default_image)
            .showImageOnFail(R.drawable.load_pho_default_image)
            .cacheInMemory(true)
            .cacheOnDisc(false).bitmapConfig(Bitmap.Config.RGB_565)
            .imageScaleType(ImageScaleType.EXACTLY).build();

    public ContactsSortAdapter(Context mContext, List<PersonInfoEntity> list, List<PersonInfoEntity> personDatas) {
        this.mContext = mContext;
        mSelectedList = new ArrayList<>();
        mSelectedList.addAll(personDatas);
        if (list == null) {
            this.mList = new ArrayList<>();
        } else {
            this.mList = list;
        }
    }

    /**
     * 當ListView資料發生變化時,調用此方法來更新ListView
     */
    public void updateListView(/*List<PersonInfoEntity> list*/List<PersonInfoEntity> selectedList) {
        /*if (list == null) {
			this.mList = new ArrayList<>();
		} else {
			this.mList = list;
		}*/
        mSelectedList.clear();
        mSelectedList.addAll(selectedList);
        notifyDataSetChanged();
    }

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

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

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

    public View getView(final int position, View view, ViewGroup arg2) {
        ViewHolder viewHolder = null;
        final PersonInfoEntity mContent = mList.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_title_name);
            viewHolder.tvHeader = (TextView) view.findViewById(R.id.tv_person_header);
            viewHolder.ivHeader = (CircleImageView) view.findViewById(R.id.iv_person_header);
            viewHolder.tvCompany = (TextView) view.findViewById(R.id.tv_content_company);
            viewHolder.tvLetter = (TextView) view.findViewById(R.id.catalog);
            viewHolder.cbChecked = (CheckBox) view.findViewById(R.id.cbChecked);
            viewHolder.viewDivider = view.findViewById(R.id.view_divider);
            view.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) view.getTag();
        }

        //根據position擷取分類的首字母的Char ascii值
        int section = getSectionForPosition(position);

        //如果目前位置等于該分類首字母的Char的位置 ,則認為是第一次出現
        if (position == getPositionForSection(section)) {
            viewHolder.tvLetter.setVisibility(View.VISIBLE);
            viewHolder.viewDivider.setVisibility(View.VISIBLE);
            viewHolder.tvLetter.setText(mContent.getSortLetters());
        } else {
            viewHolder.tvLetter.setVisibility(View.GONE);
            viewHolder.viewDivider.setVisibility(View.GONE);
        }
        String name = this.mList.get(position).getEmpName();
        String backGroundColor = this.mList.get(position).getBackGroundColor();
        String facePhotoUrl = this.mList.get(position).getFacephoto();
        if (StringUtils.isEmpty(facePhotoUrl)) {
            viewHolder.tvHeader.setVisibility(View.VISIBLE);
            viewHolder.ivHeader.setVisibility(View.GONE);
            GradientDrawable myGrad = (GradientDrawable) viewHolder.tvHeader.getBackground();
            if (!StringUtils.isEmpty(backGroundColor)) {
                myGrad.setColor(Color.parseColor(backGroundColor));
            } else {
                myGrad.setColor(Color.parseColor("#38AFF7"));
            }
            if (!StringUtils.isEmpty(name) && name.length() >= 2) {
                viewHolder.tvHeader.setText(name.substring(name.length() - 2, name.length()));
            } else {
                viewHolder.tvHeader.setText(name);
            }
        } else {
            viewHolder.ivHeader.setVisibility(View.VISIBLE);
            viewHolder.tvHeader.setVisibility(View.GONE);
            ImageLoader.getInstance().displayImage(AppConfig.IMAGE_FONT_URL + facePhotoUrl, viewHolder.ivHeader, mOption);
        }
        viewHolder.tvTitle.setText(name);
        viewHolder.tvCompany.setText(this.mList.get(position).getJobName());
        viewHolder.cbChecked.setChecked(isSelected(mContent));

        return view;

    }

    public static class ViewHolder {
        public TextView tvLetter;
        public TextView tvTitle;
        public TextView tvHeader;
        public CircleImageView ivHeader;
        public TextView tvCompany;
        public CheckBox cbChecked;
        public View viewDivider;
    }

    /**
     * 根據ListView的目前位置擷取分類的首字母的Char ascii值
     */
    public int getSectionForPosition(int position) {
        return mList.get(position).getSortLetters().charAt(0);
    }

    /**
     * 根據分類的首字母的Char ascii值擷取其第一次出現該首字母的位置
     */
    public int getPositionForSection(int section) {
        for (int i = 0; i < getCount(); i++) {
            String sortStr = mList.get(i).getSortLetters();
            char firstChar = sortStr.toUpperCase(Locale.CHINESE).charAt(0);
            if (firstChar == section) {
                return i;
            }
        }
        return -1;
    }

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

    private boolean isSelected(PersonInfoEntity model) {
        for (int i = 0; i < mSelectedList.size(); i++) {
            if (StringUtils.equals(mSelectedList.get(i).getPersonId(), model.getPersonId())) {
                return true;
            }
        }
        return false;
    }

    public void toggleChecked(int position) {
        if (isSelected(mList.get(position))) {
            removeSelected(position);
        } else {
            setSelected(position);
        }

    }

    private void setSelected(int position) {
        mSelectedList.add(mList.get(position));
    }

    private void removeSelected(int position) {
        for (int i = 0; i < mSelectedList.size(); i++) {
            if (StringUtils.equals(mSelectedList.get(i).getPersonId(), mList.get(position).getPersonId())) {
                mSelectedList.remove(mSelectedList.get(i));
                break;
            }
        }
    }

    public List<PersonInfoEntity> getSelectedList() {
        return mSelectedList;
    }
}
           
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/white"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/catalog"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:paddingLeft="15dp"
        android:gravity="center_vertical"
        android:textColor="@color/txt_gray"
        android:textSize="15sp" />

    <View
        android:id="@+id/view_divider"
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@color/txt_gray_divider"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:paddingLeft="15dp" >

        <CheckBox
            android:id="@+id/cbChecked"
            android:layout_width="17dp"
            android:layout_height="17dp"
            android:background="@drawable/selector_checkbox_contact"
            android:button="@null"
            android:clickable="false"
            android:focusable="false"
            android:visibility="visible" />

        <TextView
            android:id="@+id/tv_person_header"
            android:layout_width="36dp"
            android:layout_height="36dp"
            android:layout_marginLeft="13dp"
            android:background="@drawable/shape_circle_person_header"
            android:text="三豐"
            android:gravity="center"
            android:textSize="12sp"
            android:textColor="@color/white"
            />

        <com.pcjz.lems.business.common.view.CircleImageView
            android:id="@+id/iv_person_header"
            android:layout_width="36dp"
            android:layout_height="36dp"
            android:layout_marginLeft="13dp"
            android:src="@mipmap/ic_launcher"
            android:visibility="gone"/>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center_vertical"
                android:orientation="horizontal">
            <TextView
                android:id="@+id/tv_title_name"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_marginLeft="13dp"
                android:singleLine="true"
                android:textColor="@color/txt_black"
                android:textSize="15sp"
                android:text="李超勤"/>

            <TextView
                android:id="@+id/tv_content_company"
                android:layout_width="0dp"
                android:layout_weight="2"
                android:layout_height="wrap_content"
                android:paddingRight="20dp"
                android:singleLine="true"
                android:textColor="@color/txt_black"
                android:textSize="12sp"
                android:text="上城建築科技有限公司項目部"/>
            </LinearLayout>
            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:layout_alignParentBottom="true"
                android:layout_marginLeft="13dp"
                android:background="@color/txt_gray_divider"/>
        </RelativeLayout>
    </LinearLayout>
</LinearLayout>
           
package com.pcjz.lems.ui.workrealname.manageequipment.adapter;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.TextView;

import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.pcjz.lems.R;
import com.pcjz.lems.business.common.utils.StringUtils;
import com.pcjz.lems.business.common.view.CircleImageView;
import com.pcjz.lems.business.config.AppConfig;
import com.pcjz.lems.business.entity.PersonInfoEntity;
import com.pcjz.lems.business.widget.contractlist.CNPinyinIndex;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * created by yezhengyu on 2017/9/19 18:59
 */

public class SearchContractsAdapter extends BaseAdapter {
    private List<CNPinyinIndex<PersonInfoEntity>> mList;
    private List<PersonInfoEntity> mSelectedList;
    private Context mContext;

    private DisplayImageOptions mOption = new DisplayImageOptions.Builder()
            .showImageOnLoading(R.drawable.load_pho_default_image)
            .showImageForEmptyUri(R.drawable.load_pho_default_image)
            .showImageOnFail(R.drawable.load_pho_default_image)
            .cacheInMemory(true)
            .cacheOnDisc(false).bitmapConfig(Bitmap.Config.RGB_565)
            .imageScaleType(ImageScaleType.EXACTLY).build();

    public SearchContractsAdapter(Context mContext, List<CNPinyinIndex<PersonInfoEntity>> list) {
        this.mContext = mContext;
        mSelectedList = new ArrayList<>();
        this.mList = list;
    }

    /**
     * 當ListView資料發生變化時,調用此方法來更新ListView
     */
    public void updateListView(List<CNPinyinIndex<PersonInfoEntity>> newDatas, List<PersonInfoEntity> contactSelectedList) {
        this.mList.clear();
        this.mList.addAll(newDatas);
        mSelectedList.clear();
        mSelectedList.addAll(contactSelectedList);
        notifyDataSetChanged();
    }

    public void updateListView2(List<PersonInfoEntity> contactSelectedList) {
        mSelectedList.clear();
        mSelectedList.addAll(contactSelectedList);
        notifyDataSetChanged();
    }

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

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

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

    public View getView(final int position, View view, ViewGroup arg2) {
        ViewHolder viewHolder = null;
        CNPinyinIndex<PersonInfoEntity> cnPinyinIndex = mList.get(position);
        PersonInfoEntity personInfo = cnPinyinIndex.cnPinyin.data;
        if (view == null) {
            viewHolder = new ViewHolder();
            view = LayoutInflater.from(mContext).inflate(R.layout.item_contact, null);
            viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title_name);
            viewHolder.tvHeader = (TextView) view.findViewById(R.id.tv_person_header);
            viewHolder.ivHeader = (CircleImageView) view.findViewById(R.id.iv_person_header);
            viewHolder.tvCompany = (TextView) view.findViewById(R.id.tv_content_company);
            viewHolder.tvLetter = (TextView) view.findViewById(R.id.catalog);
            viewHolder.cbChecked = (CheckBox) view.findViewById(R.id.cbChecked);
            viewHolder.viewDivider = view.findViewById(R.id.view_divider);
            view.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) view.getTag();
        }
        viewHolder.tvLetter.setVisibility(View.GONE);
        viewHolder.viewDivider.setVisibility(View.GONE);
        String name = personInfo.getEmpName();
        String backGroundColor = personInfo.getBackGroundColor();
        String facePhotoUrl = personInfo.getFacephoto();
        if (StringUtils.isEmpty(facePhotoUrl)) {
            viewHolder.tvHeader.setVisibility(View.VISIBLE);
            viewHolder.ivHeader.setVisibility(View.GONE);
            GradientDrawable myGrad = (GradientDrawable) viewHolder.tvHeader.getBackground();
            if (!StringUtils.isEmpty(backGroundColor)) {
                myGrad.setColor(Color.parseColor(backGroundColor));
            } else {
                myGrad.setColor(Color.parseColor("#38AFF7"));
            }
            if (!StringUtils.isEmpty(name) && name.length() >= 2) {
                viewHolder.tvHeader.setText(name.substring(name.length() - 2, name.length()));
            } else {
                viewHolder.tvHeader.setText(name);
            }
        } else {
            viewHolder.ivHeader.setVisibility(View.VISIBLE);
            viewHolder.tvHeader.setVisibility(View.GONE);
            ImageLoader.getInstance().displayImage(AppConfig.IMAGE_FONT_URL + facePhotoUrl, viewHolder.ivHeader, mOption);
        }
        int startText = cnPinyinIndex.startText;
        int endText = cnPinyinIndex.endText;
        int startContent = cnPinyinIndex.startContent;
        int endContent = cnPinyinIndex.endContent;
        if (endText != 0 && endText >= startText) {
            SpannableStringBuilder ssb = new SpannableStringBuilder(personInfo.chineseText());
            ForegroundColorSpan span = new ForegroundColorSpan(Color.parseColor("#FD9426"));
            ssb.setSpan(span, startText, endText, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            viewHolder.tvTitle.setText(ssb);
        } else {
            viewHolder.tvTitle.setText(name);
        }
        if (endContent != 0 && endContent >= startContent) {
            SpannableStringBuilder ssb1 = new SpannableStringBuilder(personInfo.chineseContent());
            ForegroundColorSpan span1 = new ForegroundColorSpan(Color.parseColor("#FD9426"));
            ssb1.setSpan(span1, startContent, endContent, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            viewHolder.tvCompany.setText(ssb1);
        } else {
            viewHolder.tvCompany.setText(personInfo.getJobName());
        }
        viewHolder.cbChecked.setChecked(isSelected(personInfo));
        return view;
    }

    public static class ViewHolder {
        public TextView tvLetter;
        public TextView tvTitle;
        public TextView tvHeader;
        public CircleImageView ivHeader;
        public TextView tvCompany;
        public CheckBox cbChecked;
        public View viewDivider;
    }

    private boolean isSelected(PersonInfoEntity personInfo) {
        for (int i = 0; i < mSelectedList.size(); i++) {
            if (StringUtils.equals(mSelectedList.get(i).getPersonId(), personInfo.getPersonId())) {
                return true;
            }
        }
        return false;
    }

    public void toggleChecked(int position) {
        PersonInfoEntity personInfo = mList.get(position).cnPinyin.data;
        if (isSelected(personInfo)) {
            removeSelected(personInfo);
        } else {
            setSelected(personInfo);
        }
    }

    private void setSelected(PersonInfoEntity personInfo) {
        mSelectedList.add(personInfo);
    }

    private void removeSelected(PersonInfoEntity personInfo) {
        for (int i = 0; i < mSelectedList.size(); i++) {
            if (StringUtils.equals(mSelectedList.get(i).getPersonId(), personInfo.getPersonId())) {
                mSelectedList.remove(mSelectedList.get(i));
                break;
            }
        }
    }

    public List<PersonInfoEntity> getSelectedList() {
        return mSelectedList;
    }
}
           
package com.pcjz.lems.ui.workrealname.manageequipment.adapter;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.pcjz.lems.R;
import com.pcjz.lems.business.common.utils.StringUtils;
import com.pcjz.lems.business.common.view.CircleImageView;
import com.pcjz.lems.business.config.AppConfig;
import com.pcjz.lems.business.entity.PersonInfoEntity;

import java.util.List;

/**
 * created by yezhengyu on 2017/9/18 16:58
 */

public class SearchHeaderAdapter extends RecyclerView.Adapter<SearchHeaderAdapter.MyViewHolder> {

    private final int width;
    private Context mContext;
    private List<PersonInfoEntity> mDatas;
    private LayoutInflater mInflater;

    private DisplayImageOptions mOption = new DisplayImageOptions.Builder()
            .showImageOnLoading(R.drawable.load_pho_default_image)
            .showImageForEmptyUri(R.drawable.load_pho_default_image)
            .showImageOnFail(R.drawable.load_pho_default_image)
            .cacheInMemory(true)
            .cacheOnDisc(false).bitmapConfig(Bitmap.Config.RGB_565)
            .imageScaleType(ImageScaleType.EXACTLY).build();

    public SearchHeaderAdapter(Context context, List<PersonInfoEntity> datas, int width) {
        this.mContext = context;
        this.mDatas = datas;
        mInflater = LayoutInflater.from(context);
        this.width = width;
    }

    @Override
    public int getItemCount() {
        return mDatas == null ? 0 : mDatas.size();
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.item_search_person, parent, false);
        ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
        layoutParams.width = width / 8;
        view.setLayoutParams(layoutParams);
        MyViewHolder holder = new MyViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position) {
        String name = mDatas.get(position).getEmpName();
        String backGroundColor = mDatas.get(position).getBackGroundColor();
        String facePhotoUrl = mDatas.get(position).getFacephoto();
        if (StringUtils.isEmpty(facePhotoUrl)) {
            holder.tvPerson.setVisibility(View.VISIBLE);
            holder.ivHeader.setVisibility(View.GONE);
            GradientDrawable myGrad = (GradientDrawable) holder.tvPerson.getBackground();
            if (!StringUtils.isEmpty(backGroundColor)) {
                myGrad.setColor(Color.parseColor(backGroundColor));
            } else {
                myGrad.setColor(Color.parseColor("#38AFF7"));
            }
            String subtring = "";
            if (!StringUtils.isEmpty(name)) {
                if (name.length() > 1) {
                    subtring = name.substring(name.length() - 2, name.length());
                } else {
                    subtring = name;
                }
            }
            holder.tvPerson.setText(subtring);
        } else {
            holder.ivHeader.setVisibility(View.VISIBLE);
            holder.tvPerson.setVisibility(View.GONE);
            ImageLoader.getInstance().displayImage(AppConfig.IMAGE_FONT_URL + facePhotoUrl, holder.ivHeader, mOption);
        }
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onClickLisener.setOnClickListener(holder.itemView, position);
            }
        });
    }

    private OnClickLisenerI onClickLisener;

    public void setOnItemClickListener(OnClickLisenerI onClickLisener) {
        this.onClickLisener = onClickLisener;
    }

    public interface OnClickLisenerI{
        void setOnClickListener(View view, int position);
    }

    class MyViewHolder extends RecyclerView.ViewHolder {

        TextView tvPerson;
        CircleImageView ivHeader;

        public MyViewHolder(View itemView) {
            super(itemView);
            tvPerson = (TextView) itemView.findViewById(R.id.tv_person_header);
            ivHeader = (CircleImageView) itemView.findViewById(R.id.iv_person_header);
        }
    }
}
           
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="60dp"
    android:layout_height="60dp"
    android:orientation="vertical"
    android:gravity="center">
    <TextView
        android:id="@+id/tv_person_header"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:background="@drawable/shape_circle_person_header"
        android:text="三豐"
        android:gravity="center"
        android:textSize="12sp"
        android:textColor="@color/white"
        />

    <com.pcjz.lems.business.common.view.CircleImageView
        android:id="@+id/iv_person_header"
        android:layout_width="36dp"
        android:layout_height="36dp"
        android:visibility="gone"/>
</LinearLayout>
           
package com.pcjz.lems.business.common.view;

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

/**
 * created by yezhengyu on 2017/9/19 14:10
 */

public class SideBar extends View {
    public SideBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

    public SideBar(Context context) {
        super(context);
    }

    // 26個字母
    public static String[] b = {"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 int choose = -1;// 選中
    private Paint paint = new Paint();
    private Paint paint2 = new Paint();

    private TextView mTextDialog;

    public void setTextView(TextView mTextDialog) {
        this.mTextDialog = mTextDialog;
    }

    /**
     * 重寫這個方法
     */
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 擷取焦點改變背景顔色.
        int height = getHeight();// 擷取對應高度
        int width = getWidth(); // 擷取對應寬度
        int singleHeight = height / b.length;// 擷取每一個字母的高度

        for (int i = 0; i < b.length; i++) {
            paint.setColor(Color.parseColor("#999999"));
            paint.setTypeface(Typeface.DEFAULT_BOLD);
            paint.setAntiAlias(true);
            paint.setTextSize(dp2px(12));
            // x坐标等于中間-字元串寬度的一半.
            float xPos = width / 2 - paint.measureText(b[i]) / 2;
            float yPos = singleHeight * i + singleHeight;
            // 選中的狀态
            if (i == choose) {
                paint.setColor(Color.parseColor("#ffffff"));
                paint.setFakeBoldText(true);
                paint2.setColor(Color.parseColor("#38AFF7"));
                canvas.drawCircle(width / 2, yPos - paint.measureText(b[i]) / 2, width / 3, paint2);
            }
            canvas.drawText(b[i], xPos, yPos, paint);
            paint.reset();// 重置畫筆
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        final int action = event.getAction();
        final float y = event.getY();// 點選y坐标
        final int oldChoose = choose;
        final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
        final int c = (int) (y / getHeight() * b.length);// 點選y坐标所占總高度的比例*b數組的長度就等于點選b中的個數.
        switch (action) {
            case MotionEvent.ACTION_UP:
                //setBackgroundDrawable(new ColorDrawable(0x00000000));
                choose = -1;//
                invalidate();
                if (mTextDialog != null) {
                    mTextDialog.setVisibility(View.INVISIBLE);
                }
                break;

            default:
                //setBackgroundResource(R.drawable.sidebar_background);
                if (oldChoose != c) {
                    if (c >= 0 && c < b.length) {
                        if (listener != null) {
                            listener.onTouchingLetterChanged(b[c]);
                        }
                        if (mTextDialog != null) {
                            mTextDialog.setText(b[c]);
                            mTextDialog.setVisibility(View.VISIBLE);
                        }
                        choose = c;
                        invalidate();
                    }
                }
                break;
        }
        return true;
    }

    private OnTouchingLetterChangedListener onTouchingLetterChangedListener;

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

    public interface OnTouchingLetterChangedListener {
        void onTouchingLetterChanged(String s);
    }

    private int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
    }
}

           

拼音相關的類

/*
 * Filename	CharacterParser.java
 * Company	�Ϻ�����-�ֶ��ֹ�˾��
 * @author	LuRuihui
 * @version	0.1
 */
package com.pcjz.lems.business.common.view.personlist;

/**
 * Java����ת��Ϊƴ��
 * 
 */
public class CharacterParser {
	private static int[] pyvalue = new int[] {-20319, -20317, -20304, -20295, -20292, -20283, -20265, -20257, -20242, -20230, -20051, -20036, -20032,
			-20026, -20002, -19990, -19986, -19982, -19976, -19805, -19784, -19775, -19774, -19763, -19756, -19751, -19746, -19741, -19739, -19728,
			-19725, -19715, -19540, -19531, -19525, -19515, -19500, -19484, -19479, -19467, -19289, -19288, -19281, -19275, -19270, -19263, -19261,
			-19249, -19243, -19242, -19238, -19235, -19227, -19224, -19218, -19212, -19038, -19023, -19018, -19006, -19003, -18996, -18977, -18961,
			-18952, -18783, -18774, -18773, -18763, -18756, -18741, -18735, -18731, -18722, -18710, -18697, -18696, -18526, -18518, -18501, -18490,
			-18478, -18463, -18448, -18447, -18446, -18239, -18237, -18231, -18220, -18211, -18201, -18184, -18183, -18181, -18012, -17997, -17988,
			-17970, -17964, -17961, -17950, -17947, -17931, -17928, -17922, -17759, -17752, -17733, -17730, -17721, -17703, -17701, -17697, -17692,
			-17683, -17676, -17496, -17487, -17482, -17468, -17454, -17433, -17427, -17417, -17202, -17185, -16983, -16970, -16942, -16915, -16733,
			-16708, -16706, -16689, -16664, -16657, -16647, -16474, -16470, -16465, -16459, -16452, -16448, -16433, -16429, -16427, -16423, -16419,
			-16412, -16407, -16403, -16401, -16393, -16220, -16216, -16212, -16205, -16202, -16187, -16180, -16171, -16169, -16158, -16155, -15959,
			-15958, -15944, -15933, -15920, -15915, -15903, -15889, -15878, -15707, -15701, -15681, -15667, -15661, -15659, -15652, -15640, -15631,
			-15625, -15454, -15448, -15436, -15435, -15419, -15416, -15408, -15394, -15385, -15377, -15375, -15369, -15363, -15362, -15183, -15180,
			-15165, -15158, -15153, -15150, -15149, -15144, -15143, -15141, -15140, -15139, -15128, -15121, -15119, -15117, -15110, -15109, -14941,
			-14937, -14933, -14930, -14929, -14928, -14926, -14922, -14921, -14914, -14908, -14902, -14894, -14889, -14882, -14873, -14871, -14857,
			-14678, -14674, -14670, -14668, -14663, -14654, -14645, -14630, -14594, -14429, -14407, -14399, -14384, -14379, -14368, -14355, -14353,
			-14345, -14170, -14159, -14151, -14149, -14145, -14140, -14137, -14135, -14125, -14123, -14122, -14112, -14109, -14099, -14097, -14094,
			-14092, -14090, -14087, -14083, -13917, -13914, -13910, -13907, -13906, -13905, -13896, -13894, -13878, -13870, -13859, -13847, -13831,
			-13658, -13611, -13601, -13406, -13404, -13400, -13398, -13395, -13391, -13387, -13383, -13367, -13359, -13356, -13343, -13340, -13329,
			-13326, -13318, -13147, -13138, -13120, -13107, -13096, -13095, -13091, -13076, -13068, -13063, -13060, -12888, -12875, -12871, -12860,
			-12858, -12852, -12849, -12838, -12831, -12829, -12812, -12802, -12607, -12597, -12594, -12585, -12556, -12359, -12346, -12320, -12300,
			-12120, -12099, -12089, -12074, -12067, -12058, -12039, -11867, -11861, -11847, -11831, -11798, -11781, -11604, -11589, -11536, -11358,
			-11340, -11339, -11324, -11303, -11097, -11077, -11067, -11055, -11052, -11045, -11041, -11038, -11024, -11020, -11019, -11018, -11014,
			-10838, -10832, -10815, -10800, -10790, -10780, -10764, -10587, -10544, -10533, -10519, -10331, -10329, -10328, -10322, -10315, -10309,
			-10307, -10296, -10281, -10274, -10270, -10262, -10260, -10256, -10254};
	public static String[] pystr = new String[] {"a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian",
			"biao", "bie", "bin", "bing", "bo", "bu", "ca", "cai", "can", "cang", "cao", "ce", "ceng", "cha", "chai", "chan", "chang", "chao", "che",
			"chen", "cheng", "chi", "chong", "chou", "chu", "chuai", "chuan", "chuang", "chui", "chun", "chuo", "ci", "cong", "cou", "cu", "cuan",
			"cui", "cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "deng", "di", "dian", "diao", "die", "ding", "diu", "dong", "dou", "du",
			"duan", "dui", "dun", "duo", "e", "en", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fo", "fou", "fu", "ga", "gai", "gan", "gang",
			"gao", "ge", "gei", "gen", "geng", "gong", "gou", "gu", "gua", "guai", "guan", "guang", "gui", "gun", "guo", "ha", "hai", "han", "hang",
			"hao", "he", "hei", "hen", "heng", "hong", "hou", "hu", "hua", "huai", "huan", "huang", "hui", "hun", "huo", "ji", "jia", "jian",
			"jiang", "jiao", "jie", "jin", "jing", "jiong", "jiu", "ju", "juan", "jue", "jun", "ka", "kai", "kan", "kang", "kao", "ke", "ken",
			"keng", "kong", "kou", "ku", "kua", "kuai", "kuan", "kuang", "kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le", "lei", "leng",
			"li", "lia", "lian", "liang", "liao", "lie", "lin", "ling", "liu", "long", "lou", "lu", "lv", "luan", "lue", "lun", "luo", "ma", "mai",
			"man", "mang", "mao", "me", "mei", "men", "meng", "mi", "mian", "miao", "mie", "min", "ming", "miu", "mo", "mou", "mu", "na", "nai",
			"nan", "nang", "nao", "ne", "nei", "nen", "neng", "ni", "nian", "niang", "niao", "nie", "nin", "ning", "niu", "nong", "nu", "nv", "nuan",
			"nue", "nuo", "o", "ou", "pa", "pai", "pan", "pang", "pao", "pei", "pen", "peng", "pi", "pian", "piao", "pie", "pin", "ping", "po", "pu",
			"qi", "qia", "qian", "qiang", "qiao", "qie", "qin", "qing", "qiong", "qiu", "qu", "quan", "que", "qun", "ran", "rang", "rao", "re",
			"ren", "reng", "ri", "rong", "rou", "ru", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang", "sao", "se", "sen", "seng", "sha",
			"shai", "shan", "shang", "shao", "she", "shen", "sheng", "shi", "shou", "shu", "shua", "shuai", "shuan", "shuang", "shui", "shun",
			"shuo", "si", "song", "sou", "su", "suan", "sui", "sun", "suo", "ta", "tai", "tan", "tang", "tao", "te", "teng", "ti", "tian", "tiao",
			"tie", "ting", "tong", "tou", "tu", "tuan", "tui", "tun", "tuo", "wa", "wai", "wan", "wang", "wei", "wen", "weng", "wo", "wu", "xi",
			"xia", "xian", "xiang", "xiao", "xie", "xin", "xing", "xiong", "xiu", "xu", "xuan", "xue", "xun", "ya", "yan", "yang", "yao", "ye", "yi",
			"yin", "ying", "yo", "yong", "you", "yu", "yuan", "yue", "yun", "za", "zai", "zan", "zang", "zao", "ze", "zei", "zen", "zeng", "zha",
			"zhai", "zhan", "zhang", "zhao", "zhe", "zhen", "zheng", "zhi", "zhong", "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui",
			"zhun", "zhuo", "zi", "zong", "zou", "zu", "zuan", "zui", "zun", "zuo"};
	private StringBuilder buffer;
	private String resource;
	private static CharacterParser characterParser = new CharacterParser();

	public static CharacterParser getInstance() {
		return characterParser;
	}

	public String getResource() {
		return resource;
	}

	public void setResource(String resource) {
		this.resource = resource;
	}

	/** * ����ת��ASCII�� * * @param chs * @return */
	private int getChsAscii(String chs) {
		int asc = 0;
		try {
			byte[] bytes = chs.getBytes("gb2312");
			if (bytes == null || bytes.length > 2 || bytes.length <= 0) {
				throw new RuntimeException("illegal resource string");
			}
			if (bytes.length == 1) {
				asc = bytes[0];
			}
			if (bytes.length == 2) {
				int hightByte = 256 + bytes[0];
				int lowByte = 256 + bytes[1];
				asc = (256 * hightByte + lowByte) - 256 * 256;
			}
		} catch (Exception e) {
			System.out.println("ERROR:ChineseSpelling.class-getChsAscii(String chs)" + e);
		}
		return asc;
	}

	/** * ���ֽ��� * * @param str * @return */
	public String convert(String str) {
		String result = null;
		int ascii = getChsAscii(str);
		if (ascii > 0 && ascii < 160) {
			result = String.valueOf((char) ascii);
		} else {
			for (int i = (pyvalue.length - 1); i >= 0; i--) {
				if (pyvalue[i] <= ascii) {
					result = pystr[i];
					break;
				}
			}
		}
		return result;
	}

	/** * ������� * * @param chs * @return */
	public String getSelling(String chs) {
		String key, value;
		buffer = new StringBuilder();
		for (int i = 0; i < chs.length(); i++) {
			key = chs.substring(i, i + 1);
			if (key.getBytes().length >= 2) {
				value = convert(key);
				if (value == null) {
					value = "unknown";
				}
			} else {
				value = key;
			}
			buffer.append(value);
		}
		return buffer.toString();
	}

	public String getSpelling() {
		return this.getSelling(this.getResource());
	}

}

           
package com.pcjz.lems.business.common.view.personlist;

import com.pcjz.lems.business.entity.PersonInfoEntity;

import java.util.Comparator;

/**
 * 
 * @author xiaanming
 *
 */
public class PinyinComparator implements Comparator<PersonInfoEntity> {

	public int compare(PersonInfoEntity o1, PersonInfoEntity o2) {
		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());
		}
	}

}

           
package com.pcjz.lems.business.widget.contractlist;

import android.os.Handler;
import android.support.v4.util.ArrayMap;
import android.text.TextUtils;

import com.github.promeg.pinyinhelper.Pinyin;

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

/**
 * Created by you on 2017/9/8.
 */

public final class CNPinyinFactory {

    static final ArrayMap<Character, String> SURNAMES = new ArrayMap<>();

    static {
        SURNAMES.put('仇', "QIU");
        SURNAMES.put('柏', "BO");
        SURNAMES.put('牟', "MU");
        SURNAMES.put('颉', "XIE");
        SURNAMES.put('解', "XIE");
        SURNAMES.put('尉', "YU");
        SURNAMES.put('奇', "JI");
        SURNAMES.put('單', "SHAN");
        SURNAMES.put('谌', "SHEN");
        SURNAMES.put('樂', "YUE");
        SURNAMES.put('召', "SHAO");
        SURNAMES.put('樸', "PIAO");
        SURNAMES.put('區', "OU");
        SURNAMES.put('查', "ZHA");
        SURNAMES.put('曾', "ZENG");
        SURNAMES.put('缪', "MIAO");
    }

    static final char DEF_CHAR = '#';

    /**
     * 轉換拼音, 考慮在子線程中運作
     * @param tList
     * @param <T>
     * @return
     */
    public static <T extends CN> void createCNPinyinList(List<T> tList, int requestCode, Handler handler) {
        if (tList != null && !tList.isEmpty()) {
            ArrayList<CNPinyin<T>> cnPinyinArrayList = new ArrayList<>(tList.size());
            for (T t : tList) {
                CNPinyin<T> pinyin = createCNPinyin(t);
                if (pinyin != null) {
                    cnPinyinArrayList.add(pinyin);
                }
            }
            handler.obtainMessage(requestCode, cnPinyinArrayList).sendToTarget();
        } else {
            handler.obtainMessage(requestCode, null).sendToTarget();
        }
    }

    public static <T extends CN> CNPinyin<T> createCNPinyin(T t) {
        if (t == null || (t.chineseText() == null && t.chineseContent() == null)) return null;
        String chinese = "";
        if (t.chineseText() != null) {
            chinese = t.chineseText().trim();
        }
        String chinese1 = "";
        if (t.chineseContent() != null) {
            chinese1 = t.chineseContent().trim();
        }
        if (TextUtils.isEmpty(chinese) && TextUtils.isEmpty(chinese1)) return null;
        CNPinyin cnPinyin = new CNPinyin(t);
        if (!TextUtils.isEmpty(chinese)) {
            char[] chars = chinese.toCharArray();
            String[] charPinyins = new String[chars.length];
            StringBuilder stringBuilder = new StringBuilder();
            int pinyinsTotalLength = 0;
            for (int i = 0; i < chars.length; i++) {
                char c = chars[i];
                //将輸入的字元串轉換為拼音,并區分多音字
                String pinyin = charToPinyin(chars[i], i);
                charPinyins[i] = pinyin;
                if (pinyin.length() > 0) {
                    stringBuilder.append(pinyin.charAt(0));
                } else {
                    stringBuilder.append(c);
                }
                pinyinsTotalLength += pinyin.length();
            }
            cnPinyin.pinyinsText = charPinyins;
            cnPinyin.firstCharText = getFirstChar(charPinyins);
            cnPinyin.firstCharsText = stringBuilder.toString();
            cnPinyin.pinyinsTextTotalLength = pinyinsTotalLength;
        }
        if (!TextUtils.isEmpty(chinese1)) {
            char[] chars = chinese1.toCharArray();
            String[] charPinyins = new String[chars.length];
            StringBuilder stringBuilder = new StringBuilder();
            int pinyinsTotalLength = 0;
            for (int i = 0; i < chars.length; i++) {
                char c = chars[i];
                //将輸入的字元串轉換為拼音,并區分多音字
                String pinyin = charToPinyin(chars[i], i);
                charPinyins[i] = pinyin;
                if (pinyin.length() > 0) {
                    stringBuilder.append(pinyin.charAt(0));
                } else {
                    stringBuilder.append(c);
                }
                pinyinsTotalLength += pinyin.length();
            }
            cnPinyin.pinyinsContent = charPinyins;
            cnPinyin.firstCharContent = getFirstChar(charPinyins);
            cnPinyin.firstCharsContent = stringBuilder.toString();
            cnPinyin.pinyinsContentTotalLength = pinyinsTotalLength;
        }
        return cnPinyin;
    }

    /**
     *
     * @param c
     * @param index
     * @return
     */
    private static String charToPinyin(char c, int index) {
        if (index == 0) {
            String pinyin = SURNAMES.get(c);
            if (pinyin != null) {
                return pinyin;
            }
        }
        String pinyin =  Pinyin.toPinyin(c);
        if (pinyin == null) {
            pinyin = String.valueOf(c);
        }
        return pinyin;
    }

    /**
     * 拼音首個字母
     * @param pinyins
     * @return
     */
    private static char getFirstChar(String[] pinyins) {
        if (pinyins != null && pinyins.length > 0) {
            String firstPinying = pinyins[0];
            if (firstPinying.length() > 0) {
                return charToUpperCase(firstPinying.charAt(0));
            }
        }
        return DEF_CHAR;
    }

    /**
     * 字元轉大寫
     * @param c
     * @return
     */
    private static char charToUpperCase(char c) {
        if (c >= 'a' && c <= 'z') {
            c -= 32;
        }
        return c;
    }

}

           
package com.pcjz.lems.business.widget.contractlist;

import android.os.Handler;
import android.text.TextUtils;

import com.pcjz.lems.business.common.utils.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by you on 2017/9/8.
 */

public final class CNPinyinIndexFactory {
    /**
     * 轉換搜尋拼音集合, 考慮在子線程中運作
     *
     * @param cnPinyinList
     * @param keyword
     * @return
     */
    public static <T extends CN> void indexList(List<CNPinyin<T>> cnPinyinList, String keyword, int requestCode, Handler handler) {
        ArrayList<CNPinyinIndex<T>> cnPinyinIndexArrayList = new ArrayList<>();
        if (cnPinyinList != null && cnPinyinList.size() != 0) {
            for (CNPinyin<T> cnPinyin : cnPinyinList) {
                CNPinyinIndex<T> index = index(cnPinyin, keyword);
                if (index != null) {
                    cnPinyinIndexArrayList.add(index);
                }
            }
        }
        handler.obtainMessage(requestCode, cnPinyinIndexArrayList).sendToTarget();
    }

    /**
     * 比對拼音
     *
     * @param cnPinyin
     * @return null代表沒有比對
     */
    public static <T extends CN> CNPinyinIndex<T> index(CNPinyin<T> cnPinyin, String keyword) {
        if (TextUtils.isEmpty(keyword)) return null;
        //比對中文搜尋
        CNPinyinIndex cnPinyinIndex = matcherChinese(cnPinyin, keyword);
        if (isContainChinese(keyword)) {//包含中文隻比對原字元
            return cnPinyinIndex;
        }
        if (cnPinyinIndex == null) {
            //比對首字母搜尋
            cnPinyinIndex = matcherFirst(cnPinyin, keyword);
            if (cnPinyinIndex == null) {
                //比對全拼音搜尋
                cnPinyinIndex = matchersPinyins(cnPinyin, keyword);
            }
        }
        return cnPinyinIndex;
    }

    /**
     * 比對中文
     *
     * @param cnPinyin
     * @param keyword
     * @return
     */
    static CNPinyinIndex matcherChinese(CNPinyin cnPinyin, String keyword) {
        if (cnPinyin.data.chineseText() != null) {
            if (keyword.length() <= cnPinyin.data.chineseText().length()) {
                Matcher matcher = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE).matcher(cnPinyin.data.chineseText());
                if (matcher.find()) {
                    if (cnPinyin.data.chineseContent() != null) {
                        if (keyword.length() <= cnPinyin.data.chineseContent().length()) {
                            Matcher matcher1 = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE).matcher(cnPinyin.data.chineseContent());
                            if (matcher1.find()) {
                                return new CNPinyinIndex(cnPinyin, matcher.start(), matcher.end(), matcher1.start(), matcher1.end());
                            }
                        }
                    }
                    return new CNPinyinIndex(cnPinyin, matcher.start(), matcher.end(), 0, 0);
                }
            }
        }
        if (cnPinyin.data.chineseContent() != null) {
            if (keyword.length() <= cnPinyin.data.chineseContent().length()) {
                Matcher matcher1 = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE).matcher(cnPinyin.data.chineseContent());
                if (matcher1.find()) {
                    return new CNPinyinIndex(cnPinyin, 0, 0, matcher1.start(), matcher1.end());
                }
            }
        }
        return null;
    }


    /**
     * 比對首字母
     *
     * @param cnPinyin
     * @param keyword
     * @return
     */
    static CNPinyinIndex matcherFirst(CNPinyin cnPinyin, String keyword) {
        if (cnPinyin.pinyinsText != null) {
            if (keyword.length() <= cnPinyin.pinyinsText.length) {
                Matcher matcher = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE).matcher(cnPinyin.firstCharsText);
                if (matcher.find()) {
                    if (cnPinyin.pinyinsContent != null) {
                        if (keyword.length() <= cnPinyin.pinyinsContent.length) {
                            Matcher matcher1 = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE).matcher(cnPinyin.firstCharsContent);
                            if (matcher1.find()) {
                                return new CNPinyinIndex(cnPinyin, matcher.start(), matcher.end(), matcher1.start(), matcher1.end());
                            }
                        }
                    }
                    return new CNPinyinIndex(cnPinyin, matcher.start(), matcher.end(), 0, 0);
                }
            }
        }
        if (cnPinyin.pinyinsContent != null) {
            if (keyword.length() <= cnPinyin.pinyinsContent.length) {
                Matcher matcher1 = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE).matcher(cnPinyin.firstCharsContent);
                if (matcher1.find()) {
                    return new CNPinyinIndex(cnPinyin, 0, 0, matcher1.start(), matcher1.end());
                }
            }
        }
        return null;
    }

    /**
     * 所有拼音比對
     * @param cnPinyin
     * @param keyword
     * @return
     */
    static CNPinyinIndex matchersPinyins(CNPinyin cnPinyin, String keyword) {
        if (keyword.length() <= cnPinyin.pinyinsTextTotalLength) {
            String startAndEnd = startAndEnd(cnPinyin.pinyinsText, keyword);
            String[] split = startAndEnd.split("\\.");
            int start = Integer.parseInt(split[0]);
            int end = Integer.parseInt(split[1]);
            if (start >= 0 && end >= start) {
                if (keyword.length() <= cnPinyin.pinyinsContentTotalLength) {
                    String start1AndEnd1 = startAndEnd(cnPinyin.pinyinsContent, keyword);
                    String[] split1 = start1AndEnd1.split("\\.");
                    int start1 = Integer.parseInt(split1[0]);
                    int end1 = Integer.parseInt(split1[1]);
                    if (start1 >= 0 && end1 >= start1) {
                        return new CNPinyinIndex(cnPinyin, start, end, start1, end1);
                    }
                }
                return new CNPinyinIndex(cnPinyin, start, end, 0, 0);
            }
        }
        if (keyword.length() <= cnPinyin.pinyinsContentTotalLength) {
            String start1AndEnd1 = startAndEnd(cnPinyin.pinyinsContent, keyword);
            String[] split1 = start1AndEnd1.split("\\.");
            int start1 = Integer.parseInt(split1[0]);
            int end1 = Integer.parseInt(split1[1]);
            if (start1 >= 0 && end1 >= start1) {
                return new CNPinyinIndex(cnPinyin, 0, 0, start1, end1);
            }
        }
        return null;
    }

    private static String startAndEnd(String[] pinyins, String keyword) {
        int start = -1;
        int end = -1;
        for (int i = 0; i < pinyins.length; i++) {
            String pat = pinyins[i];
            if (pat.length() >= keyword.length()) {//首個位置索引
                Matcher matcher = Pattern.compile(keyword, Pattern.CASE_INSENSITIVE).matcher(pat);
                if (matcher.find() && matcher.start() == 0) {
                    start = i;
                    end = i + 1;
                    break;
                }
            } else {
                Matcher matcher = Pattern.compile(pat, Pattern.CASE_INSENSITIVE).matcher(keyword);
                if (matcher.find() && matcher.start() == 0) {//全拼比對第一個必須在0位置
                    start = i;
                    String left = matcher.replaceFirst("");
                    end = end(pinyins, left, ++i);
                    break;
                }
            }
        }
        return start + "." + end;
    }

    /**
     * 根據比對字元遞歸查找下一結束位置
     *
     * @param pinyinGroup
     * @param pattern
     * @param index
     * @return -1 比對失敗
     */
    private static int end(String[] pinyinGroup, String pattern, int index) {
        if (index < pinyinGroup.length) {
            String pinyin = pinyinGroup[index];
            if (pinyin.length() >= pattern.length()) {//首個位置索引
                Matcher matcher = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE).matcher(pinyin);
                if (matcher.find() && matcher.start() == 0) {
                    return index + 1;
                }
            } else {
                Matcher matcher = Pattern.compile(pinyin, Pattern.CASE_INSENSITIVE).matcher(pattern);
                if (matcher.find() && matcher.start() == 0) {//全拼比對第一個必須在0位置
                    String left = matcher.replaceFirst("");
                    return end(pinyinGroup, left, index + 1);
                }
            }
        }
        return -1;
    }

    private static boolean isContainChinese(String str) {
        Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
        Matcher m = p.matcher(str);
        return m.find();
    }

}

           

實體類:聯系人、拼音實體類(拼音,首字母,長度)、拼音下标類

package com.pcjz.lems.business.widget.contractlist;

import java.io.Serializable;

/**
 * Created by you on 2017/9/11.
 */

public interface CN extends Serializable {

    String chineseText();
    String chineseContent();
}

           
package com.pcjz.lems.business.entity;

import com.pcjz.lems.business.widget.contractlist.CN;

import java.io.Serializable;

/**
 * created by yezhengyu on 2017/9/19 15:49
 */

public class PersonInfoEntity implements CN,Serializable{
    private String empName;
    private String jobName;
    private String facephoto;
    private String backGroundColor;
    private String personId;
    private String projectPersonId;
    private String deleteStatus;
    private String sortLetters;  //顯示資料拼音的首字母

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getJobName() {
        return jobName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName;
    }

    public String getFacephoto() {
        return facephoto;
    }

    public void setFacephoto(String facephoto) {
        this.facephoto = facephoto;
    }

    public String getPersonId() {
        return personId;
    }

    public void setPersonId(String personId) {
        this.personId = personId;
    }

    public String getProjectPersonId() {
        return projectPersonId;
    }

    public void setProjectPersonId(String projectPersonId) {
        this.projectPersonId = projectPersonId;
    }

    public String getSortLetters() {
        return sortLetters;
    }

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

    public String getBackGroundColor() {
        return backGroundColor;
    }

    public void setBackGroundColor(String backGroundColor) {
        this.backGroundColor = backGroundColor;
    }

    public String getDeleteStatus() {
        return deleteStatus;
    }

    public void setDeleteStatus(String deleteStatus) {
        this.deleteStatus = deleteStatus;
    }

    @Override
    public String chineseText() {
        return empName;
    }

    @Override
    public String chineseContent() {
        return jobName;
    }
}

           
package com.pcjz.lems.business.widget.contractlist;

import java.io.Serializable;

import static com.pcjz.lems.business.widget.contractlist.CNPinyinFactory.DEF_CHAR;
/**
 * Created by you on 2017/9/7.
 */

public class CNPinyin <T extends CN> implements Serializable, Comparable<CNPinyin<T>> {

    /**
     * 對應首字首拼音字母
     */
    char firstCharText;
    /**
     * 所有字元中的拼音首字母
     */
    String firstCharsText;
    /**
     * 對應的所有字母拼音
     */
    String[] pinyinsText;

    /**
     * 拼音總長度
     */
    int pinyinsTextTotalLength;


    //此處是加了内容後對内容的處理
    char firstCharContent;
    String firstCharsContent;
    String[] pinyinsContent;
    int pinyinsContentTotalLength;


    public final T data;

    CNPinyin(T data) {
        this.data = data;
    }

    public char getFirstCharText() {
        return firstCharText;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder().append("--firstChar--").append(firstCharText).append("--pinyins:");
        for (String str : pinyinsText) {
            sb.append(str);
        }
        return sb.toString();
    }

    int compareValue() {
        if (firstCharText == DEF_CHAR) {
            return 'Z' + 1;
        }
        return firstCharText;
    }

    @Override
    public int compareTo(CNPinyin<T> tcnPinyin) {
        int compare = compareValue() - tcnPinyin.compareValue();
        if (compare == 0) {
            String chinese1 = data.chineseText();
            String chinese2 = tcnPinyin.data.chineseText();
            return chinese1.compareTo(chinese2);
        }
        return compare;
    }
}

           
package com.pcjz.lems.business.widget.contractlist;

import java.io.Serializable;

/**
 * Created by you on 2017/9/8.
 */

public class CNPinyinIndex <T extends CN> implements Serializable {

    public final CNPinyin<T> cnPinyin;

    //對名字進行變色處理
    public final int startText;
    public final int endText;

    //此處是加了内容後對内容的處理
    public final int startContent;
    public final int endContent;

    CNPinyinIndex(CNPinyin cnPinyin, int startText, int endText, int startContent, int endContent) {
        this.cnPinyin = cnPinyin;
        this.startText = startText;
        this.endText = endText;
        this.startContent = startContent;
        this.endContent = endContent;
    }

    @Override
    public String toString() {
        return cnPinyin.toString()+"  start " + startText+"  end " + endText;
    }
}