这里不是直接讲怎么实现GridLayoutManager的居中对齐事情。
原始需求是这样的:
每行显示4个标签,当有超过4个item时左对齐,但当item少于4个时候要居中对齐,并且大小不变间距不变。一下图片:
先要知道public GridLayoutManager(Context context, int spanCount)中spanCount的值一旦设定好了,那么item的大小就确定了。那么当有少于spanCount的数据时,也只是占据靠左的几个span。
为了以下分析,我们设定每行4个item。
首先想到是判断数据个数少于4,那么我们就重新设定spanCount为实际个数,例如3。但是这么做是把原有的大小平均分成了3份,但是不能解决每个item大小不变,间距不变的问题。所以不行。
我这个方法是利用public static abstract class SpanSizeLookup这个来实现,作用是可以重新设定每个item锁占用的span数。例如可以设定一个item占用2个span,那么itme的width就变成2倍的。
先讲下实现原理。
那么一行出现的数据情况是1个、2个、3个、4个,那就4*3*2*1=24。设定spanCount为24。
00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 3 | 3 | 3 | 3 | 3 | 3 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
表格简单展示了在不懂数量下,每个item所站的span数量。
具体定义方式如下。
int[][] count = {{24,0,0,0},{12,12,0,0},{9,6,9,0},{6,6,6,6}};
现在不同数量的item的width已经确定了,那么要利用间距来实现item的显示width保持一致。例如item的width理论是4个span,那么当width大于4个span时,那么久设置这个item的边距使得他的现实width变成4个span。
先上代码
主TestActivity
package com.holy.demo.ui;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.holy.demo.R;
import com.holy.demo.constant.TestItemDecoration;
import java.util.ArrayList;
import java.util.List;
/**
* Copyright 2017, Smart Haier. All rights reserved.
* Description:
* Author: hanhongliang@smart-haier.com (Han Holy)
* Date: 2017/10/23
* ModifyBy:
* ModifyDate:
* ModifyDes :
*/
public class TestActivity extends AppCompatActivity implements View.OnClickListener {
private RecyclerView mTestList;
private TestAdapter mTestAdapter;
private GridLayoutManager layoutManager;
private TestItemDecoration mItemDecoration;
private final int mListNum = 3;
private final int mSpanSize = 24;
private final int mSpanShowSize = 4;
private int SpanCounts = mSpanSize;
private List<String> mData;
private Button testadd, testromove;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
testadd = (Button) findViewById(R.id.testadd);
testromove = (Button) findViewById(R.id.testromove);
initData();
testadd.setOnClickListener(this);
testromove.setOnClickListener(this);
mTestList = (RecyclerView) findViewById(R.id.testlist);
layoutManager = new GridLayoutManager(TestActivity.this, mSpanSize);
SpanCounts = mData.size()<mSpanShowSize?mData.size():mSpanShowSize;
layoutManager.setSpanSizeLookup(new TestSpanSizeLookup(SpanCounts));
mTestList.setLayoutManager(layoutManager);
mTestList.addItemDecoration(mItemDecoration = new TestItemDecoration(SpanCounts,mSpanShowSize, 30));
mTestList.setAdapter(mTestAdapter = new TestAdapter());
}
public List<String> initData() {
mData = new ArrayList<>();
for (int i = 0; i < mListNum; i++) {
mData.add(Integer.toString(i));
}
return mData;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.testadd:
if (layoutManager != null) {
mData.add(Integer.toString(mData.size()));
SpanCounts = mData.size()<mSpanShowSize?mData.size():mSpanShowSize;
layoutManager.setSpanSizeLookup(new TestSpanSizeLookup(SpanCounts));
mTestList.setLayoutManager(layoutManager);
mTestList.removeItemDecoration(mItemDecoration);
mTestList.addItemDecoration(mItemDecoration = new TestItemDecoration(SpanCounts, mSpanShowSize,30));
mTestList.setAdapter(mTestAdapter = new TestAdapter());
}
break;
case R.id.testromove:
if (layoutManager != null) {
mData.remove(mData.size() - 1);
SpanCounts = mData.size()<mSpanShowSize?mData.size():mSpanShowSize;
layoutManager.setSpanSizeLookup(new TestSpanSizeLookup(SpanCounts));
mTestList.setLayoutManager(layoutManager);
mTestList.removeItemDecoration(mItemDecoration);
mTestList.addItemDecoration(mItemDecoration = new TestItemDecoration(SpanCounts,mSpanShowSize, 30));
mTestList.setAdapter(mTestAdapter = new TestAdapter());
}
break;
}
}
public class TestViewHolder extends RecyclerView.ViewHolder {
TextView v;
public TestViewHolder(View itemView) {
super(itemView);
v = (TextView) itemView.findViewById(R.id.item_test);
}
}
public class TestAdapter extends RecyclerView.Adapter<TestViewHolder> {
@Override
public TestViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TestViewHolder holder = new TestViewHolder(LayoutInflater.from(TestActivity.this).inflate(R.layout.item_test, parent, false));
return holder;
}
@Override
public void onBindViewHolder(TestViewHolder holder, int position) {
holder.v.setText(mData.get(position));
}
@Override
public int getItemCount() {
return mData.size();
}
}
public class TestSpanSizeLookup extends GridLayoutManager.SpanSizeLookup{
int spanCount =0;
public TestSpanSizeLookup(int spanCount) {
this.spanCount = spanCount;
}
@Override
public int getSpanSize(int position) {
int[][] count = {{24,0,0,0},{12,12,0,0},{9,6,9,0},{6,6,6,6}};
int tmp = position%spanCount;
return count[spanCount-1][tmp];
}
}
}
主activity_test.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:layout_marginTop="@dimen/h2"
android:id="@+id/testlist"
android:layout_width="@dimen/w85"
android:layout_height="@dimen/h80"
android:layout_gravity="center">
</android.support.v7.widget.RecyclerView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="@dimen/h2"
android:gravity="center">
<Button
android:id="@+id/testadd"
android:layout_width="@dimen/w30"
android:layout_height="wrap_content"
android:background="@drawable/button_shape"
android:text="add"
android:textSize="@dimen/h2"/>
<Button
android:layout_marginLeft="@dimen/w5"
android:id="@+id/testromove"
android:layout_width="@dimen/w30"
android:layout_height="wrap_content"
android:background="@drawable/button_shape"
android:text="romove"
android:textSize="@dimen/h2"/>
</LinearLayout>
</LinearLayout>
item样式item_test.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/item_test"
android:layout_width="match_parent"
android:layout_height="@dimen/h5"
android:layout_gravity="center"
android:layout_marginBottom="@dimen/h0.5"
android:gravity="center"
android:background="@drawable/item_shape"
android:text="123"
android:textSize="@dimen/h3"/>
</LinearLayout>
边距设置TestItemDecoration
package com.holy.demo.constant;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* Copyright 2017, Smart Haier. All rights reserved.
* Description:
* Author: hanhongliang@smart-haier.com (Han Holy)
* Date: 2017/10/23
* ModifyBy:
* ModifyDate:
* ModifyDes :
*/
public class TestItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private int spanSize;
public TestItemDecoration(int spanCount, int spanSize, int spacing) {
this.spanCount = spanCount;
this.spacing = spacing;
this.spanSize = spanSize;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view); // item position
int column = position % spanCount; // item c
int width = parent.getWidth();
int childWidth = (width/spanSize)-spacing;
int tmp = (width-spacing*(spanCount-1)-childWidth*spanCount)/2;
if(spanCount == 1){
outRect.left = tmp; //
outRect.right = tmp; //
}else {
if (column == 0) {
outRect.left = tmp; //
outRect.right = spacing / 2;
} else if (column == (spanCount - 1)) {
outRect.left = spacing / 2; //
outRect.right = tmp;
} else {
outRect.left = spacing / 2; //
outRect.right = spacing / 2;
}
}
if (position >= spanCount) {
outRect.top = spacing / 2; // item top
}
}
}
以上代码中有item和button样式的shape,自己可以先去掉。xml文件中涉及到的w或h开头的尺寸值时请替换
以下是解释:
1、设置layoutmanager,mSpanSize常量24;
layoutManager = new GridLayoutManager(TestActivity.this, mSpanSize);
2、SpanSizeLookup重写这个类;按照数据的个数,以及当前item的编号,来返回其所占用的span个数。
public class TestSpanSizeLookup extends GridLayoutManager.SpanSizeLookup{
int spanCount =0;
public TestSpanSizeLookup(int spanCount) {
this.spanCount = spanCount;
}
@Override
public int getSpanSize(int position) {
int[][] count = {{24,0,0,0},{12,12,0,0},{9,6,9,0},{6,6,6,6}};
int tmp = position%spanCount;
return count[spanCount-1][tmp];
}
}
3、重新设定setSpanSizeLookup。SpanCounts是实际上每行显示的个数,应该根据实际数据数量来判断。
layoutManager.setSpanSizeLookup(new TestSpanSizeLookup(SpanCounts));
SpanCounts = mData.size()<mSpanShowSize?mData.size():mSpanShowSize;
4、计算item的边距
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view); // item 编号
int column = position % spanCount; // item 列数
int width = parent.getWidth();// RecyclerView的宽度
int childWidth = (width/spanSize)-spacing;// item的显示宽度
int tmp = (width-spacing*(spanCount-1)-childWidth*spanCount)/2;// 最左和最右的边距
if(spanCount == 1){
outRect.left = tmp; //
outRect.right = tmp; //
}else {
if (column == 0) {
outRect.left = tmp; //
outRect.right = spacing / 2;
} else if (column == (spanCount - 1)) {
outRect.left = spacing / 2; //
outRect.right = tmp;
} else {
outRect.left = spacing / 2; //
outRect.right = spacing / 2;
}
}
if (position >= spanCount) {
outRect.top = spacing / 2; // item top
}
}