Android 8.0(Oreo)中SeekBar thumb调用setColorFilter()致全部变色问题
解决Android 8.0上RecyclerView中SeekBar Thumb颜色被统一覆盖的问题
我之前在项目里碰到过一模一样的问题!这确实是Android 8.0(Oreo)系统在SeekBar的Thumb实例复用逻辑上的一个坑,不是你的代码逻辑问题。
问题根源
在Android 8.0及以上版本,系统对SeekBar的Thumb Drawable做了共享实例的优化——也就是说,RecyclerView里的所有LevelSeekBar可能复用了同一个Drawable对象。当你调用getThumb().setColorFilter()时,其实是在修改这个共享的Drawable实例,所以最后一次设置的颜色会覆盖所有SeekBar的Thumb样式,导致全部变成最后一个Item的状态颜色。而8.0以下系统每个SeekBar的Thumb都是独立的Drawable实例,所以不会出现这个问题。
解决思路&代码修改
核心就是打破Drawable的状态共享,为每个LevelSeekBar创建独立的Thumb实例,用mutate()方法就能实现:
修改你的setEnabled方法:
@Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); int color = getResources().getColor(getColorForState(enabled)); // 关键:调用mutate()获取独立的Drawable副本,避免共享状态被修改 Drawable thumb = getThumb().mutate(); thumb.setColorFilter(color, PorterDuff.Mode.SRC_IN); // 重新设置thumb到当前SeekBar setThumb(thumb); }
另外,也可以在LevelSeekBar的构造方法里提前处理,确保每个实例从一开始就持有独立的Thumb:
public LevelSeekBar(Context context, AttributeSet attrs) { super(context, attrs); // 初始化时就为当前实例创建独立的Thumb if (getThumb() != null) { setThumb(getThumb().mutate()); } }
验证方法
你可以在setEnabled方法里打印getThumb().hashCode(),对比Android 8.0和低版本的输出:
- 低版本每个SeekBar的thumb hashCode都不一样,说明是独立实例;
- 8.0版本如果多个SeekBar的thumb hashCode相同,就坐实了共享实例的问题,修改后每个hashCode应该不同。
这样修改后,每个LevelSeekBar的Thumb都是独立的状态,就不会出现颜色被统一覆盖的情况了。
内容的提问来源于stack exchange,提问作者EMon
相关产品推荐
相关产品推荐

