如何获取滚动位置相对于当前文本视图开始的偏移量?

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 位置时,我可以听到,但这在快速滚动时会不准确。

有没有更好的方法?

不要使用以像素为单位的位置,使用视图的索引。使用布局管理器的 findFirstVisibleItemPositionfindFirstCompletelyVisibleItemPosition.

这是一个非常受欢迎的问题,尽管思考和搜索像素而不是索引可能更直观。

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
}