微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

除非手动滚动,否则ListView中的Android TextView不能测量正确的高度

如何解决除非手动滚动,否则ListView中的Android TextView不能测量正确的高度

我有一个用多行TextView填充的listView。每个TextView都有不同数量的文本。按下按钮后,用户将被带到另一个活动,他们可以在其中更改字体和字体大小。重新进入片段后,如果这些设置已更改,则将重置listView并更改TextViews的尺寸。

更改这些设置后,我需要知道视图中第一个TextView的测量高度。由于某种原因,测量后的高度首先会有所不同。手动滚动列表后,便会记录实际的高度测量值。

日志输出

测量后:电视高度= 2036

测量后:电视高度= 2036

滚动后:电视高度= 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)
                    f()
                }
            }
        })
    }
    /*=======================================================================================================*/
    override fun onStart() {
    if(Settings.settingsChanged) {
        setListView(lvTextList)
        lvTextList.afterMeasured {
                println("After measured: tv height = ${lvTextList[0].height}")
            }
        }
    }
}

我尝试过的事情:

我尝试使用text和layoutParams设置TextView并按此处说明的{Getting height of text view before rendering to layout)读取高度,但是结果是相同的。测量的高度比滚动列表后要小得多。

我还尝试使用lvTextList.scrollBy(0,1)以编程方式滚动列表,以触发滚动侦听器或在读取正确高度时触发的其他任何事件。

编辑:我回到片段后放了一个延迟:

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

这将报告正确的高度。所以我的猜测是OnGlobalLayoutListener被提早调用了。有什么办法解决吗?

解决方法

这是我的解决方案。我需要知道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)
        }
    }
}

由于某种原因,在计划延迟之前,我必须先滚动到某个位置,所以我只是滚动到TextView的开头。

500毫秒是愚蠢的,只是一个估计,但它可以工作。实际上,它在手机上的工作时间为100毫秒,但我想确保跨设备成功的机会更大。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。