Android 按字母排序的通讯录

Android 字母排序通讯录

效果图:
这里写图片描述

代码部门:

Activity

class ContactViewActivity: AppCompatActivity(){

    private var mAdater: ContactSortAdapter? = null
    private var mDataList: MutableList<SortModel>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sort_recycler)
        initView()
        initData()
    }

    fun initView(){
        ed_search?.addTextChangedListener(object : TextWatcher {

            override fun onTextChanged(s: CharSequence, start: Int, before: Int,
                                       count: Int) {
                if (TextUtils.isEmpty(s.toString().trim())){
                    contact_view?.initData(mDataList)
                    mAdater?.initData(mDataList)
                }else{
                    mAdater?.initData(contact_view?.updateData(s.toString()))
                }

            }

            override fun beforeTextChanged(s: CharSequence, start: Int,
                                           before: Int, count: Int) {

            }

            override fun afterTextChanged(arg0: Editable) {

            }
        })

    }

    fun initData(){
        val arrayData = arrayOf("a", "bd", "ced", "de", "as", "东皇太一","宫本武藏","王昭君","李元芳","刘禅","后裔","许爱明","无名","流海"
                ,"亚瑟","吕布", "秋雅", "夏洛", "公孙离", "张良", "孙尚香", "我", "你", "啊", "哈哈", "嘿"
                ,"无名","流海","亚瑟","吕布", "夏洛", "公孙离", "张良", "孙尚香","无名","流海","亚瑟","吕布", "刘备", "夏洛", "公孙离", "张良", "孙尚香"
                ,"无名","流海","亚瑟","吕布", "秋雅", "夏洛", "公孙离", "张良", "孙尚香","无名","流海","亚瑟","吕布", "秋雅", "夏洛", "公孙离", "张良", "孙尚香")
        var data = mutableListOf<String>()
        for (i in arrayData){
            data.add(i)
        }
        mAdater = ContactSortAdapter()
        RecyclerViewUtil.initNoDecoration(this,contact_view?.getRecycler(),mAdater)
        mDataList = contact_view?.sortData(data)
        contact_view?.initData(mDataList)
        mAdater?.initData(mDataList)

    }

}

布局

<?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:background="@color/white"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/ll_top_search"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:gravity="center"
        android:background="@color/color_0">

        <EditText
            android:id="@+id/ed_search"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:background="@drawable/radius_bg"
            android:drawableLeft="@mipmap/iv_search_normal"
            android:drawablePadding="4dp"
            android:paddingLeft="4dp"
            android:hint="请输入关键字"
            android:textSize="14dp"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"/>

    </LinearLayout>

    <com.zs.various.view.ContactRecyclerView
        android:id="@+id/contact_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/ll_top_search"/>

</RelativeLayout>

Activity 页面部分整体是一个RecyclerView,上面是搜索的EditText,主要的逻辑都在自定义的控件里面处理,activity主要是获取数据,然后调用排序方法,把数据根据字母顺序重新排序,填充到adapter中。

class ContactSortAdapter : RecyclerView.Adapter<ContactSortAdapter.ContactHolder>() {

    var mData: MutableList<SortModel>? = null

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ContactHolder {
        var view = View.inflate(parent?.context, R.layout.adapter_sort,null)
        return ContactHolder(view)
    }

    override fun getItemCount(): Int {
        return if (mData == null) 0 else mData!!.size
    }

    fun initData(data: MutableList<SortModel>?){
        this.mData = data
        notifyDataSetChanged()
    }

    override fun onBindViewHolder(holder: ContactHolder?, position: Int) {
        holder?.bindData(position)
    }


    inner class ContactHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
        fun bindData(position: Int) = with(itemView) {
            var sortModel = mData!![position]
            name?.text = sortModel.name
            if (!compareSection(position)){
                tv_letter?.visibility = View.VISIBLE
                tv_letter?.text = sortModel.letter
                line_view?.visibility = View.GONE
            }else{
                tv_letter?.visibility = View.GONE
                line_view?.visibility = View.VISIBLE
            }
        }
    }

    fun compareSection(position: Int): Boolean {
        return if (position == 0) {
            false
        } else {
            val current = getSectionForPosition(position)
            val previous = getSectionForPosition(position - 1)
            current == previous
        }

    }

    // 获取当前位置的首字母(int表示ascii码)
    fun getSectionForPosition(position: Int): Int {
        return mData!![position].letter[0].toInt()
    }

    // 获取字母首次出现的位置
    fun getPositionForSection(section: Int): Int {
        for (i in 0 until itemCount) {
            val s = mData!![i].letter
            val firstChar = s.toUpperCase()[0]
            if (firstChar.toInt() == section) {
                return i
            }
        }
        return -1
    }

}

每个条目都有显示字母的title,adapter中控制显示,主要的判断逻辑是,判断上一个数据中名字的首字母是否和本条目的首字母相同,不相同显示title,否则不显示。

自定义 ContactRecyclerView

布局

<?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">

    <RelativeLayout
        android:id="@+id/rl_container_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true">

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

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            />

        <TextView
            android:id="@+id/tv_letter_show"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_centerInParent="true"
            android:background="@color/hint_color"
            android:gravity="center"
            android:textColor="@color/white"
            android:textSize="40dp"
            android:visibility="gone" />

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

        <com.zs.various.view.SideBarView
            android:id="@+id/view_sidebar"
            android:layout_width="30dp"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="20dp"
            android:layout_marginTop="20dp">

        </com.zs.various.view.SideBarView>

    </RelativeLayout>

</RelativeLayout>
/**
     * 数据排序
     */
    fun sortData(data: MutableList<String>): MutableList<SortModel> {
        val list = mutableListOf<SortModel>()
        for (i in data.indices) {
            val sm = SortModel()
            sm.name = data[i]
            val pinyin = mParser.getSelling(data[i])
            val sortString = pinyin.substring(0, 1).toUpperCase()
            if (sortString.matches("[A-Z]".toRegex())) {
                sm.letter = sortString
            } else {
                sm.letter = "#"
            }
            list.add(sm)
        }
        Collections.sort(list, PinyinComparator())
        return list
    }

把数据进行字母排序

init {
        LayoutInflater.from(context).inflate(R.layout.contact_list_layout, this)
        ll_top_title = findViewById(R.id.ll_top_title)
        tv_letter = findViewById(R.id.tv_letter)
        view_sidebar?.setLetterTouchListener(this)
        recycler_view?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                mLetterHeight = ll_top_title?.height!!
            }

            override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                mLayoutManager = recyclerView?.layoutManager as LinearLayoutManager
                //找到列表下一个可见的View
                var view = mLayoutManager?.findViewByPosition(mCurrentPosition + 1)
                // 检查列表中的letter布局是否显示
                if (view != null && view.top <= mLetterHeight && view?.findViewById<TextView>(R.id.tv_letter)?.visibility == View.VISIBLE){
                    //被顶掉的效果
                    ll_top_title?.y = (-(mLetterHeight - view.top)).toFloat()
                }else{
                    ll_top_title?.y = 0f
                }
                //判断是否需要更新悬浮条
                if (mCurrentPosition != mLayoutManager?.findFirstVisibleItemPosition()){
                    ll_top_title?.y = 0f
                    updateLetter()
                }
            }

        })
    }

RecyclerView添加滑动监听事件,处理title顶出的效果,在RecycerView外层添加一层title,recyclerview滑动过程中计算adapter中item的滑动位置,当到达外层title的高度时,动态改变外层title的位置,直到外层title被全部顶出,再把它复位,刷新外层title的字母。
右侧的SideBarView控件控制列表的快速滑动,在ContactRecyclerView中添加触摸监听,处理点击字母和滑动字母时的回调事件。

override fun setLetterVisibility(visibility: Int) {
        tv_letter_show?.visibility = visibility
    }

    override fun setLetter(letter: String?) {
        if (TextUtils.isEmpty(letter) || letter!!.isEmpty()){
            return
        }
        tv_letter_show?.text = letter
        var position = getPositionForSection(letter!![0].toInt())
        if (position != -1) {
            updateLetter()
            mLetterHeight = ll_top_title?.height!!
            mLayoutManager?.scrollToPositionWithOffset(position,0) // 使当前位置处于最顶端
        }
    }

源码地址

github源码地址

猜你喜欢

转载自blog.csdn.net/QQ55214/article/details/81204402