Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/225.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用RecyclerView GridLayoutManager按Item装饰设置列间距时,项的宽度不同_Java_Android_Android Recyclerview_Gridlayoutmanager - Fatal编程技术网

Java 使用RecyclerView GridLayoutManager按Item装饰设置列间距时,项的宽度不同

Java 使用RecyclerView GridLayoutManager按Item装饰设置列间距时,项的宽度不同,java,android,android-recyclerview,gridlayoutmanager,Java,Android,Android Recyclerview,Gridlayoutmanager,我正在尝试使用RecyclerView和GridLayoutManager创建一个3列网格,我使用ItemDecoration创建列间距,现在的问题是第三列中的项目宽度小于第一列和第二列中的项目!请参见下面的屏幕截图 如果我不将自定义的项目装饰添加到回收视图,一切正常 这是我的密码: MainActivity.java: public class MainActivity extends AppCompatActivity { private RecyclerView mRecycl

我正在尝试使用
RecyclerView
GridLayoutManager
创建一个3列网格,我使用
ItemDecoration
创建列间距,现在的问题是第三列中的项目宽度小于第一列和第二列中的项目!请参见下面的屏幕截图

如果我不将自定义的
项目装饰
添加到
回收视图
,一切正常

这是我的密码:

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();
    }
}

如果有人能解释这个问题,我将不胜感激。

我自己找到了问题的原因。
项目装饰
中的偏移量被视为项目尺寸(宽度和高度)的一部分

让我们看看上面问题中的示例代码和屏幕截图。屏幕截图的宽度是480像素,这里有3列,每个项目的宽度是480/3=160像素。在
SpacingDecoration
中,我在第一列和第二列上添加了一个左偏移量(20像素),因此第一列和第二列项目的内容宽度为160-20=140,然后我在第三列项目上添加了左偏移量和右偏移量,因此第三列项目的内容宽度为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


这是我自定义的
项目装饰
间距,它与
线性布局管理器
网格布局管理器
交错网格布局管理器
配合得很好,所有项目的宽度都相同。您可以在代码中直接使用它

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;
            }
        }
    }
}
public类间距分隔扩展ItemDecoration{
私有int mHorizontalSpacing=0;
私有int mVerticalSpacing=0;
私有布尔值mincludedge=false;
公共间距分隔(int hSpacing、int vSpacing、布尔includeEdge){
mHorizontalSpacing=hSpacing;
mVerticalSpacing=vSpacing;
MINCLUDEDGE=包含边缘;
}
@凌驾
public void getItemOffsets(Rect outRect、View View、RecyclerView父级、RecyclerView.State){
super.getItemOffset(outRect、view、parent、state);
//只处理垂直的情况
int position=parent.getChildAdapterPosition(视图);
if(GridLayoutManager的parent.getLayoutManager()实例){
GridLayoutManager布局管理器=(GridLayoutManager)父级。getLayoutManager();
int spanCount=layoutManager.getSpanCount();
int列=位置%spanCount;
GetGridItemOffset(outRect、position、column、spanCount);
}else if(StaggedGridLayoutManager的父级.getLayoutManager()实例){
StaggedGridLayoutManager布局管理器=(StaggedGridLayoutManager)父级。getLayoutManager();
int spanCount=layoutManager.getSpanCount();
StaggedGridLayoutManager.LayoutParams lp=(StaggedGridLayoutManager.LayoutParams)视图。getLayoutParams();
int column=lp.getSpanIndex();
GetGridItemOffset(outRect、position、column、spanCount);
}else if(LinearLayoutManager的父级.getLayoutManager()实例){
outRect.left=mHorizontalSpacing;
outRect.right=mHorizontalSpacing;
if(mincludedge){
如果(位置==0){
outRect.top=垂直间距;
}
outRect.bottom=垂直间距;
}否则{
如果(位置>0){
outRect.top=垂直间距;
}
}
}
}
私有void getGridItemOffset(Rect-outRect、int-position、int-column、int-spanCount){
if(mincludedge){
outRect.left=mHorizontalSpacing*(spanCount-列)/spanCount;
outRect.right=mHorizontalSpacing*(列+1)/spanCount;
如果(位置=spanCount){
outRect.top=垂直间距;
}
}
}
}

根据@AvatarQing的回答,我写了一个更加健壮的项目装饰:

您可以在项目之间设置相同的垂直或水平间距,此外还可以为不同类型的项目设置不同的间距


在您的公式中,右边距中的
+1
实际上不是
+1
,它应该是单元格的
+span大小
。(您可以使用spanSizeLookup或在单元格的LayoutParams中检索跨度大小)感谢分享@Bos!现在,您的类的关键部分已经包含在库中并得到了改进,以处理更高级的功能。
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)
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;
            }
        }
    }
}