在RecyclerView中创建最后一个项目/ ViewHolder,填充屏幕的其余部分或具有最小高度

时间:2016-12-21 01:30:37

标签: android google-maps android-recyclerview adapter android-adapter

我正在使用RecyclerView。我使用循环器视图来显示模型类的详细信息。

//My model class
MyModel {
    String name;
    Double latitude;
    Double longitude;
    Boolean isOnline;
    ...
}

由于某些值可能不存在,我使用RecyclerView和自定义视图类型(一个代表我的模型的每个值)。

//Inside my custom adapter
public void setModel(T model) {
    //Reset values
    itemCount = 0;
    deviceOfflineViewPosition = -1;
    mapViewPosition = -1;

    //If device is offline, add device offline item
    if (device.isOnline() == null || !device.isOnline()) {
        deviceOfflineViewPosition = itemCount;
        itemCount++;
    }

    //Add additional items if necessary
    ...

    //Always add the map as the last item
    mapViewPosition = itemCount;
    itemCount++;

    notifyDataSetChanged();
}

@Override
public int getItemViewType(int position) {
    if (position == deviceOfflineViewPosition) {
        return ITEM_VIEW_TYPE_OFFLINE;
    } else if (position == mapViewPosition) {
        return ITEM_VIEW_TYPE_MAP;
    } else if (...) {
        //Check for other view types
    }
}

使用RecyclerView,我可以在运行时轻松确定哪些值可用,并将相应的项添加到RecyclerView数据源。我简化了代码,但我的模型有更多的值,而且我有更多的视图类型。

RecyclerView中的最后一项始终是地图,并且始终存在。即使我的模型中根本没有任何价值,也至少会有一个项目,即地图。

问题:如何让RecyclerView中的最后一项填满屏幕上的剩余空间,并且还有最小高度。尺寸应该是更大的价值:剩余空间或最小高度。例如:

  • 模型有一些值,总计占用600dp屏幕的100dp - >地图高度应为500dp
  • 模型有很多值,总计需要500dp的600dp屏幕 - > map heigh应该是最小值200dp
  • 模型没有值 - >地图填满整个屏幕

enter image description here

4 个答案:

答案 0 :(得分:4)

布局完最后一项后,您可以在RecyclerView中找到剩余空间,并将剩余空间添加到最后一项的TransactionSynchronizationManager.getResource(key)中。

minHeight

val isLastItem = getItemCount() - 1 == position if (isLastItem) { val lastItemView = holder.itemView lastItemView.doOnLayout { val recyclerViewHeight = recyclerView.height val lastItemBottom = lastItemView.bottom val heightDifference = recyclerViewHeight - lastItemBottom if (heightDifference > 0) { lastItemView.minimumHeight = lastItemView.height + heightDifference } } } 中,使用onBindHolder检查该项是否为最后一项。如果是最后一项,则通过在lastItem bottom减去recyclerView height来找到高度差(getItemCount() - 1 == position给出了视图相对于其父视图的最底像素。在这种情况下,我们的父视图是getBottom() )。

如果差异大于0,则将其添加到最后一个视图的当前高度并将其设置为RecyclerView。我们将其设置为minHeight,而不是直接设置为minHeight,以支持最后一个视图的动态内容更改。

注意:此代码为Kotlin,而doOnLayout函数来自Android KTx。同样,您的height高度应为RecyclerView,这样才能正常工作(我想这很明显)。

答案 1 :(得分:0)

我使用muthuraj解决方案来解决类似的问题,如果以前的项目占据了页面的高度或更高的高度,但我希望最后一个项目能够正常显示在最后一个项目上,但如果上一个项目没有填补高度,我希望最后一项显示在页面底部。

当上一个项目+ specialItem占据所有位置时。
--------------
项目
项目
项目
specialItem
--------------

当上一个项目+ specialItem占用的高度超过高度时
--------------
项目
项目
项目
项目
项目
项目
specialItem
--------------

当上一个项目+ specialItem占用的高度小于高度时
--------------



specialItem
--------------

--------------
项目


specialItem
--------------

要对此进行存档,我将使用此代码

val lastItemView = holder.itemView
            //TODO use a better option instead of waiting for 200ms
            Handler().postDelayed({
                val lastItemTop = lastItemView.top
                val remainingSpace = recyclerViewHeight() - lastItemTop
                val heightToSet = Math.max(remainingSpace, minHeight)

                if (lastItemView.height != heightToSet) {
                    val layoutParams = lastItemView.layoutParams
                    layoutParams.height = heightToSet
                    lastItemView.layoutParams = layoutParams
                }
            }, 200)

我使用Handler()。postDelayed的原因是doOnLayout从来没有被我调用过,而且我不知道为什么,所以以200ms的延迟运行代码,直到找到更好的方法为止。

答案 2 :(得分:0)

您可以扩展LinearLayoutManager来自己布置最后一个项目。

这是一个FooterLinearLayoutManager,它将把列表的最后一项移到屏幕底部(如果尚不存在)。通过覆盖layoutDecoratedWithMarginsLinearLayouyManager调用了项目应该到达的位置,但我们可以将其与父级高度匹配。

注意::这不会“调整”视图的大小,因此精美的背景或类似内容可能无法工作,它只会将最后一项移至屏幕底部。

/**
 * Moves the last list item to the bottom of the screen.
 */
class FooterLinearLayoutManager(context: Context) : LinearLayoutManager(context) {

    override fun layoutDecoratedWithMargins(child: View, left: Int, top: Int, right: Int, bottom: Int) {
        val lp = child.layoutParams as RecyclerView.LayoutParams
        if (lp.viewAdapterPosition < itemCount - 1)
            return super.layoutDecoratedWithMargins(child, left, top, right, bottom)

        val parentBottom = height - paddingBottom
        return if (bottom < parentBottom) {
            val offset = parentBottom - bottom
            super.layoutDecoratedWithMargins(child, left, top + offset, right, bottom + offset)
        } else {
            super.layoutDecoratedWithMargins(child, left, top, right, bottom)
        }
    }
}

答案 3 :(得分:0)

这对我有用,让recycler用layout_height =“ 0dp”视图,并将顶部约束放在相应的上面项的底部,并将底部约束放在父项,然后将其与剩余空间一起调整大小:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ToggleButton
        android:id="@+id/toggleUpcomingButton"
        style="?attr/materialButtonOutlinedStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:gravity="top"
        android:text="@string/upcoming"
        app:layout_constraintEnd_toStartOf="@+id/togglePupularButton"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ToggleButton
        android:id="@+id/togglePupularButton"
        style="?attr/materialButtonOutlinedStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="top"
        android:text="@string/popular"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/toggleUpcomingButton"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="@string/popular_movies"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/toggleUpcomingButton" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text_title" />
</androidx.constraintlayout.widget.ConstraintLayout>