android自定义View之从入门到放弃(六)实现字母索引条

周末无聊过来加班,正好这周将字母索引条的功能实现了,然后趁今天无聊把它整理一下
废话不多说,上效果图:
在这里插入图片描述
在这里插入图片描述
实现的功能:
手指按下右侧字母索引条,索引条被禁发生变化 显示当前按下的字母,以及列表跳转到该字母的位置
ok,那我们就来一点点的实现
首先 layout使用

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
  <include layout="@layout/titlebar"/>
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/user_refre"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/user_recycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

    <com.example.smartchat.widget.SlideBar

        android:id="@+id/slidebar"
        android:layout_width="20dp"
        android:layout_height="match_parent"
        android:layout_gravity="right"
         />

        <TextView
            android:visibility="gone"
            android:layout_gravity="center"
            android:textSize="18sp"
            android:padding="8dp"
            android:background="@color/blue"
            android:id="@+id/hinttext"
            android:textColor="#FFFFFF"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </FrameLayout>
</LinearLayout>

从代码中我们可以看出 我指定了自定义SlideBar的宽度位指定值,高度是match_parent
然后我们来一点一点的实现功能吧

class SlideBar(context: Context?, attrs: AttributeSet?=null) : View(context, attrs) {
     var singleHeight = 0f
     var baseLine = 0f
     val paint = Paint()

    companion object{
        private val SELECTIONS = arrayOf("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")
    }
      init {
          paint.apply {
             // setAntiAlias(true)
              color = resources.getColor(com.example.smartchat.R.color.gray)
              textSize = sp(12).toFloat()
              textAlign = Paint.Align.CENTER
          }
      }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {

        singleHeight = h *1.0f/SELECTIONS.size

      //  val fontMetrics = paint.fontMetrics  //拿到绘制的文本高度
//        //计算绘制文本高度
//        val textHeight = fontMetrics.descent - fontMetrics.ascent
//        baseLine = singleHeight/2 +(textHeight/2 - fontMetrics.descent)

    }


    override fun onDraw(canvas: Canvas) {
        //绘制所有的字母
        val x = width *1.0f/2
        var y = singleHeight
        SELECTIONS.forEach {
            canvas.drawText(it,x,y,paint)
            y += singleHeight

        }

    }



}

以上就可以实现A-Z子母索引的显示了,其中我们在onSizeChanged()中拿到了当前控件的高度,然后除以a-z的个数就可以算出一个字母所占的高度,x为宽度的一半 就拿到了x,y就可以将他们进行绘制了

override fun onTouchEvent(event: MotionEvent): Boolean {
        when(event.action){
            MotionEvent.ACTION_DOWN->{
                setBackgroundResource(com.example.smartchat.R.drawable.bg_slide_bar)
                
                val index = getTouchIndex(event)
                val firstLetters = SELECTIONS[index]   //拿到当前字母
            }
           
            MotionEvent.ACTION_UP->{
                setBackgroundColor(Color.TRANSPARENT)
               
            }
        }
        return true
    }

 private fun getTouchIndex(event: MotionEvent): Int {  //获取当前下标
       var index:Int = (event.y/singleHeight).toInt()
        //范围控制
        if(index<0){
            index=0
        }else if(index>= SELECTIONS.size){
            index = SELECTIONS.size-1
        }
        return index
    }

然后我们在onTouchEvent中 根据用户的手势进行判断,当用户按下时修改当前自定义控件的背景颜色,当用户手抬起时又恢复到之前的透明色,然后根据我们自定义控件所占的高度在除以一个所占的高度就可以拿到当前位置的索引值,根据索引值就可以拿到当前手指按下时选中的是哪一个字母


var onSelectionChangedListener:OnSectionChangedListener?= null
interface OnSectionChangedListener{
        fun onSectionChanged(fiestLetters:String)
        fun onSlideFinish()
    }



  override fun onTouchEvent(event: MotionEvent): Boolean {
        when(event.action){
            MotionEvent.ACTION_DOWN->{
                setBackgroundResource(com.example.smartchat.R.drawable.bg_slide_bar)
                //找到点击的字母
                val index = getTouchIndex(event)
                val firstLetters = SELECTIONS[index]   //拿到当前字母
                onSelectionChangedListener?.onSectionChanged(firstLetters)

            }
            MotionEvent.ACTION_MOVE->{
                val index = getTouchIndex(event)
                val firstLetters = SELECTIONS[index]   //拿到当前字母
                onSelectionChangedListener?.onSectionChanged(firstLetters)
            }
            MotionEvent.ACTION_UP->{
                setBackgroundColor(Color.TRANSPARENT)
                onSelectionChangedListener?.onSlideFinish()
            }
        }
        return true
    }

然后我们在自定义接口回调 这样在我们使用该控件的activity/fragment中就可以进行事件回调,拿到当前的字母
完整的的自定义View的代码为:

class SlideBar(context: Context?, attrs: AttributeSet?=null) : View(context, attrs) {
     var singleHeight = 0f
     var baseLine = 0f
     val paint = Paint()

    var onSelectionChangedListener:OnSectionChangedListener?= null
    companion object{
        private val SELECTIONS = arrayOf("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")
    }
      init {
          paint.apply {
             // setAntiAlias(true)
              color = resources.getColor(com.example.smartchat.R.color.gray)
              textSize = sp(12).toFloat()
              textAlign = Paint.Align.CENTER
          }
      }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {

        singleHeight = h *1.0f/SELECTIONS.size

      //  val fontMetrics = paint.fontMetrics  //拿到绘制的文本高度
//        //计算绘制文本高度
//        val textHeight = fontMetrics.descent - fontMetrics.ascent
//        baseLine = singleHeight/2 +(textHeight/2 - fontMetrics.descent)

    }


    override fun onDraw(canvas: Canvas) {
        //绘制所有的字母
        val x = width *1.0f/2
        var y = singleHeight
        SELECTIONS.forEach {
            canvas.drawText(it,x,y,paint)
            y += singleHeight

        }

    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when(event.action){
            MotionEvent.ACTION_DOWN->{
                setBackgroundResource(com.example.smartchat.R.drawable.bg_slide_bar)
                //找到点击的字母
                val index = getTouchIndex(event)
                val firstLetters = SELECTIONS[index]   //拿到当前字母
                onSelectionChangedListener?.onSectionChanged(firstLetters)

            }
            MotionEvent.ACTION_MOVE->{
                val index = getTouchIndex(event)
                val firstLetters = SELECTIONS[index]   //拿到当前字母
                onSelectionChangedListener?.onSectionChanged(firstLetters)
            }
            MotionEvent.ACTION_UP->{
                setBackgroundColor(Color.TRANSPARENT)
                onSelectionChangedListener?.onSlideFinish()
            }
        }
        return true
    }

    private fun getTouchIndex(event: MotionEvent): Int {  //获取当前下标
       var index:Int = (event.y/singleHeight).toInt()
        //范围控制
        if(index<0){
            index=0
        }else if(index>= SELECTIONS.size){
            index = SELECTIONS.size-1
        }
        return index
    }

    interface OnSectionChangedListener{
        fun onSectionChanged(fiestLetters:String)
        fun onSlideFinish()
    }

}

然后就是在当前使用该控件中的引用

    slidebar.onSelectionChangedListener = object :SlideBar.OnSectionChangedListener{
            override fun onSlideFinish() {
                hinttext.visibility = View.GONE
            }

            override fun onSectionChanged(fiestLetters: String) {
                hinttext.visibility = View.VISIBLE
                hinttext.text = fiestLetters
                user_recycler.smoothScrollToPosition(getPosition(fiestLetters))//平滑的滑动到具体位置
            }

        }

   private fun getPosition(fiestLetters: String): Int =//获取当前位置
         presenter.userbeanItem.binarySearch {
                 userbeanItem->userbeanItem.firstLetter.minus(fiestLetters[0])
         }

欧克,这就完成了对于字母的索引条的实现了

发布了43 篇原创文章 · 获赞 60 · 访问量 6746

猜你喜欢

转载自blog.csdn.net/yuhang01/article/details/103325044