Android:处理RecyclerView局部刷新视图导致闪烁,以及TextView多行设置ellipsize失效的问题

27 篇文章 2 订阅
7 篇文章 1 订阅

RecyclerView局部刷新视图导致闪烁

问题:RecyclerView 调用notifyItemChanged()方法更新单个Item时,此Item有闪烁
因为RecyclerView 默认实现了一个DefaultItemAnimator,如果RecyclerView 的item布局过于复杂且包含 重新测量等复杂步骤的时候,就会产生闪烁。所以最好的解决办法就是不产生动画效果。
看到网上有很多的设置方法。最常见的是关闭,是这么写的。

 recyclerView.getItemAnimator().setAddDuration(0);
recyclerView.getItemAnimator().setChangeDuration(0);
recyclerView.getItemAnimator().setMoveDuration(0);
recyclerView.getItemAnimator().setRemoveDuration(0);
((SimpleItemAnimator)recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);

解决:以上方法,不知道大家是否可以,但是我测下来还是有点问题,虽然动画间隔设置为了0,但是还是有执行动画的操作,所以简单粗暴的设置方法,直接设置animator为null。简单有效

recyclerView.setItemAnimator(null);

TextView设置多行后ellipsize失效的问题

问题:我这里要求是设置textview最多展示两行,然后多出来的中间显示省略号,xml布局设置了maxlines = 2,ellipsize="middle"但是没有效果。。。。。
没办法只能自己实现了。最终决定借助ViewTreeObserver来实现这个效果
当监听到布局完成之后,获取到textView当前的总行数。判断如果大于需要限定的行数,就开始处理。首先使用getLineEnd获取指定行最后一个字符后的文本偏移量(文字数量)。根据获取到的文字数量截取字符串,然后重新设置textview就可以了。
解决:具体代码如下所示:

    open fun dealMiddleNoEffect(tv: TextView, lines: Int = 2) {
        val viewTreeObserver = tv.viewTreeObserver
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                if (viewTreeObserver.isAlive) {
                    //回调过来之后直接移除监听
                    viewTreeObserver.removeOnGlobalLayoutListener(this)
                }
                //处理判断大于多少行时进行改变
                if (viewTreeObserver.isAlive && tv.lineCount > lines) {
                    val lineEnd = tv.layout.getLineEnd(lines)
                    val allText = tv.text
                    val text = lineEnd.let {
                        val i = (it - 3) / 2
                        "${allText.substring(0, i)}...${allText.substring(allText.length - i + 3, allText.length)}"
                    }
                    tv.text = text
                }

            }
        })

    }

扩展一下:在onCreate中获取view的宽高?
方法一:使用MeasureSpec进行测量,如下所示:

fun calculateViewWidthAndHeight(view: View): Pair<Int, Int> {
    val measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
    view.measure(measureSpec, measureSpec)
    return view.measuredWidth to view.measuredHeight
}

方法二:使用ViewTreeObserver 来获得宽高,当获得正确的宽高后,移除观察者,否则会调会多次执行

//使用 addOnGlobalLayoutListener
val observer = view.viewTreeObserver
 observer.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
     override fun onGlobalLayout() {
         //判断ViewTreeObserver 是否alive,如果存活的话移除这个观察者
         if (observer.isAlive) {
             observer.removeOnGlobalLayoutListener(this)
             //获得宽高
             val viewWidth = view.measuredWidth
             val viewHeight = view.measuredHeight
             //todo 逻辑处理
             
         }
     }
 })
 //或者使用  在执行onDraw之前已经执行了onLayout()和onMeasure(),可以得到宽高了
val observer = view.viewTreeObserver
 observer.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
     override fun onPreDraw(): Boolean {
         if (observer.isAlive) {
             observer.removeOnPreDrawListener(this)
         }
         //获得宽高
         val viewWidth = view.measuredWidth
         val viewHeight = view.measuredHeight
         return true
     }
 })

方法三:使用post函数进行获取,runnable对象会在view的measure、layout后进行触发,比如

post {
            attackView = AttackView(context).apply {
                val lp = LayoutParams(this@BowShooterPanel.width / 2, LayoutParams.MATCH_PARENT)
                layoutParams = lp
                lp.gravity = Gravity.END
                setOnAngleChangeListener(AttackHandler(engine))
            }
            attackView.setBackgroundResource(R.color.seed)
            addView(attackView)
        }
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pumpkin的玄学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值