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)
}