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) // 使当前位置处于最顶端
}
}