如何获取滚动位置相对于当前文本视图开始的偏移量?
How to get the offset of scroll position relative to the current text view's start?
我有一个包含 TextView 的 RecyclerView。 TextView 的数量可以变化,它们的大小也可以变化,并且可以动态更改。
当用户滚动到列表中的某个位置并退出应用程序时,我希望能够在下一个会话中return到那个确切位置。
为此,我需要知道从视图中当前 TextView 开始的位置滚动过去了多少像素以及滚动的当前位置。例如,如果用户在视图中有第三个 TextView 并从该 TextView 开始的位置向下滚动 100 像素,我将能够使用 scrollToPositionWithOffset(2, 100) return 到这个位置。如果 TextView 更改大小(由于字体更改),我还可以通过使用 TextView 的高度计算偏移百分比来 return 到同一位置。
问题是,我无法在任何准确的庄园中获得偏移值。
我知道我可以使用 对滚动的 Y 值进行 运行 计算,但这并没有告诉我 TextView 实际开始的位置。当用户滚动到任何 TextView 的开头并记录那里的 Y 位置时,我可以听到,但这在快速滚动时会不准确。
有没有更好的方法?
不要使用以像素为单位的位置,使用视图的索引。使用布局管理器的 findFirstVisibleItemPosition
或 findFirstCompletelyVisibleItemPosition
.
这是一个非常受欢迎的问题,尽管思考和搜索像素而不是索引可能更直观。
Get visible items in RecyclerView
不信任像素的一个很好的理由是它在某些情况下没有用,例如旋转屏幕、resizeing/splitting 应用大小以适应并排放置其他应用、可折叠手机和更改文本/屏幕分辨率。
我通过转换为 ListView 解决了这个问题:
lateinit var adapterRead: AdapterRead // Custom Adapter
lateinit var itemListView: ListView
/*=======================================================================================================*/
// OnViewCreated
itemListView = view.findViewById(R.id.read_listview)
setListView(itemListView)
// Upon entering this Fragment, will automatically scroll to saved position:
itemListView.afterMeasured {
scrollToPosition(itemListView, getPosition(), getOffset())
}
itemListView.setOnScrollListener(object : AbsListView.OnScrollListener {
private var currentFirstVisibleItem = 0
var offset = 0
override fun onScrollStateChanged(view: AbsListView, scrollState: Int) {
// When scrolling stops, will save the current position and offset:
if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
offset = if(itemListView.getChildAt(0) == null) 0 else itemListView.getChildAt(0).top - itemListView.paddingTop
saveReadPosition(getReadPosition(itemListView), offset)
}
}
override fun onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) {
currentFirstVisibleItem = firstVisibleItem
}
})
/*=======================================================================================================*/
// Thanks to https://antonioleiva.com/kotlin-ongloballayoutlistener/ for this:
inline fun <T : View> T.afterMeasured(crossinline f: T.() -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if(measuredWidth > 0 && measuredHeight > 0) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
f()
}
}
})
}
/*=======================================================================================================*/
fun setListView(lv: ListView) {
adapterRead = AdapterRead(list, context!!)
lv.apply {this.adapter = adapterRead}
}
/*=======================================================================================================*/
fun scrollToPosition(lv: ListView, position: Int, offset: Int) {
lv.post { lv.setSelectionFromTop(position, offset) }
}
/*=======================================================================================================*/
fun saveReadPosition(position: Int, offset: Int) {
// Persist your data to database here
}
/*=======================================================================================================*/
fun getPosition() {
// Get your saved position here
}
/*=======================================================================================================*/
fun getOffse() {
// Get your saved offset here
}
我有一个包含 TextView 的 RecyclerView。 TextView 的数量可以变化,它们的大小也可以变化,并且可以动态更改。
当用户滚动到列表中的某个位置并退出应用程序时,我希望能够在下一个会话中return到那个确切位置。
为此,我需要知道从视图中当前 TextView 开始的位置滚动过去了多少像素以及滚动的当前位置。例如,如果用户在视图中有第三个 TextView 并从该 TextView 开始的位置向下滚动 100 像素,我将能够使用 scrollToPositionWithOffset(2, 100) return 到这个位置。如果 TextView 更改大小(由于字体更改),我还可以通过使用 TextView 的高度计算偏移百分比来 return 到同一位置。
问题是,我无法在任何准确的庄园中获得偏移值。
我知道我可以使用
有没有更好的方法?
不要使用以像素为单位的位置,使用视图的索引。使用布局管理器的 findFirstVisibleItemPosition
或 findFirstCompletelyVisibleItemPosition
.
这是一个非常受欢迎的问题,尽管思考和搜索像素而不是索引可能更直观。
Get visible items in RecyclerView
不信任像素的一个很好的理由是它在某些情况下没有用,例如旋转屏幕、resizeing/splitting 应用大小以适应并排放置其他应用、可折叠手机和更改文本/屏幕分辨率。
我通过转换为 ListView 解决了这个问题:
lateinit var adapterRead: AdapterRead // Custom Adapter
lateinit var itemListView: ListView
/*=======================================================================================================*/
// OnViewCreated
itemListView = view.findViewById(R.id.read_listview)
setListView(itemListView)
// Upon entering this Fragment, will automatically scroll to saved position:
itemListView.afterMeasured {
scrollToPosition(itemListView, getPosition(), getOffset())
}
itemListView.setOnScrollListener(object : AbsListView.OnScrollListener {
private var currentFirstVisibleItem = 0
var offset = 0
override fun onScrollStateChanged(view: AbsListView, scrollState: Int) {
// When scrolling stops, will save the current position and offset:
if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
offset = if(itemListView.getChildAt(0) == null) 0 else itemListView.getChildAt(0).top - itemListView.paddingTop
saveReadPosition(getReadPosition(itemListView), offset)
}
}
override fun onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) {
currentFirstVisibleItem = firstVisibleItem
}
})
/*=======================================================================================================*/
// Thanks to https://antonioleiva.com/kotlin-ongloballayoutlistener/ for this:
inline fun <T : View> T.afterMeasured(crossinline f: T.() -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if(measuredWidth > 0 && measuredHeight > 0) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
f()
}
}
})
}
/*=======================================================================================================*/
fun setListView(lv: ListView) {
adapterRead = AdapterRead(list, context!!)
lv.apply {this.adapter = adapterRead}
}
/*=======================================================================================================*/
fun scrollToPosition(lv: ListView, position: Int, offset: Int) {
lv.post { lv.setSelectionFromTop(position, offset) }
}
/*=======================================================================================================*/
fun saveReadPosition(position: Int, offset: Int) {
// Persist your data to database here
}
/*=======================================================================================================*/
fun getPosition() {
// Get your saved position here
}
/*=======================================================================================================*/
fun getOffse() {
// Get your saved offset here
}