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

使用 GridLayoutManager 将项目粘贴到 RecyclerView 的底部

如何解决使用 GridLayoutManager 将项目粘贴到 RecyclerView 的底部

我想要一个 RecyclerView 可以按照认情况从左到右和从上到下对其项目进行排序。但另外我想遵循如下图右侧的特殊行为。

enter image description here

我希望项目“作为一个整体”粘在底部,好像 RecyclerView 本身会调整其大小以包装其内容,如果项目不超过 RecyclerViews 高度 - 为了更好用拇指搜索结果的可访问性。我无法通过 LayoutManager::reverseLayoutRecyclerView::layoutDirectionwrap_content 的组合来获得这种行为 RecyclerView 并且让它粘在底部也不起作用,因为我想要整个列表区域都可以滚动(RecyclerView 必须保持其完整大小)- 任何想法如何获得这种行为?

解决方法

要实现此行为以将所有项目与 RecyclerView 的底部对齐,您需要设置标志 setStackFromEnd(true)

当 stack from bottom 设置为 true 时,列表填充其内容 从视图底部开始

但是 GridLayout 不支持此标志,抛出如下异常:

GridLayoutManager 不支持从末端堆栈。考虑使用 反向布局

但即使在 GridLayoutManager 中使用 setReverseLayout(true) 它也会从下到上和从左到右呈现项目,这不是您想要的结果,甚至反转列表的数据。

可能的解决方案:

  1. 使用 GridLayoutManager 并仅将顶部填充添加到第一列以填充剩余的顶部区域,甚至使用虚拟空列。

  2. 使用带有 setStackFromEnd(true) 的 LinearLayout,它按预期工作,其中每个项目从 RecyclerView 的底部到顶部呈现,每个项目行(项目视图)持有例如:4 个视图(列数)等宽。当然那样的话,列表中的每个 Item 模型必须保存 4 个数据,每列一个。

如果您选择第二个选项,您可以为每一行准备列数据,如下面的代码片段:

List<ItemModel> data = new ArrayList<>();
int listCnt = 14;
for(int i=0; i<listCnt; i++){
    if(i%4 == 0) {
        String dataCol1 = i < listCnt ? String.valueOf(i) : "";
        String dataCol2 = (i + 1) < listCnt ? String.valueOf(i + 1) : "";
        String dataCol3 = (i + 2) < listCnt ? String.valueOf(i + 2) : "";
        String dataCol4 = (i + 3) < listCnt ? String.valueOf(i + 3) : "";
        data.add(new ItemModel(dataCol1,dataCol2,dataCol3,dataCol4));
    }
}

//set LinearLayoutManager
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this,RecyclerView.VERTICAL,false);
linearLayoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(linearLayoutManager);

//set adapter
RecyclerViewAdapter adapter = new RecyclerViewAdapter(data);
recyclerView.setAdapter(adapter);

其中 ItemModel 保存每一行的数据,例如:

public class ItemModel {

    public String dataCol1="",dataCol2="",dataCol3="",dataCol4="";

    public ItemModel(String dataCol1,String dataCol2,String dataCol3,String dataCol4) {
        this.dataCol1 = dataCol1;
        this.dataCol2 = dataCol2;
        this.dataCol3 = dataCol3;
        this.dataCol4 = dataCol4;
    }
}

当然,在您的适配器中,您必须将 Visibility 列设置为 View.INVISIBLE,对于那些没有任何数据的人,以及 View.VISIBLE 为那些拥有的人。 适配器示例如下:

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{

        private List<ItemModel> mData;

        public RecyclerViewAdapter(List<ItemModel> data) {
            this.mData = data;
        }

        @NonNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_row,parent,false);
            return new ItemViewHolder(view);
        }

        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder,int position) {
           ItemModel itemModel = mData.get(position);
           if(holder instanceof ItemViewHolder) {
               ((ItemViewHolder) holder).bind(itemModel);
           }
        }

        @Override
        public int getItemCount() {
            return mData.size();
        }

        public class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
            public TextView itemTextView1,itemTextView2,itemTextView3,itemTextView4;

            public ItemViewHolder(View itemView) {
                super(itemView);
                itemTextView1 = itemView.findViewById(R.id.itemTextView1);
                itemTextView2 = itemView.findViewById(R.id.itemTextView2);
                itemTextView3 = itemView.findViewById(R.id.itemTextView3);
                itemTextView4 = itemView.findViewById(R.id.itemTextView4);

                itemTextView1.setOnClickListener(this);
                itemTextView2.setOnClickListener(this);
                itemTextView3.setOnClickListener(this);
                itemTextView4.setOnClickListener(this);
            }

            public void bind(ItemModel itemModel){
                itemTextView1.setText(itemModel.dataCol1);
                itemTextView2.setText(itemModel.dataCol2);
                itemTextView3.setText(itemModel.dataCol3);
                itemTextView4.setText(itemModel.dataCol4);

                itemTextView1.setVisibility(TextUtils.isEmpty(itemModel.dataCol1) ? View.INVISIBLE : View.VISIBLE);
                itemTextView2.setVisibility(TextUtils.isEmpty(itemModel.dataCol2) ? View.INVISIBLE : View.VISIBLE);
                itemTextView3.setVisibility(TextUtils.isEmpty(itemModel.dataCol3) ? View.INVISIBLE : View.VISIBLE);
                itemTextView4.setVisibility(TextUtils.isEmpty(itemModel.dataCol4) ? View.INVISIBLE : View.VISIBLE);
            }

            @Override
            public void onClick(View v) {
                int cId = v.getId();
                if(cId == itemTextView1.getId()){
                    //get col1 data
                    String dataCol1 = mData.get(getAdapterPosition()).dataCol1;
                }
                else if(cId == itemTextView2.getId()){
                    //get col2 data
                    String dataCol2 = mData.get(getAdapterPosition()).dataCol2;
                }
                else if(cId == itemTextView3.getId()){
                    //get col3 data
                    String dataCol3 = mData.get(getAdapterPosition()).dataCol3;
                }
                else if(cId == itemTextView4.getId()){
                    //get col4 data
                    String dataCol4 = mData.get(getAdapterPosition()).dataCol4;
                }
            }
        }
    }

其中包含 4 个列视图的列表项 R.layout.item_list_row 可以如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/itemTextView1"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:gravity="center"
        android:layout_weight="1"
        android:background="@android:color/holo_blue_light"
        android:textColor="@android:color/black"
        tools:text="1"/>

    <TextView
        android:id="@+id/itemTextView2"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:gravity="center"
        android:layout_weight="1"
        android:background="@android:color/holo_blue_light"
        android:textColor="@android:color/black"
        tools:text="2"/>

    <TextView
        android:id="@+id/itemTextView3"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:gravity="center"
        android:layout_weight="1"
        android:background="@android:color/holo_blue_light"
        android:textColor="@android:color/black"
        tools:text="3"/>

    <TextView
        android:id="@+id/itemTextView4"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:gravity="center"
        android:layout_weight="1"
        android:background="@android:color/holo_blue_light"
        android:textColor="@android:color/black"
        tools:text="4"/>

</LinearLayout>

当然,如果你有不同的跨度计数,你可以在 RecyclerView Adapter 中创建不同的 ViewHolder 类型。

RecyclerView 高度分别为 match_parent300dpwrap_content 的选项 2 的结果如下所示。 RecyclerView 有灰色背景:

match_parent 300dp wrap_content

,

这是我对您尝试做的事情的理解。

  1. RecyclerView 在屏幕上有一些空间,对于任何给定的设备/方向,其宽度和高度都是恒定的。

  2. 如果项目计数使其显示填充 RecyclerView 的 屏幕区域,则 RecyclerView 将填充该区域并按原样滚动。

  3. 如果项目计数使得 RecyclerView's 屏幕区域没有被填满,那么项目的整个显示应该下降到 RecyclerView's 的底部- 屏幕区域在顶部留下空白空间。

一种方法是以编程方式为 RecyclerView 设置顶部填充,即 RecyclerView 的高度与显示项目的总高度之间的差值,如果它们不会填充 RecyclerView。如果您可以在布局前计算高度差,这会更容易,但您也可以在布局后使用 global layout listener 计算。

LinearLayoutManager#findFirstCompletelyVisibleItemPosition() 会告诉您哪个项目在显示屏的顶部。

LinearLayoutManager#findLastCompletelyVisibleItemPosition() 会告诉你最后的位置。

如果显示了所有位置,您就会知道要引入顶部填充。

我不清楚您是否希望项目能够滚动到顶部,如果它们不填充 RecyclerView。如果他们应该向上滚动,在底部留下空白空间,请引入底部填充。

您还必须在 RecyclerView 上指定 android:clipToPadding="false" 以使填充按上述说明工作。

以下是一个示例活动,展示了如何做到这一点:

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private final List<String> mItems = new ArrayList<>();
    private RecyclerView mRecycler;
    private final int mCount = 24;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        for (int i = 0; i < mCount; i++) {
            mItems.add("[" + i + "] " + getString(R.string.lorem));
        }

        mRecycler = findViewById(R.id.recyclerView);
        RecyclerViewAdapter mAdapter = new RecyclerViewAdapter(mItems);
        mRecycler.setAdapter(mAdapter);
        mRecycler.setLayoutManager(new GridLayoutManager(this,5));

        mRecycler.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                mRecycler.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                GridLayoutManager glm = (GridLayoutManager) mRecycler.getLayoutManager();
                if (glm == null) return;
                int lastPosition = glm.findLastCompletelyVisibleItemPosition();
                if (lastPosition == (mCount - 1)) {
                    View lastView = glm.findViewByPosition(lastPosition);
                    if (lastView == null) return;
                    int newPadding = mRecycler.getHeight() - lastView.getBottom();
                    // Set bottom padding to newPadding to allow items to scroll to the top
                    // of the RecyclerView.
                    mRecycler.setPadding(0,newPadding,newPadding);
                    mRecycler.setClipToPadding(false);
                }
            }
        });
    }
}

enter image description here

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