自定义View核心逻辑+算法[城市导航]

导航View的事件分发逻辑

在这里插入图片描述

城市导航图示

在这里插入图片描述

城市导航控件,是结合事件分发机制及功能需求,来实现绘制出的自定义View。

当在左侧城市列表页面滑动浏览时,右侧导航省、直辖市列表会跟随联动,并根据左侧滑动区间显示选中状态。右侧通过手势滑动,左侧会有View提示及城市列表联动。

事件消费,处理绘制+手势

在自定义该城市导航View中,主要涉及对事件消费处理。及绘制逻辑处理。
主要伪代码如下 ~


/** 自定义view,城市导航组件 - CityViewIndicator.kt */
class CityViewIndicator : View {
    .... ....
	... ...
    /** 初始化操作 */
    private fun initView() {
        // 抗锯齿
        paint.isAntiAlias = true
    }
	... ... .
    /** 执行绘制 */
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.let {
            if (list.size > 0) {
                onIndicatorDraw(it)
            }
        }
    }
	// 绘制逻辑处理
    private fun onIndicatorDraw(canvas: Canvas) {...}
	// 事件消费处理
    override fun onTouchEvent(event: MotionEvent?): Boolean {...}
... ... ...
}

事件消费处理

在该事件消费处理中,核心算法是选择在哪里(什么方法中)拦截用户操作事件。且如何将手指滑动时的坐标转化为列表中省、直辖市的下标。

事件的消费处理核心算法写在dispatchTouchEvent中也是ok的。鉴于CityViewIndicator作为View层次中最底层且需要捕获并消费事件的控件,在onTouchEvent中处理消费逻辑,是更容易理解和考虑到的。

13行,事件(手指触到)y坐标 与 当前CityViewIndicator的高度比值。计算出当前省、直辖市对应List坐标。
11行oldCheckIndex预先记录下已选中省、直辖市List对应坐标。13行计算出的下标在18行赋值给oldCheckIndex,且赋值前会在15行判断是否是相同操作,以防止重复绘制。
在这里插入图片描述

/** 事件消费处理 */
override fun onTouchEvent(event: MotionEvent?): Boolean {
        event?.let {
            when(it.action) {
                MotionEvent.ACTION_DOWN -> {
                	// 导航弹窗提示-显示 [对外开放接口]
                    this.indicatorTouchListener?.winDialogChange(true)
                }
                MotionEvent.ACTION_MOVE -> {
                    // 滑动
                    var oldCheckIndex = checkedIndex // 防止重复
                    // 当手指摁下去时,触摸到的坐标 转化为 选中item的下标
                    val localIndex = (event.y / viewHeight * list.size).toInt()

                    if(oldCheckIndex != localIndex) { // 防重复判断
                        // 向外传递数据,传递省份-直辖市 [对外开放接口]
                        this.indicatorTouchListener?.valueDelivery(list[localIndex])
                        checkedIndex = localIndex
                        invalidate() // 刷新 [只执行onDraw,不执行onLayout]
                    }
                }
                MotionEvent.ACTION_UP -> {
                	// 导航弹窗提示-隐藏 [对外开放接口]
                    this.indicatorTouchListener?.winDialogChange(false) 
                }
            }
        }
        return true
    }

绘制导航文本

在该绘制逻辑处理中,核心的算法逻辑在如何绘制省、直辖市的文本列表。

准备 ~
1,创建Paint画笔实例;
2,通过canvas画布执行绘制;

文本绘制逻辑 ~
绘制当前省、直辖市列表文本思维 :
自定义View —— CityViewIndicator本身一定宽度条件下,开始绘制(画)起笔[Paint]应该落在画布[Canvas]什么位置[坐标]
16行,通过paint.measureText(txt)计算文本宽度,使用自定义View本身宽度减去文本宽度。此时得到的值,是文本在view中左右未占据的空间宽度之和。那么一分为二的值,则是左侧下笔的起始坐标。且文本始终会居中显示~
在这里插入图片描述

private fun onIndicatorDraw(canvas: Canvas) {
        itemHeight = viewHeight / list.size // 获取每条item,应该分配到的高度
        list.forEachIndexed { index, txt ->
            if (index == checkedIndex) {
                paint.color = checkedTxtColor
                paint.textSize = checkedTxtSize
                paint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD))
            } else {
                paint.color = nocheckedTxtColor
                paint.textSize = nocheckedTxtSize
                paint.setTypeface(Typeface.DEFAULT)
            }
            // 开始绘制文本
            val txtX = (viewWidth - paint.measureText(txt))/2
            val txtY = ((1 + index) * itemHeight).toFloat()
            canvas.drawText(txt, txtX, txtY, paint)
        }
        canvas.drawARGB(80, 211, 211, 211) // 绘制透明灰色背景
    }

事件分发机制

跟随本篇博客了解事件分发机制,请到这里。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值