这是我对你所尝试做的事情的理解:
其中一种方法是以编程方式设置 RecyclerView 的顶部填充值,该值为如果显示的项目未填满 RecyclerView 的总高度与 RecyclerView 的高度差。如果能够在布局之前计算出高度差,那会更容易些,但您也可以通过全局布局监听器在布局后计算出来。
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, 0, newPadding);
mRecycler.setClipToPadding(false);
}
}
});
}
}
要实现将RecyclerView中的所有项对齐到底部的行为,您需要设置标志setStackFromEnd(true)
当从底部堆叠设置为true时,列表从视图底部开始填充其内容
但是,这个标志不被GridLayout支持,会抛出异常,如下所示:
GridLayoutManager不支持从末尾堆栈。考虑使用反向布局
但即使在GridLayoutManager中使用setReverseLayout(true)
,它也会从底部向上和从左到右呈现项目,这不是您想要的结果,即使翻转列表数据。
可能的解决方案:
使用GridLayoutManager并仅向第一列添加顶部填充以填充剩余的顶部区域,甚至使用虚拟空列。
使用LinearLayout和setStackFromEnd(true)
,它按预期工作,其中每个项从RecyclerView的底部向上呈现,并且每个项行(Item View)保持例如: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;
}
}
当然,在您的适配器中,对于那些没有任何数据的列,您必须将其可见性设置为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;
}
}
}
}
列表项 R.layout.item_list_row
包含 4 列视图,可以如下所示:
<?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适配器中创建不同的ViewHolder类型。
选项2的结果,RecyclerView高度分别为match_parent
、300dp
和wrap_content
,如下所示。 RecyclerView具有灰色背景:
setReverseLayout
+ 反向数据 + 不可见的虚拟项放在最后,但这也有一些小缺点... 目前为止,这仍然是我最好的想法。 - prom85
GridLayoutManager
。网格只是象征着项目以特殊的方式粘附在底部,并且RecyclerView
不会包装其内容。因此,最终网格应该是不可见的。添加虚拟项确实有效,我一直都是这样做的 - 但我可能也会这样做,因为这是我能想到的唯一解决方案。此外,如果您使用具有不同跨度计数的项目,则虚拟项目的逻辑必须处理此问题,但仍然有效。 - prom85