RecyclerView使用databinding出现数据闪烁问题

295 阅读3分钟

最初在Recyclerview的Adapter中写的代码如下,代码构建没有问题,可是当刷新界面时会出现界面闪烁以下的问题。

**

@Override
public void onBindViewHolder(@NotNull VH holder, int position) {

    ViewDataBinding binding = baseViewHolder.getBinding();
    BeanViewModel bean = new BeanViewModel(mList.get(position), position);
    binding.setVariable(BR.bean,bean);
       
}

查阅了一下资料发现加入一行代码即可解决问题:

**

binding.executePendingBindings();

完整代码如下:

**

@Override
public void onBindViewHolder(@NotNull VH holder, int position) {

    ViewDataBinding binding = baseViewHolder.getBinding();
    BeanViewModel bean = new BeanViewModel(mList.get(position), position);
    binding.setVariable(BR.bean,bean);
    binding.executePendingBindings();
       
}

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

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); 1 2 3 4 5 解决:以上方法,不知道大家是否可以,但是我测下来还是有点问题,虽然动画间隔设置为了0,但是还是有执行动画的操作,所以简单粗暴的设置方法,直接设置animator为null。简单有效

recyclerView.setItemAnimator(null); 1 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
            }

        }
    })

}

23 扩展一下:在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
     }
 })

28 方法三:使用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)
        }