根据内容设置 ViewPager2 的高度无法正常工作
Setting the height of the ViewPager2 based on its content is not working properly
我有一个ViewPager2
with TabLayout
. I'm trying to set the height of the ViewPager2
dynamically based on its content. I have checked some related questions like and this。实际上,后者对我有帮助,但无法正常工作。当我导航到 DetailsFragment
时,我从网络获取数据如下:
DetailFragmentViewModel
private val _advert = MutableLiveData<Resource<DetailAdvert>>()
val advert: LiveData<Resource<DetailAdvert>> get() = _advert
init {
savedStateHandle.get<Int>("id")?.let {
getAdvert(it)
}
}
private fun getAdvert(id: Int) {
_advert.value = Resource.Loading()
viewModelScope.launch(Dispatchers.Main) {
_advert.value = repo.advert(id)
}
}
我在 DetailsFragment
中观察 advert
LiveData
:
DetailsFragment
mViewModel.advert.observe(viewLifecycleOwner) {
when (it) {
is Resource.Loading -> {
showProgress(true)
}
is Resource.Error -> {
showSnack(it.message!!)
showProgress(false)
}
is Resource.Success -> {
it.data?.let { advert ->
this.advert = advert
bindAdvert(advert)
}
showProgress(false)
}
}
}
private fun bindAdvert(item: DetailAdvert) {
initTabLayout(fragments, item)
}
当我成功收到数据时,我正在初始化我的 TabLayout
和 ViewPager2
,然后将 List
传递给 InfoFragment
,这是第一个选项卡和描述文本到第二个选项卡 DescriptionFragment
private fun initTabLayout(fragments: List<Fragment>, advert: DetailAdvert) {
viewpagerAdapter =
DetailsFragmentPagerAdapter(childFragmentManager, lifecycle, fragments, advert, this)
binding.viewpager.adapter = viewpagerAdapter
binding.viewpager.isUserInputEnabled = false
//initializing utility class
val heightAnimator = ViewPager2ViewHeightAnimator()
heightAnimator.viewPager2 = binding.viewpager
tabLayoutMediator = TabLayoutMediator(binding.tabs, binding.viewpager) { tab, position ->
tab.text = titles[position]
}
tabLayoutMediator.attach()
}
DetailsFragmentPagerAdapter
class DetailsFragmentPagerAdapter(
fragmentManager: FragmentManager,
lifecycle: Lifecycle,
private val fragments: List<Fragment>,
private val advert: DetailAdvert,
private val parentFragment: Fragment
) : FragmentStateAdapter(fragmentManager, lifecycle) {
init {
registerFragmentTransactionCallback(object :
FragmentStateAdapter.FragmentTransactionCallback() {
override fun onFragmentMaxLifecyclePreUpdated(
fragment: Fragment,
maxLifecycleState: Lifecycle.State,
) = if (maxLifecycleState == Lifecycle.State.RESUMED) {
OnPostEventListener {
fragment.parentFragmentManager.commitNow {
setPrimaryNavigationFragment(fragment)
}
}
} else {
super.onFragmentMaxLifecyclePreUpdated(fragment, maxLifecycleState)
}
})
}
override fun getItemCount(): Int = fragments.size
override fun createFragment(position: Int): Fragment {
val fragment = fragments[position]
when (fragment) {
is DescriptionFragment -> {
fragment.arguments = Bundle().apply {
putString(DESCRIPTION_KEY, advert.text)
}
}
is InfoFragment -> {
fragment.arguments = Bundle().apply {
val infoList = getInfoList(advert,parentFragment.requireContext())
putParcelableArray(INFO_KEY, infoList.toTypedArray())
}
}
}
return fragment
}
}
在第一个选项卡中,我只有 RecyclerView
显示列表项:
fragment_info.xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/info_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adapter="@{adapter}"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
InfoFragment
@AndroidEntryPoint
class InfoFragment : Fragment(R.layout.fragment_info_layout) {
private lateinit var binding: FragmentInfoLayoutBinding
@Inject
lateinit var mAdapter: InfoFragmentAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentInfoLayoutBinding.bind(view)
val infoList = arguments?.getParcelableArray(Constants.INFO_KEY)!!.toList() as List<Info>
binding.adapter = mAdapter
val dividerItemDecoration = DividerItemDecoration(binding.infoRv.context,
LinearLayoutManager(requireContext()).orientation)
binding.infoRv.addItemDecoration(dividerItemDecoration)
mAdapter.submitList(infoList)
}
override fun onDestroyView() {
super.onDestroyView()
binding.unbind()
}
}
在第二个选项卡 DescriptionFragment
中,我有一个 NestedScrollView
和一个 TextView
:
fragment_description.xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:id="@+id/nsv_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/description_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{Jsoup.parse(description).text()}"
android:lineSpacingExtra="8dp"
tools:text="@string/lorem" />
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
DescriptionFragment
class DescriptionFragment : Fragment(R.layout.fragment_description_layout) {
private lateinit var binding: FragmentDescriptionLayoutBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentDescriptionLayoutBinding.bind(view)
val description = arguments?.getString(Constants.DESCRIPTION_KEY)!!
binding.description = description
}
override fun onDestroyView() {
super.onDestroyView()
binding.unbind()
}
}
最后是我的布局 DetailsFragment
:
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@android:color/transparent">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/view_pager_height"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/advert_images_vp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adapter="@{adapter}"
android:transitionGroup="true"
android:orientation="horizontal"
/>
<TextView
android:id="@+id/no_image_tv"
style="@style/TextAppearance.AppCompat.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/no_image"
android:textColor="@color/gray"
android:visibility="invisible" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
android:background="@color/primaryColor"
app:tabContentStart="72dp"
app:tabGravity="fill"
app:tabIndicatorColor="@color/white"
app:tabMode="fixed"
app:tabTextAppearance="@android:style/TextAppearance.Widget.TabWidget"
app:tabTextColor="@color/white" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/nested_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
//other stuff
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
正如我之前提到的,我正在使用以下 class 动态更改 ViewPager2
的高度:
ViewPager2ViewHeightAnimator
class ViewPager2ViewHeightAnimator {
var viewPager2: ViewPager2? = null; set(value) {
if (field != value) {
field?.unregisterOnPageChangeCallback(onPageChangeCallback)
field = value
value?.registerOnPageChangeCallback(onPageChangeCallback)
}
}
private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager
private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
recalculate(position, positionOffset)
}
}
fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply {
val leftView = findViewByPosition(position) ?: return@apply
val rightView = findViewByPosition(position + 1)
val setMeasure = {
viewPager2?.apply {
val leftHeight = getMeasuredViewHeightFor(leftView)
layoutParams = layoutParams.apply {
height = if (rightView != null) {
val rightHeight = getMeasuredViewHeightFor(rightView)
leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt()
} else {
leftHeight
}
}
invalidate()
}
}
val onLayoutChanged =
ViewTreeObserver.OnGlobalLayoutListener {
setMeasure.invoke()
}
leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged)
rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged)
setMeasure.invoke()
}
private fun getMeasuredViewHeightFor(view: View): Int {
val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY)
val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
view.measure(wMeasureSpec, hMeasureSpec)
return view.measuredHeight
}
}
但结果是:
我也曾尝试修改here中的代码,但结果是:
我认为发生这种情况是因为异步加载数据。我该如何解决?
谢谢。
好的,我不知道发生了什么,但我已经解决了我的问题。这是代码:
ViewPager2HeightAnimator
class ViewPager2HeightAnimator {
var viewPager2: ViewPager2? = null; set(value) {
if (field != value) {
field?.unregisterOnPageChangeCallback(onPageChangeCallback)
field = value
value?.registerOnPageChangeCallback(onPageChangeCallback)
}
}
private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager
private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int,
) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
recalculate(position, positionOffset)
}
}
fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply {
val leftView = findViewByPosition(position) ?: return@apply
val rightView = findViewByPosition(position + 1)
val setMeasure = {
viewPager2?.apply {
val leftHeight = getMeasuredViewHeightFor(leftView)
layoutParams = layoutParams.apply {
height = if (rightView != null) {
val rightHeight = getMeasuredViewHeightFor(rightView)
leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt()
} else {
leftHeight
}
}
invalidate()
}
}
val onLayoutChanged =
ViewTreeObserver.OnGlobalLayoutListener {
setMeasure.invoke()
}
leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged)
rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged)
}
private fun getMeasuredViewHeightFor(view: View): Int {
val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY)
val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
view.measure(wMeasureSpec, hMeasureSpec)
return view.measuredHeight
}
}
然后我将 binding.infoRv.isNestedScrollingEnabled = false
添加到第一个选项卡的 OnViewCreated
方法中。虽然它有一些小的性能问题,但它按预期工作。
我有一个ViewPager2
with TabLayout
. I'm trying to set the height of the ViewPager2
dynamically based on its content. I have checked some related questions like DetailsFragment
时,我从网络获取数据如下:
DetailFragmentViewModel
private val _advert = MutableLiveData<Resource<DetailAdvert>>()
val advert: LiveData<Resource<DetailAdvert>> get() = _advert
init {
savedStateHandle.get<Int>("id")?.let {
getAdvert(it)
}
}
private fun getAdvert(id: Int) {
_advert.value = Resource.Loading()
viewModelScope.launch(Dispatchers.Main) {
_advert.value = repo.advert(id)
}
}
我在 DetailsFragment
中观察 advert
LiveData
:
DetailsFragment
mViewModel.advert.observe(viewLifecycleOwner) {
when (it) {
is Resource.Loading -> {
showProgress(true)
}
is Resource.Error -> {
showSnack(it.message!!)
showProgress(false)
}
is Resource.Success -> {
it.data?.let { advert ->
this.advert = advert
bindAdvert(advert)
}
showProgress(false)
}
}
}
private fun bindAdvert(item: DetailAdvert) {
initTabLayout(fragments, item)
}
当我成功收到数据时,我正在初始化我的 TabLayout
和 ViewPager2
,然后将 List
传递给 InfoFragment
,这是第一个选项卡和描述文本到第二个选项卡 DescriptionFragment
private fun initTabLayout(fragments: List<Fragment>, advert: DetailAdvert) {
viewpagerAdapter =
DetailsFragmentPagerAdapter(childFragmentManager, lifecycle, fragments, advert, this)
binding.viewpager.adapter = viewpagerAdapter
binding.viewpager.isUserInputEnabled = false
//initializing utility class
val heightAnimator = ViewPager2ViewHeightAnimator()
heightAnimator.viewPager2 = binding.viewpager
tabLayoutMediator = TabLayoutMediator(binding.tabs, binding.viewpager) { tab, position ->
tab.text = titles[position]
}
tabLayoutMediator.attach()
}
DetailsFragmentPagerAdapter
class DetailsFragmentPagerAdapter(
fragmentManager: FragmentManager,
lifecycle: Lifecycle,
private val fragments: List<Fragment>,
private val advert: DetailAdvert,
private val parentFragment: Fragment
) : FragmentStateAdapter(fragmentManager, lifecycle) {
init {
registerFragmentTransactionCallback(object :
FragmentStateAdapter.FragmentTransactionCallback() {
override fun onFragmentMaxLifecyclePreUpdated(
fragment: Fragment,
maxLifecycleState: Lifecycle.State,
) = if (maxLifecycleState == Lifecycle.State.RESUMED) {
OnPostEventListener {
fragment.parentFragmentManager.commitNow {
setPrimaryNavigationFragment(fragment)
}
}
} else {
super.onFragmentMaxLifecyclePreUpdated(fragment, maxLifecycleState)
}
})
}
override fun getItemCount(): Int = fragments.size
override fun createFragment(position: Int): Fragment {
val fragment = fragments[position]
when (fragment) {
is DescriptionFragment -> {
fragment.arguments = Bundle().apply {
putString(DESCRIPTION_KEY, advert.text)
}
}
is InfoFragment -> {
fragment.arguments = Bundle().apply {
val infoList = getInfoList(advert,parentFragment.requireContext())
putParcelableArray(INFO_KEY, infoList.toTypedArray())
}
}
}
return fragment
}
}
在第一个选项卡中,我只有 RecyclerView
显示列表项:
fragment_info.xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/info_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adapter="@{adapter}"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
InfoFragment
@AndroidEntryPoint
class InfoFragment : Fragment(R.layout.fragment_info_layout) {
private lateinit var binding: FragmentInfoLayoutBinding
@Inject
lateinit var mAdapter: InfoFragmentAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentInfoLayoutBinding.bind(view)
val infoList = arguments?.getParcelableArray(Constants.INFO_KEY)!!.toList() as List<Info>
binding.adapter = mAdapter
val dividerItemDecoration = DividerItemDecoration(binding.infoRv.context,
LinearLayoutManager(requireContext()).orientation)
binding.infoRv.addItemDecoration(dividerItemDecoration)
mAdapter.submitList(infoList)
}
override fun onDestroyView() {
super.onDestroyView()
binding.unbind()
}
}
在第二个选项卡 DescriptionFragment
中,我有一个 NestedScrollView
和一个 TextView
:
fragment_description.xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:id="@+id/nsv_detail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/description_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{Jsoup.parse(description).text()}"
android:lineSpacingExtra="8dp"
tools:text="@string/lorem" />
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
DescriptionFragment
class DescriptionFragment : Fragment(R.layout.fragment_description_layout) {
private lateinit var binding: FragmentDescriptionLayoutBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentDescriptionLayoutBinding.bind(view)
val description = arguments?.getString(Constants.DESCRIPTION_KEY)!!
binding.description = description
}
override fun onDestroyView() {
super.onDestroyView()
binding.unbind()
}
}
最后是我的布局 DetailsFragment
:
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@android:color/transparent">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/view_pager_height"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/advert_images_vp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adapter="@{adapter}"
android:transitionGroup="true"
android:orientation="horizontal"
/>
<TextView
android:id="@+id/no_image_tv"
style="@style/TextAppearance.AppCompat.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/no_image"
android:textColor="@color/gray"
android:visibility="invisible" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
android:background="@color/primaryColor"
app:tabContentStart="72dp"
app:tabGravity="fill"
app:tabIndicatorColor="@color/white"
app:tabMode="fixed"
app:tabTextAppearance="@android:style/TextAppearance.Widget.TabWidget"
app:tabTextColor="@color/white" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/nested_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
//other stuff
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
正如我之前提到的,我正在使用以下 class 动态更改 ViewPager2
的高度:
ViewPager2ViewHeightAnimator
class ViewPager2ViewHeightAnimator {
var viewPager2: ViewPager2? = null; set(value) {
if (field != value) {
field?.unregisterOnPageChangeCallback(onPageChangeCallback)
field = value
value?.registerOnPageChangeCallback(onPageChangeCallback)
}
}
private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager
private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
recalculate(position, positionOffset)
}
}
fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply {
val leftView = findViewByPosition(position) ?: return@apply
val rightView = findViewByPosition(position + 1)
val setMeasure = {
viewPager2?.apply {
val leftHeight = getMeasuredViewHeightFor(leftView)
layoutParams = layoutParams.apply {
height = if (rightView != null) {
val rightHeight = getMeasuredViewHeightFor(rightView)
leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt()
} else {
leftHeight
}
}
invalidate()
}
}
val onLayoutChanged =
ViewTreeObserver.OnGlobalLayoutListener {
setMeasure.invoke()
}
leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged)
rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged)
setMeasure.invoke()
}
private fun getMeasuredViewHeightFor(view: View): Int {
val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY)
val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
view.measure(wMeasureSpec, hMeasureSpec)
return view.measuredHeight
}
}
但结果是:
我也曾尝试修改here中的代码,但结果是:
我认为发生这种情况是因为异步加载数据。我该如何解决?
谢谢。
好的,我不知道发生了什么,但我已经解决了我的问题。这是代码:
ViewPager2HeightAnimator
class ViewPager2HeightAnimator {
var viewPager2: ViewPager2? = null; set(value) {
if (field != value) {
field?.unregisterOnPageChangeCallback(onPageChangeCallback)
field = value
value?.registerOnPageChangeCallback(onPageChangeCallback)
}
}
private val layoutManager: LinearLayoutManager? get() = (viewPager2?.getChildAt(0) as? RecyclerView)?.layoutManager as? LinearLayoutManager
private val onPageChangeCallback = object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int,
) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
recalculate(position, positionOffset)
}
}
fun recalculate(position: Int, positionOffset: Float = 0f) = layoutManager?.apply {
val leftView = findViewByPosition(position) ?: return@apply
val rightView = findViewByPosition(position + 1)
val setMeasure = {
viewPager2?.apply {
val leftHeight = getMeasuredViewHeightFor(leftView)
layoutParams = layoutParams.apply {
height = if (rightView != null) {
val rightHeight = getMeasuredViewHeightFor(rightView)
leftHeight + ((rightHeight - leftHeight) * positionOffset).toInt()
} else {
leftHeight
}
}
invalidate()
}
}
val onLayoutChanged =
ViewTreeObserver.OnGlobalLayoutListener {
setMeasure.invoke()
}
leftView.viewTreeObserver.addOnGlobalLayoutListener(onLayoutChanged)
rightView?.viewTreeObserver?.addOnGlobalLayoutListener(onLayoutChanged)
}
private fun getMeasuredViewHeightFor(view: View): Int {
val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY)
val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
view.measure(wMeasureSpec, hMeasureSpec)
return view.measuredHeight
}
}
然后我将 binding.infoRv.isNestedScrollingEnabled = false
添加到第一个选项卡的 OnViewCreated
方法中。虽然它有一些小的性能问题,但它按预期工作。