ListView内的Android TextView在手动滚动前无法测量正确的高度

1 人关注

我有一个充满多行TextViews的listView。每个TextView都有不同数量的文本。在按下一个按钮后,用户被带到另一个活动中,他们可以改变字体和字体大小。当重新进入Fragment时,如果这些设置发生了变化,listView会被重置,TextViews的尺寸也会改变。

我需要知道这些设置改变后,视图中第一个TextView的测量高度。由于某些原因,测量后的高度一开始是不同的。一旦我手动滚动列表,真正的高度测量就会被记录下来。

Log output:

测量后:电视高度=2036

测量后:电视高度=2036

滚动后:tv高度=7950

最小的代码。

class FragmentRead : Fragment() {
    private var firstVisiblePos = 0
    lateinit var adapterRead: AdapterRead
    lateinit var lvTextList: ListView
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        lvTextList = view.findViewById(R.id.read_listview)
        setListView(lvTextList)
        lvTextList.setOnScrollListener(object : AbsListView.OnScrollListener {
            var offset = 0
            override fun onScrollStateChanged(view: AbsListView, scrollState: Int) {
                if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                    offset = if(lvTextList.getChildAt(0) == null) 0 else lvTextList.getChildAt(0).top - lvTextList.paddingTop
                    println("After scroll: tv height = ${lvTextList[0].height}")
            override fun onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) {
                firstVisiblePos = firstVisibleItem
    /*=======================================================================================================*/
    fun setListView(lv: ListView) {
        adapterRead = AdapterRead(Data.getTextList(), context!!)
        lv.apply {this.adapter = adapterRead}
    /*=======================================================================================================*/
    inline fun <T : View> T.afterMeasured(crossinline f: T.() -> Unit) {
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                if(measuredWidth > 0 && measuredHeight > 0) {
                    println("After measured: tv height = ${lvTextList[0].height}")
                    viewTreeObserver.removeOnGlobalLayoutListener(this)
    /*=======================================================================================================*/
    override fun onStart() {
    if(Settings.settingsChanged) {
        setListView(lvTextList)
        lvTextList.afterMeasured {
                println("After measured: tv height = ${lvTextList[0].height}")

我所尝试的。

我试着用文本和layoutParams设置一个TextView,并按照这里的解释读取高度(在渲染到布局之前获得文本视图的高度),但结果是一样的。测量的高度比我滚动列表后的高度小得多。

我也试着用lvTextList.scrollBy(0,1)来编程滚动列表,以便在读取正确的高度时触发滚动监听器或其他什么东西。

编辑:我在回到碎片后放了一个延迟。

Handler().postDelayed({
println("tv height after delay = ${lvScriptureList[0].height}")}, 1000)

而这报告的高度是正确的。所以我的猜测是,OnGlobalLayoutListener被提前调用了。有什么方法可以解决这个问题吗?

android
listview
scroll
textview
ongloballayoutlistener
Samuel
Samuel
发布于 2020-11-12
1 个回答
Samuel
Samuel
发布于 2020-11-14
已采纳
0 人赞同

下面是我的解决方案。我之所以需要知道TextView的高度,是因为在用户改变设置(例如字体、字号、行距)后,TextView的大小会发生变化。为了返回到TextView之前所在的位置,我需要知道新测量的TextView的高度。然后我就可以根据之前的位置去到同一个位置(或者非常接近),并根据新的高度重新计算。

因此,在改变设置并重新加载Fragment后。

override fun onStart(){
    if(Settings.settingsChanged) {
        setListView(lvTextList)
        lvTextList.afterMeasured {
            lvTextList.post { lvTextList.setSelectionFromTop(readPos, 0) }
            Handler().postDelayed({
                val newOffset = getNewOffset() // Recalculates the new offset based on the last offset and the new TextView height
                lvTextList.post { lvTextList.setSelectionFromTop(readPos, newOffset) }
            }, 500)