Элементы имеют разную ширину при использовании RecyclerView GridLayoutManager для создания интервалов столбцов по ItemDecoration
Я пытаюсь использовать RecyclerView
и GridLayoutManager
чтобы сделать сетку из 3 столбцов, и я использую ItemDecoration
чтобы сделать интервал столбцов, теперь проблема заключается в том, что ширина элемента в третьем столбце меньше, чем элемент в первом и втором столбце! См. снимок экрана ниже.
если я не добавить ItemDecoration
to RecyclerView
все ОК.
вот мой код:
MainActivity.java:
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mAdapter = new MyAdapter();
mRecyclerView.setAdapter(mAdapter);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);
mRecyclerView.setLayoutManager(gridLayoutManager);
int horizontalSpacing = 20;
int verticalSpacing = 10;
SpacingDecoration decoration = new SpacingDecoration(horizontalSpacing, verticalSpacing, true);
mRecyclerView.addItemDecoration(decoration);
}
private static class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private int[] mColors = new int[]{Color.RED, Color.BLUE, Color.MAGENTA};
private static class ItemHolder extends RecyclerView.ViewHolder {
public MyTextView title;
public ItemHolder(View itemView) {
super(itemView);
title = (MyTextView) itemView.findViewById(android.R.id.text1);
title.setTextColor(Color.WHITE);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
ItemHolder holder = new ItemHolder(itemView);
holder.itemView.setOnClickListener(itemClickListener);
return holder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder rHolder, int position) {
ItemHolder holder = (ItemHolder) rHolder;
holder.title.setText(String.format("[%d]width:%d", position, holder.itemView.getWidth()));
holder.itemView.setBackgroundColor(mColors[position % mColors.length]);
holder.itemView.setTag(position);
holder.title.setTag(position);
}
@Override
public int getItemCount() {
return 50;
}
private View.OnClickListener itemClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = (int) v.getTag();
showText(v.getContext(), String.format("[%d]->width:%d", position, v.getWidth()));
}
};
}
public static class SpacingDecoration extends RecyclerView.ItemDecoration {
private int mHorizontalSpacing = 5;
private int mVerticalSpacing = 5;
private boolean isSetMargin = true;
public SpacingDecoration(int hSpacing, int vSpacing, boolean setMargin) {
isSetMargin = setMargin;
mHorizontalSpacing = hSpacing;
mVerticalSpacing = vSpacing;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
boolean isSetMarginLeftAndRight = this.isSetMargin;
int bottomOffset = mVerticalSpacing;
int leftOffset = 0;
int rightOffset = 0;
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();
if (parent.getLayoutManager() instanceof GridLayoutManager) {
GridLayoutManager lm = (GridLayoutManager) parent.getLayoutManager();
GridLayoutManager.LayoutParams gridLp = (GridLayoutManager.LayoutParams) lp;
if (gridLp.getSpanSize() == lm.getSpanCount()) {
// Current item is occupied the whole row
// We just need to care about margin left and right now
if (isSetMarginLeftAndRight) {
leftOffset = mHorizontalSpacing;
rightOffset = mHorizontalSpacing;
}
} else {
// Current item isn't occupied the whole row
if (gridLp.getSpanIndex() > 0) {
// Set space between items in one row
leftOffset = mHorizontalSpacing;
} else if (gridLp.getSpanIndex() == 0 && isSetMarginLeftAndRight) {
// Set left margin of a row
leftOffset = mHorizontalSpacing;
}
if (gridLp.getSpanIndex() == lm.getSpanCount() - gridLp.getSpanSize() && isSetMarginLeftAndRight) {
// Set right margin of a row
rightOffset = mHorizontalSpacing;
}
}
}
outRect.set(leftOffset, 0, rightOffset, bottomOffset);
}
}
private static Toast sToast;
public static void showText(Context context, String text) {
if (sToast != null) {
sToast.cancel();
}
sToast = Toast.makeText(context, text, Toast.LENGTH_LONG);
sToast.show();
}
}
activity_main.в XML
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
товар.в XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.liuqing.rvgldemo.MyTextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp"
android:textColor="#ffffff"
android:textAppearance="?android:attr/textAppearanceMedium"/>
</LinearLayout>
MyTextView.java
public class MyTextView extends TextView {
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus) {
setText("[" + getTag() + "]width:" + getWidth());
}
}
}
будет очень признателен, если кто-то сможет объяснить эту проблему.
2 ответов
Я сам нашел причину проблемы. Смещение, которое сделано в ItemDecoration
рассматривается как часть размеры элемента(ширина и высота)!
давайте посмотрим на пример кода и захвата экрана в вопросе выше. Ширина захвата экрана составляет 480 пикселей, а вот 3 столбца, ширина каждого элемента составляет 480/3 = 160 пикселей. В SpacingDecoration
, Я добавляю левое смещение (20 пикселей) в первый и второй столбцы, поэтому ширина содержимого первого и второго столбца 160-20=140, то я могу добавить левый и правый зачетом по 3-й колонке, так что ширина контента 3-й элемент столбца 160-20-20=120.
теперь мы хотим сделать содержимое каждого элемента (цветной прямоугольник) одинаковой ширины, мы должны рассчитать, сколько каждый элемент столбца делит общее расстояние одной строки, но я плохо пишу подробный анализ, поэтому здесь я пишу грубый процесс вычисления, вы можете передать его и перейти к выводу.
spacing = 20
columnCount = 3
rowWidth = 480
itemWidth = rowWidth / columnCount
itemOccupiedSpacing = (spacing * (columnCount + 1)) / columnCount = spacing + spacing * (1/columnCount)
itemContentWidth = itemWidth - itemOccupiedSpacing
firstItemLeftOffset = spacing = spacing * (3/columnCount)
firstItemRightOffset = itemOccupiedSpacing - spacing = spacing * (1/columnCount)
secondItemLeftOffset = spacing - firstRightOffset = spacing * (2/columnCount)
secondItemRightOffset = itemOccupiedSpacing - secondLeftOffset = spacing * (2/columnCount)
thirdItemLeftOffset = itemOccupiedSpacing - secondLeftOffset = spacing * (1/columnCount)
thirdItemRightOffset = spacing = spacing * (3/columnCount)
мы можем заключить :
itemLeftOffset = spacing * ((columnCount - colunmnIndex) / columnCount)
itemRightOffset = spacing * ((colunmnIndex + 1) / columnCount)
colunmnIndex больше 0 и меньше, чем columnCount.
вот мой заказ ItemDecoration
для интервалов он хорошо работает с LinearLayoutManager
,GridLayoutManager
и StaggeredGridLayoutManager
, все элементы имеют одинаковую ширину. Вы можете использовать его непосредственно в коде.
public class SpacingDecoration extends ItemDecoration {
private int mHorizontalSpacing = 0;
private int mVerticalSpacing = 0;
private boolean mIncludeEdge = false;
public SpacingDecoration(int hSpacing, int vSpacing, boolean includeEdge) {
mHorizontalSpacing = hSpacing;
mVerticalSpacing = vSpacing;
mIncludeEdge = includeEdge;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
// Only handle the vertical situation
int position = parent.getChildAdapterPosition(view);
if (parent.getLayoutManager() instanceof GridLayoutManager) {
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
int spanCount = layoutManager.getSpanCount();
int column = position % spanCount;
getGridItemOffsets(outRect, position, column, spanCount);
} else if (parent.getLayoutManager() instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) parent.getLayoutManager();
int spanCount = layoutManager.getSpanCount();
StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
int column = lp.getSpanIndex();
getGridItemOffsets(outRect, position, column, spanCount);
} else if (parent.getLayoutManager() instanceof LinearLayoutManager) {
outRect.left = mHorizontalSpacing;
outRect.right = mHorizontalSpacing;
if (mIncludeEdge) {
if (position == 0) {
outRect.top = mVerticalSpacing;
}
outRect.bottom = mVerticalSpacing;
} else {
if (position > 0) {
outRect.top = mVerticalSpacing;
}
}
}
}
private void getGridItemOffsets(Rect outRect, int position, int column, int spanCount) {
if (mIncludeEdge) {
outRect.left = mHorizontalSpacing * (spanCount - column) / spanCount;
outRect.right = mHorizontalSpacing * (column + 1) / spanCount;
if (position < spanCount) {
outRect.top = mVerticalSpacing;
}
outRect.bottom = mVerticalSpacing;
} else {
outRect.left = mHorizontalSpacing * column / spanCount;
outRect.right = mHorizontalSpacing * (spanCount - 1 - column) / spanCount;
if (position >= spanCount) {
outRect.top = mVerticalSpacing;
}
}
}
}
Я написал более надежный ItemDecoration на основе ответа @AvatarQing: SCommonItemDecoration
вы можете установить такое же вертикальное или горизонтальное пространство между элементами, кроме того, вы можете установить другое пространство для разных типов элементов.