如何解决使用 GridLayoutManager 将项目粘贴到 RecyclerView 的底部
我想要一个 RecyclerView
可以按照默认情况从左到右和从上到下对其项目进行排序。但另外我想遵循如下图右侧的特殊行为。
我希望项目“作为一个整体”粘在底部,好像 RecyclerView
本身会调整其大小以包装其内容,如果项目不超过 RecyclerViews
高度 - 为了更好用拇指搜索结果的可访问性。我无法通过 LayoutManager::reverseLayout
和 RecyclerView::layoutDirection
和 wrap_content
的组合来获得这种行为 RecyclerView
并且让它粘在底部也不起作用,因为我想要整个列表区域都可以滚动(RecyclerView
必须保持其完整大小)- 任何想法如何获得这种行为?
解决方法
要实现此行为以将所有项目与 RecyclerView 的底部对齐,您需要设置标志 setStackFromEnd(true)
当 stack from bottom 设置为 true 时,列表填充其内容 从视图底部开始
但是 GridLayout 不支持此标志,抛出如下异常:
GridLayoutManager 不支持从末端堆栈。考虑使用 反向布局
但即使在 GridLayoutManager 中使用 setReverseLayout(true)
它也会从下到上和从左到右呈现项目,这不是您想要的结果,甚至反转列表的数据。
可能的解决方案:
-
使用 GridLayoutManager 并仅将顶部填充添加到第一列以填充剩余的顶部区域,甚至使用虚拟空列。
-
使用带有
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_parent
、300dp
和 wrap_content
的选项 2 的结果如下所示。 RecyclerView 有灰色背景:
这是我对您尝试做的事情的理解。
-
RecyclerView 在屏幕上有一些空间,对于任何给定的设备/方向,其宽度和高度都是恒定的。
- 如果项目计数使其显示填充 RecyclerView 的 屏幕区域,则 RecyclerView 将填充该区域并按原样滚动。
- 如果项目计数使得 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);
}
}
});
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。