recycleview的scrollbar的背景
最近遇到了一个问题:需要将一张图片设置为recycleview的scrollbar的背景,直接设置
android:scrollbarThumbVertical="@drawable/icon_scrollbar"
这里的icon_scrollbar为一张切图,但实际上呈现的效果是如下图:
可以看到图片被拉伸了
而当这张图片放在android项目drawable下,用layer-list进行包装,
android:scrollbarThumbVertical="@drawable/shape_scrollbar_thumb"
shape_scrollbar_thumb 内容为:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:height="82dp"
android:drawable="@drawable/icon_scrollbar"
/>
</layer-list>
呈现的效果就满足需求:
就这一现象,引发出以下几个知识点?
1、drawble是什么?
Android把可绘制的对象抽象为Drawable,不同的图形图像资源就代表着不同的drawable类型。Android FrameWork提供了一些具体的Drawable实现,通常在代码中都不会直接接触Drawable的实现类。
在实际的开发过程中,会把使用到的资源都放置在res/drawable目录,剩下的工作交给Android SDK 就行了,当需要使用图片资源的时候,可以使用@drawable标志在xml中引用drawable资源就行,也可以在代码中使用id引用这些drawable资源。
2、从layer-list获取drawble对象和直接从图片获取drawble的区别?
当你直接从drwable获取图片时,返回的是BitmapDrawable,recycleview会根据item的多少改变高度,所以图片会被拉伸。而用layer-list包装一层,返回的layerDrawable,同样拉伸时,整个drawable数组会被拉伸,但此时图片是item里面的一个子元素已经被指定了高度,图片则不会被拉伸。简单来说即:layer-list返回的是个整体的viewGroup,把layerlist看成一个关于图片的framelayout。每一子项都可以控制位置,间距,以及大小,如果里面的子控件自身指定了宽高等特性就由自身决定,如果没有指定,则取决于外部。
3、RecycleView源码,默认情况下滑动时,thumb的高度或者宽度是根据整个列表的长度决定的,如果不自定义scrollbar,则可能会出现拉伸
新的问题又出来了
虽然背景是不会拉伸了,但会发现scrollbar是不能滑动到底部和顶部的。如果需要是可以动态改变Scrollbar的高度,以及滑动的位置。
`
@Override
public int computeVerticalScrollOffset() {
if (mLayout == null) {
return 0;
}
return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
}
@Override
public int computeVerticalScrollExtent() {
if (mLayout == null) {
return 0;
}
return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
}
@Override
public int computeVerticalScrollRange() {
if (mLayout == null) {
return 0;
}
return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
}
![] 计算scrollbar滑动位置的原理如图 scrollbar相关源码的三个变量需要注意:offset、extent、range
/**
* 计算竖直方向总的滑动区域
*/
override fun computeVerticalScrollRange(): Int {
if (trackLength == -1) {
trackLength = this.measuredHeight
}
return trackLength
}
/**
* 计算当前这次竖直方向滑动的位移
*/
override fun computeVerticalScrollOffset(): Int {
getHeights()
val highestVisiblePixel = super.computeVerticalScrollOffset()
return computeScrollOffset(highestVisiblePixel)
}
private fun computeScrollOffset(highestVisiblePixel: Int): Int {
/**
* 不可见区域的列表(可以滑动的总长度)
*/
val invisiblePartOfRecyclerView: Int = totalLength - trackLength
/**
* 剩余需要滑动的长度
*/
val scrollAmountRemaining = invisiblePartOfRecyclerView - highestVisiblePixel
return when {
invisiblePartOfRecyclerView == scrollAmountRemaining -> {
0
}
scrollAmountRemaining > 0 -> {
((trackLength - thumbLength) / (invisiblePartOfRecyclerView.toFloat() / highestVisiblePixel)).roundToInt()
}
else -> {
trackLength - thumbLength
}
}
}
/**
* thumb的长度,当前固定为thumbLength
*/
override fun computeVerticalScrollExtent(): Int {
return thumbLength
}
部分源码可参考:github.com/yuanting201…