本文出自 “阿敏其人” 簡(jiǎn)書(shū)博客恢暖,轉(zhuǎn)載或引用請(qǐng)注明出處。
RecyclerView不屬于MD系列踩身,但是卻常常一起使用胀茵。
一社露、Reclycler的作用和優(yōu)點(diǎn)
用來(lái)干嘛—— 代替 ListView個(gè)GridView挟阻。
- 自帶ViewHolder
- 分割線控制方便
- 橫向,豎向峭弟、列表附鸽,多行列表和流式皆可
- item增刪動(dòng)畫(huà)控制方便
二、簡(jiǎn)單使用
以前沒(méi)有RecyclerView瞒瘸,我們要使用RecyclerView可以引入下面這個(gè)
com.android.support:recyclerview-v7:23.4.0
不過(guò)你要是使用md設(shè)計(jì)坷备,也以這樣如下引用就好 (本文采用的就是這樣)
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
support:design 里面應(yīng)該包含了 support:recyclerview 。
最基本的使用:
布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/mRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#ffff0000"
android:dividerHeight="10dp"
>
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
.
.
item布局
<?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="wrap_content"
android:orientation="vertical"
android:background="#66ff0000"
android:padding="5dp"
>
<TextView
android:id="@+id/mTvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="標(biāo)題"
android:textSize="20sp"
android:padding="10dp"
android:textColor="#ff0000"
android:background="#4f92c9"
/>
<TextView
android:id="@+id/mTvDesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="描述"
android:textSize="16sp"
android:padding="10dp"
android:textColor="#3657d7"
android:background="#a9a667"
/>
</LinearLayout>
.
.
MainActivity
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private List<DataBean> mDatas;
private TestAdapter mAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
mRecyclerView = (RecyclerView) findViewById(R.id.mRecycler);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter = new TestAdapter());
}
protected void initData()
{
mDatas = new ArrayList<DataBean>();
DataBean dataBean = null;
for (int i = 0; i < 20; i++)
{
dataBean = new DataBean();
dataBean.title = "標(biāo)題 "+i;
dataBean.desc = "描述一下 "+i;
mDatas.add(dataBean);
}
}
class TestAdapter extends RecyclerView.Adapter<TestAdapter.MyViewHolder>{
// 孩子數(shù)
@Override
public int getItemCount() {
return mDatas.size();
}
// 創(chuàng)建視圖
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder myViewHolder = new MyViewHolder(LayoutInflater.from(MainActivity.this)
.inflate(R.layout.item_recy_test,parent, false));
return myViewHolder;
}
// 綁定視圖視圖 以前getView的事情 關(guān)鍵方法
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
DataBean dataBean = mDatas.get(position);
holder.mTvTitle.setText(dataBean.title);
holder.mTvDesc.setText(dataBean.desc);
}
// 必須實(shí)現(xiàn)的Holder
class MyViewHolder extends RecyclerView.ViewHolder
{
TextView mTvTitle;
TextView mTvDesc;
public MyViewHolder(View itemView) {
super(itemView);
mTvTitle = (TextView) itemView.findViewById(R.id.mTvTitle);
mTvDesc = (TextView) itemView.findViewById(R.id.mTvDesc);
}
}
}
}
.
.
效果圖
少年我知道你不是伸手黨情臭,你會(huì)自己去打一遍省撑,而且不怎么看代碼赌蔑。如果你遇到了下面的問(wèn)題,那你可以看看
- 問(wèn)題1竟秫、為什么RecyclerView的數(shù)據(jù)沒(méi)顯示出來(lái)娃惯?
遙想當(dāng)年,我們?cè)O(shè)置ListView肥败,find到ListView之后滿腦海想的就是setAdapter趾浅,然后在這里我們也是這么做,所以馒稍,沒(méi)顯示出來(lái)
很有可能是你沒(méi)設(shè)置 LayoutManager皿哨,LayoutManager對(duì)于RecyclerView是一個(gè)很重要的概念。
結(jié)果就像現(xiàn)在下圖
- 問(wèn)題2纽谒、明明就是item就是match_parent证膨,但是為什么只顯示一部分?
還是需要遙想當(dāng)年佛舱,左牽黃椎例,右擎蒼,瀟瀟灑灑地在ListView的getView方法的時(shí)候请祖,我們總是傳入layout布局订歪,然后就null,寫(xiě)順手了肆捕。
如果寫(xiě)成上圖備注的樣子刷晋,即父親為null,就會(huì)出現(xiàn)下圖的狀況
三慎陵、列表分割線
我們說(shuō)過(guò)眼虱,RecyclerView自帶設(shè)置分割線,(但是卻沒(méi)有分割線的樣式可以選席纽,必須自己實(shí)現(xiàn))
先聽(tīng)一下捏悬,回頭看一下我們之前activity_main的布局文件
我們?cè)O(shè)置分割線,但是安卓ListView的做法設(shè)置分割線沒(méi)有顯示润梯。
現(xiàn)在我們來(lái)設(shè)置分割線
RecyclerView自帶設(shè)置分割線的api
mRecyclerView.addItemDecoration((ItemDecoration decor);
我們分割線的樣式需要自己實(shí)現(xiàn)
下面附上一份分割線樣式代碼 參考
public class RecycleViewDivider extends RecyclerView.ItemDecoration {
private Paint mPaint;
private Drawable mDivider;
private int mDividerHeight = 2;//分割線高度过牙,默認(rèn)為1px
private int mOrientation;//列表的方向:LinearLayoutManager.VERTICAL或LinearLayoutManager.HORIZONTAL
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
/**
* 默認(rèn)分割線:高度為2px,顏色為灰色
*
* @param context
* @param orientation 列表方向
*/
public RecycleViewDivider(Context context, int orientation) {
if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
throw new IllegalArgumentException("請(qǐng)輸入正確的參數(shù)纺铭!");
}
mOrientation = orientation;
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
}
/**
* 自定義分割線
*
* @param context
* @param orientation 列表方向
* @param drawableId 分割線圖片
*/
public RecycleViewDivider(Context context, int orientation, int drawableId) {
this(context, orientation);
mDivider = ContextCompat.getDrawable(context, drawableId);
mDividerHeight = mDivider.getIntrinsicHeight();
}
/**
* 自定義分割線
*
* @param context
* @param orientation 列表方向
* @param dividerHeight 分割線高度
* @param dividerColor 分割線顏色
*/
public RecycleViewDivider(Context context, int orientation, int dividerHeight, int dividerColor) {
this(context, orientation);
mDividerHeight = dividerHeight;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(dividerColor);
mPaint.setStyle(Paint.Style.FILL);
}
//獲取分割線尺寸
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.set(0, 0, 0, mDividerHeight);
}
//繪制分割線
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if (mOrientation == LinearLayoutManager.VERTICAL) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
//繪制橫向 item 分割線
private void drawHorizontal(Canvas canvas, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getMeasuredWidth() - parent.getPaddingRight();
final int childSize = parent.getChildCount();
for (int i = 0; i < childSize; i++) {
final View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
final int top = child.getBottom() + layoutParams.bottomMargin;
final int bottom = top + mDividerHeight;
if (mDivider != null) {
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
if (mPaint != null) {
canvas.drawRect(left, top, right, bottom, mPaint);
}
}
}
//繪制縱向 item 分割線
private void drawVertical(Canvas canvas, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
final int childSize = parent.getChildCount();
for (int i = 0; i < childSize; i++) {
final View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getRight() + layoutParams.rightMargin;
final int right = left + mDividerHeight;
if (mDivider != null) {
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
if (mPaint != null) {
canvas.drawRect(left, top, right, bottom, mPaint);
}
}
}
}
.
.
設(shè)置分割線:
mRecyclerView = (RecyclerView) findViewById(R.id.mRecycler);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.addItemDecoration(new RecycleViewDivider(MainActivity.this, LinearLayoutManager.VERTICAL)); // 設(shè)置分割線
mRecyclerView.setAdapter(mAdapter = new TestAdapter());
發(fā)現(xiàn)只需要在原來(lái)的代碼上加上一行代碼
mRecyclerView.addItemDecoration(new RecycleViewDivider(MainActivity.this,
效果圖:
看得很辛苦寇钉,那么我們讓分割線加大一些,指定一下顏色吧
mRecyclerView.addItemDecoration(new RecycleViewDivider(MainActivity.this,
LinearLayoutManager.VERTICAL,20, Color.GRAY)); // 設(shè)置分割線
四舶赔、布局管理器
RecyclerView.LayoutManager扫倡,這是一個(gè)抽象類,系統(tǒng)提供了3個(gè)實(shí)現(xiàn)類:
LinearLayoutManager 線性管理器竟纳,支持橫向撵溃、縱向疚鲤。
GridLayoutManager 網(wǎng)格布局管理器
StaggeredGridLayoutManager 瀑布就式布局管理器
說(shuō)說(shuō)LinearLayoutManager
LinearLayoutManager 我們上面用的一直是縱向。
現(xiàn)在來(lái)設(shè)置成為橫向的:
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); // 創(chuàng)建線性布局管理器
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); // 設(shè)置線性布局為橫向(默認(rèn)為縱向)
mRecyclerView.setLayoutManager(linearLayoutManager); // 設(shè)置布局管理器
說(shuō)說(shuō)GridLayoutManager
- 縱向
mRecyclerView = (RecyclerView) findViewById(R.id.mRecycler);
mRecyclerView.setLayoutManager(new C(this,2)); // 設(shè)置布局管理器 GridView
mRecyclerView.addItemDecoration(new RecycleViewDivider(MainActivity.this, GridLayoutManager.VERTICAL)); // 設(shè)置分割線
mRecyclerView.setAdapter(mAdapter = new TestAdapter());
效果
- 橫向
利用 StaggeredGridLayoutManager
只需要一行代碼:
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.HORIZONTAL)); // 設(shè)置布局管理器 GridView
不過(guò)布局有一點(diǎn)點(diǎn)需要改動(dòng)
activity_main的RecyclerView高度需要包裹缘挑,不能填充父窗體
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/mRecycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#ffff0000"
android:dividerHeight="10dp"></android.support.v7.widget.RecyclerView>
</RelativeLayout>
item布局寬度不能填充父窗體
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="180dp"
android:layout_height="100dp"
android:orientation="vertical"
android:background="#66ff0000"
android:padding="5dp"
>
<TextView
android:id="@+id/mTvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="標(biāo)題"
android:textSize="20sp"
android:padding="10dp"
android:textColor="#ff0000"
android:background="#4f92c9"
/>
<TextView
android:id="@+id/mTvDesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="描述"
android:textSize="16sp"
android:padding="10dp"
android:textColor="#3657d7"
android:background="#a9a667"
/>
</LinearLayout>
效果:
說(shuō)說(shuō)StaggeredGridLayoutManager 流式布局
在上面的橫向布局里面石咬,我們已經(jīng)用到了StaggeredGridLayoutManager
做的東西就幾點(diǎn):
1、item布局需要在外層給出具體高度卖哎,這樣方便待會(huì)計(jì)算的時(shí)候保證最低高度
2鬼悠、在RecyclerView.Adapter的繼承類的onBindViewHolder里面動(dòng)態(tài)改變高度
接下來(lái)看代碼:
item布局里面,我們給最外層指定了最低高度100dp亏娜,最低寬度180dp
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mLlItemRoot"
android:layout_width="180dp"
android:layout_height="100dp"
android:orientation="vertical"
android:background="#66ff0000"
android:padding="5dp"
android:gravity="center"
android:layout_margin="2dp"
>
<TextView
android:id="@+id/mTvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="標(biāo)題"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:padding="10dp"
android:textColor="#ff0000"
android:background="#4f92c9"
/>
<TextView
android:id="@+id/mTvDesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="描述"
android:textSize="16sp"
android:gravity="center_horizontal"
android:padding="10dp"
android:textColor="#3657d7"
android:background="#a9a667"
/>
</LinearLayout>
.
.
接下來(lái)是adapter焕窝,主要看 onBindViewHolder方法
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.MyViewHolder>{
private List<DataBean> mDatas;
private Context mContext;
private LayoutInflater mInflater;
private List<Integer> mHeights;
public TestAdapter(Context mContext, List<DataBean> mDatas) {
mInflater = LayoutInflater.from(mContext);
this.mContext = mContext;
this.mDatas = mDatas;
mHeights = new ArrayList<Integer>();
for (int i = 0; i < mDatas.size(); i++)
{
// 這里為什么是300? 因?yàn)閕tem的高度我們給的是100.如果item不給實(shí)際高度维贺,那么lp.height拿出來(lái)的很可能是個(gè)負(fù)數(shù)
// 我給item的值100它掂,但是下面lp.height給出來(lái)的是300,才寫(xiě)了300
mHeights.add( 300+(int) (Math.random() * 100));
}
}
// 孩子數(shù)
@Override
public int getItemCount() {
return mDatas.size();
}
// 創(chuàng)建視圖
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// 應(yīng)該調(diào)用三個(gè)參數(shù)的inflate方法溯泣,傳入父親(parent)
MyViewHolder myViewHolder = new MyViewHolder(mInflater.inflate(R.layout.item_recy_test,parent, false));
return myViewHolder;
}
// 綁定視圖視圖 以前getView的事情 關(guān)鍵方法
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
ViewGroup.LayoutParams lp = holder.mLlItemRoot.getLayoutParams();
//
if(position==1){
System.out.println("===========lp.height: "+lp.height);
System.out.println("===========lp.height + 50 : "+(lp.height+50));
}
lp.height = mHeights.get(position);
holder.mLlItemRoot.setLayoutParams(lp);
DataBean dataBean = mDatas.get(position);
holder.mTvTitle.setText(dataBean.title);
holder.mTvDesc.setText(dataBean.desc);
}
// 必須實(shí)現(xiàn)的Holder
class MyViewHolder extends RecyclerView.ViewHolder
{
TextView mTvTitle;
TextView mTvDesc;
LinearLayout mLlItemRoot;
public MyViewHolder(View itemView) {
super(itemView);
mLlItemRoot = (LinearLayout) itemView.findViewById(R.id.mLlItemRoot);
mTvTitle = (TextView) itemView.findViewById(R.id.mTvTitle);
mTvDesc = (TextView) itemView.findViewById(R.id.mTvDesc);
}
}
}
還要需要注意的就是這句代碼
mHeights.add( 300+(int) (Math.random() * 100));
這里為什么是300虐秋? 因?yàn)閕tem的高度我們給的是100.如果item不給實(shí)際高度,那么lp.height拿出來(lái)的很可能是個(gè)負(fù)數(shù)
我給item的值100垃沦,但是下面lp.height給出來(lái)的是300客给,才寫(xiě)了300
為什么要把高度寫(xiě)在集合里面而不寫(xiě)在onBindViewHolder?
如果這樣循環(huán)往復(fù)拉動(dòng)幾次會(huì)出現(xiàn)什么狀況?高度越來(lái)越大肢簿,每次都上在上次的基礎(chǔ)上擴(kuò)大靶剑。
.
.
MainActivity
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private List<DataBean> mDatas;
private TestAdapter mAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
mRecyclerView = (RecyclerView) findViewById(R.id.mRecycler);
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL)); // 設(shè)置布局管理器 GridView
mRecyclerView.addItemDecoration(new DividerGridItemDecoration(MainActivity.this)); // 設(shè)置分割線
mRecyclerView.setAdapter(mAdapter = new TestAdapter(MainActivity.this,mDatas));
}
protected void initData()
{
mDatas = new ArrayList<DataBean>();
DataBean dataBean = null;
for (int i = 0; i < 20; i++)
{
dataBean = new DataBean();
dataBean.title = "標(biāo)題 "+i;
dataBean.desc = "描述一下 "+i;
mDatas.add(dataBean);
}
}
}
.
.
在上面的代碼這句
mRecyclerView.addItemDecoration(new DividerGridItemDecoration(MainActivity.this)); // 設(shè)置分割線
我們還用到了一個(gè)分割線文件
附上分割線文件,參考csdn 鴻洋
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration
{
private static final int[] ATTRS = new int[] { android.R.attr.listDivider };
private Drawable mDivider;
public DividerGridItemDecoration(Context context)
{
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
}
@Override
public void onDraw(Canvas c, RecyclerView parent, State state)
{
drawHorizontal(c, parent);
drawVertical(c, parent);
}
private int getSpanCount(RecyclerView parent)
{
// 列數(shù)
int spanCount = -1;
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
spanCount = ((StaggeredGridLayoutManager) layoutManager)
.getSpanCount();
}
return spanCount;
}
public void drawHorizontal(Canvas c, RecyclerView parent)
{
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++)
{
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getLeft() - params.leftMargin;
final int right = child.getRight() + params.rightMargin
+ mDivider.getIntrinsicWidth();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawVertical(Canvas c, RecyclerView parent)
{
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++)
{
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getTop() - params.topMargin;
final int bottom = child.getBottom() + params.bottomMargin;
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
int childCount)
{
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{
if ((pos + 1) % spanCount == 0)// 如果是最后一列池充,則不需要繪制右邊
{
return true;
}
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL)
{
if ((pos + 1) % spanCount == 0)// 如果是最后一列桩引,則不需要繪制右邊
{
return true;
}
} else
{
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// 如果是最后一列,則不需要繪制右邊
return true;
}
}
return false;
}
private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
int childCount)
{
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// 如果是最后一行收夸,則不需要繪制底部
return true;
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
// StaggeredGridLayoutManager 且縱向滾動(dòng)
if (orientation == StaggeredGridLayoutManager.VERTICAL)
{
childCount = childCount - childCount % spanCount;
// 如果是最后一行坑匠,則不需要繪制底部
if (pos >= childCount)
return true;
} else
// StaggeredGridLayoutManager 且橫向滾動(dòng)
{
// 如果是最后一行,則不需要繪制底部
if ((pos + 1) % spanCount == 0)
{
return true;
}
}
}
return false;
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition,
RecyclerView parent)
{
int spanCount = getSpanCount(parent);
int childCount = parent.getAdapter().getItemCount();
if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,則不需要繪制底部
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,則不需要繪制右邊
{
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(),
mDivider.getIntrinsicHeight());
}
}
}
效果:
感覺(jué)有點(diǎn)丑噪馏,后面我們可以用CradView感覺(jué)就會(huì)好很多。
五手幢、item的增刪動(dòng)畫(huà) ItemAnimator
我們之前ListView的時(shí)候捷凄,通知試圖刷新變化調(diào)用的是 notifyDataSetChanged()
而在RecyclerView忱详,除了notifyDataSetChanged(),還有
- 添加 notifyItemInserted(position) 指定位置添加一個(gè)item
- 刪除 notifyItemRemoved(position) 指定刪除哪一個(gè)item
既然知道怎么添加和刪除跺涤,那么我們就在adapter里面新增兩個(gè)對(duì)用的方法
public void addData(int position)
{
DataBean tempAdd = new DataBean();
tempAdd.title="新增標(biāo)題";
tempAdd.desc="新增描述";
mDatas.add(position, tempAdd);
mHeights.add( 300+(int) (Math.random() * 100));
notifyItemInserted(position);
}
public void removeData(int position)
{
mDatas.remove(position);
notifyItemRemoved(position);
}
然后我們弄兩個(gè)按鈕匈睁,點(diǎn)擊就調(diào)用方法
mTvAdd = (TextView) findViewById(R.id.mTvAdd);
mTvAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAdapter.addData(1);
}
});
mTvRemove = (TextView) findViewById(R.id.mTvRemove);
mTvRemove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAdapter.removeData(1);
}
});
效果圖
我們發(fā)現(xiàn)這個(gè)增刪的過(guò)程還是有動(dòng)畫(huà)的监透,這是RecyclerView我們實(shí)現(xiàn)的默認(rèn)動(dòng)畫(huà)。
mRecyclerView = (RecyclerView) findViewById(R.id.mRecycler);
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL)); // 設(shè)置布局管理器 GridView
mRecyclerView.addItemDecoration(new DividerGridItemDecoration(MainActivity.this)); // 設(shè)置分割線
// 設(shè)置item動(dòng)畫(huà)
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.setAdapter(mAdapter = new TestAdapter(MainActivity.this,mDatas));
我們也可以利用ItemAnimator()實(shí)現(xiàn)動(dòng)畫(huà)航唆。
六胀蛮、自己實(shí)現(xiàn)點(diǎn)擊事件
RecyclerView沒(méi)有點(diǎn)擊回調(diào)的方法方法可以用,我們需要自己實(shí)現(xiàn)
// 點(diǎn)擊回調(diào)
public interface OnItemClickLitener
{
void onItemClick(View view, int position); // 點(diǎn)擊
void onItemLongClick(View view , int position); // 長(zhǎng)按
}
private OnItemClickLitener mOnItemClickLitener;
public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
{
this.mOnItemClickLitener = mOnItemClickLitener;
}
// 綁定視圖視圖 以前getView的事情 關(guān)鍵方法
@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
ViewGroup.LayoutParams lp = holder.mLlItemRoot.getLayoutParams();
// 如果設(shè)置了回調(diào)糯钙,則設(shè)置點(diǎn)擊事件
if (mOnItemClickLitener != null)
{
holder.itemView.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
int pos = holder.getLayoutPosition();
mOnItemClickLitener.onItemClick(holder.itemView, pos);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener()
{
@Override
public boolean onLongClick(View v)
{
int pos = holder.getLayoutPosition();
mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
return false;
}
});
}
}
效果圖
到這里粪狼,回顧開(kāi)頭我們說(shuō)的
- 自帶ViewHolder
- 分割線控制方便
- 橫向,豎向任岸、列表再榄,多行列表和流式皆可
- item增刪動(dòng)畫(huà)控制方便
都言及了。
七享潜、CardView 的配合使用
對(duì)于CardView困鸥,你可以簡(jiǎn)單的認(rèn)為它是一個(gè)使用了Material Desgin風(fēng)格的FrameLayout,只不過(guò)比普通的FrameLayout多了圓角背景和陰影效果剑按。所以它常用作ListView 或者 RecyclerView等視圖Item的布局容器疾就;
我們自然可以聯(lián)想到它的使用跟FrameLayout非常相似,只不多多了幾個(gè)用于控制圓角艺蝴、陰影等自身特有的屬性:
design里面沒(méi)有包含CardView
所以我們還需要引入CardView
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
compile 'com.android.support:cardview-v7:23.4.0' // 引入 cardview
接下來(lái)猬腰,把itme的布局文件最外層設(shè)置為CardView
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mCvItemRoot"
android:layout_width="180dp"
android:layout_height="100dp"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
card_view:cardBackgroundColor="@color/cardview_dark_background"
card_view:cardCornerRadius="10dp"
card_view:cardElevation="8dp"
android:layout_margin="3dp"
>
<LinearLayout
android:id="@+id/mLlItemRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp"
android:gravity="center"
android:layout_margin="2dp">
<TextView
android:id="@+id/mTvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="標(biāo)題"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:padding="10dp"
android:textColor="#ff0000"
android:background="#4f92c9"
/>
<TextView
android:id="@+id/mTvDesc"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="描述"
android:textSize="16sp"
android:gravity="center_horizontal"
android:padding="10dp"
android:textColor="#3657d7"
android:background="#a9a667"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
其中關(guān)鍵代碼無(wú)非這兩句
card_view:cardBackgroundColor="@color/cardview_dark_background"
card_view:cardCornerRadius="10dp"
card_view:cardElevation="8dp"
第一句指定顏色
第二句指定圓角角度
第三句指定陰影
當(dāng)然Adapter也有相應(yīng)的變化
起始就是把原來(lái)的 LinearLayout 換成 CardView ,計(jì)算高度也是換成 CardView 而已猜敢,為了讓流式效果明顯一些漆诽,我們把隨機(jī)生成的高度弄大了一些。
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.MyViewHolder>{
private List<DataBean> mDatas;
private Context mContext;
private LayoutInflater mInflater;
private List<Integer> mHeights;
public TestAdapter(Context mContext, List<DataBean> mDatas) {
mInflater = LayoutInflater.from(mContext);
this.mContext = mContext;
this.mDatas = mDatas;
mHeights = new ArrayList<Integer>();
for (int i = 0; i < mDatas.size(); i++)
{
// 這里為什么是300锣枝? 因?yàn)閕tem的高度我們給的是100.如果item不給實(shí)際高度厢拭,那么lp.height拿出來(lái)的很可能是個(gè)負(fù)數(shù)
// 我給item的值100,但是下面lp.height給出來(lái)的是300撇叁,才寫(xiě)了300
mHeights.add( 300+(int) (Math.random() * 200));
}
}
// 孩子數(shù)
@Override
public int getItemCount() {
return mDatas.size();
}
// 創(chuàng)建視圖
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// 應(yīng)該調(diào)用三個(gè)參數(shù)的inflate方法供鸠,傳入父親(parent)
MyViewHolder myViewHolder = new MyViewHolder(mInflater.inflate(R.layout.item_recy_test,parent, false));
return myViewHolder;
}
// 點(diǎn)擊回調(diào)
public interface OnItemClickLitener
{
void onItemClick(View view, int position); // 點(diǎn)擊
void onItemLongClick(View view , int position); // 長(zhǎng)按
}
private OnItemClickLitener mOnItemClickLitener;
public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
{
this.mOnItemClickLitener = mOnItemClickLitener;
}
// 綁定視圖視圖 以前getView的事情 關(guān)鍵方法
@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
// 如果設(shè)置了回調(diào),則設(shè)置點(diǎn)擊事件
if (mOnItemClickLitener != null)
{
holder.itemView.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
int pos = holder.getLayoutPosition();
mOnItemClickLitener.onItemClick(holder.itemView, pos);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener()
{
@Override
public boolean onLongClick(View v)
{
int pos = holder.getLayoutPosition();
mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
return false;
}
});
}
ViewGroup.LayoutParams lp = holder.mCvItemRoot.getLayoutParams();
if(position==1){
System.out.println("===========lp.height: "+lp.height);
System.out.println("===========lp.height + 50 : "+(lp.height+50));
}
lp.height = mHeights.get(position);
holder.mCvItemRoot.setLayoutParams(lp);
DataBean dataBean = mDatas.get(position);
holder.mTvTitle.setText(dataBean.title);
holder.mTvDesc.setText(dataBean.desc);
}
// 必須實(shí)現(xiàn)的Holder
class MyViewHolder extends RecyclerView.ViewHolder
{
TextView mTvTitle;
TextView mTvDesc;
LinearLayout mLlItemRoot;
CardView mCvItemRoot;
public MyViewHolder(View itemView) {
super(itemView);
mCvItemRoot = (CardView) itemView.findViewById(R.id.mCvItemRoot);
mLlItemRoot = (LinearLayout) itemView.findViewById(R.id.mLlItemRoot);
mTvTitle = (TextView) itemView.findViewById(R.id.mTvTitle);
mTvDesc = (TextView) itemView.findViewById(R.id.mTvDesc);
}
}
public void addData(int position)
{
DataBean tempAdd = new DataBean();
tempAdd.title="新增標(biāo)題";
tempAdd.desc="新增描述";
mDatas.add(position, tempAdd);
mHeights.add( 300+(int) (Math.random() * 100));
notifyItemInserted(position);
}
public void removeData(int position)
{
mDatas.remove(position);
notifyItemRemoved(position);
}
}
八陨闹、下拉刷新和上拉加載更多
ANDROID官方的SwipeRefreshLayout可用于刷新楞捂,但是這個(gè)只是做下拉刷新。
而下拉刷新需要判斷列表是否抵達(dá)底部趋厉、
所以需要注意兩點(diǎn):
一是用 SwipeRefreshLayout 包裹 RecyclerView 實(shí)現(xiàn)下拉刷新寨闹。
下拉刷新是通過(guò)實(shí)現(xiàn) SwipeRefreshLayout.OnRefreshListener 接口來(lái)實(shí)現(xiàn)的,也就是說(shuō)下拉刷新具有了通用性君账,不只是 RecyclerView ;
二是滑倒底部的時(shí)候自動(dòng)加載實(shí)現(xiàn)加載更多繁堡。
加載更多要通過(guò) LayoutManager 來(lái)獲取 RecyclerView 是否滑動(dòng)到底部來(lái)實(shí)現(xiàn)。
下拉刷新
說(shuō)起來(lái)就是幾步
1、RecyclerView 外層需要包一個(gè) SwipeRefreshLayout
2椭蹄、給SwipeRefreshLayout設(shè)置顏色
mSwipeRefresh.setColorSchemeResources(
R.color.google_blue,
R.color.google_green,
R.color.google_red,
R.color.google_yellow
);
3闻牡、回調(diào)接口
mSwipeRefresh.setOnRefreshListener(this); //復(fù)寫(xiě)onRefresh方法 做下拉刷新
...
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
DataBean addTemp = new DataBean();
addTemp.title = "標(biāo)題 下拉新增";
addTemp.desc = "desc 下拉新增";
mDatas.add(0,addTemp);
mAdapter.notifyDataSetChanged();
mSwipeRefresh.setRefreshing(false); //
}
}, 1000);
}
大概步驟如上。
接下來(lái)看代碼:
簡(jiǎn)單的下拉刷新的實(shí)現(xiàn)
<?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"
>
<LinearLayout
...
</LinearLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/mSwipeRefresh"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/mRecycler"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:divider="#ffff0000"
android:dividerHeight="10dp">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
Mainactivity
public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
private RecyclerView mRecyclerView;
private TextView mTvAdd;
private TextView mTvRemove;
private List<DataBean> mDatas;
private TestAdapter mAdapter;
private SwipeRefreshLayout mSwipeRefresh;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvAdd = (TextView) findViewById(R.id.mTvAdd);
mTvAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAdapter.addData(1);
}
});
mTvRemove = (TextView) findViewById(R.id.mTvRemove);
mTvRemove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAdapter.removeData(1);
}
});
mRecyclerView = (RecyclerView) findViewById(R.id.mRecycler);
initData();
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL)); // 設(shè)置布局管理器 GridView
mRecyclerView.addItemDecoration(new DividerGridItemDecoration(MainActivity.this)); // 設(shè)置分割線
mRecyclerView.setItemAnimator(new DefaultItemAnimator()); // 設(shè)置item動(dòng)畫(huà)
mRecyclerView.setAdapter(mAdapter = new TestAdapter(MainActivity.this,mDatas));
mAdapter.setOnItemClickLitener(new TestAdapter.OnItemClickLitener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this,"點(diǎn)擊:"+position,Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position) {
Toast.makeText(MainActivity.this,"長(zhǎng)按:"+position,Toast.LENGTH_SHORT).show();
}
});
// 刷新
mSwipeRefresh = (SwipeRefreshLayout) findViewById(R.id.mSwipeRefresh);
// 刷新的時(shí)候的顏色
mSwipeRefresh.setColorSchemeResources(
R.color.google_blue,
R.color.google_green,
R.color.google_red,
R.color.google_yellow
);
// implements SwipeRefreshLayout.OnRefreshListener
mSwipeRefresh.setOnRefreshListener(this); //復(fù)寫(xiě)onRefresh方法 做下拉刷新
// 刷新
}
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
DataBean addTemp = new DataBean();
addTemp.title = "標(biāo)題 下拉新增";
addTemp.desc = "desc 下拉新增";
mDatas.add(0,addTemp);
mAdapter.notifyDataSetChanged();
mSwipeRefresh.setRefreshing(false); //
}
}, 1000);
}
protected void initData()
{
mDatas = new ArrayList<DataBean>();
DataBean dataBean = null;
for (int i = 0; i < 20; i++)
{
dataBean = new DataBean();
dataBean.title = "標(biāo)題 "+i;
dataBean.desc = "描述一下 "+i;
mDatas.add(dataBean);
}
}
}
.
.
效果圖
上拉加載更多數(shù)據(jù)
上拉加載更多用一個(gè)新的頁(yè)面展示绳矩,更加清晰罩润。
主要就是完成從下面這幾步:
- 1、Adapter 定義兩個(gè)常量區(qū)分普通item視圖和腳部
private static final int TYPE_ITEM = 0;
private static final int TYPE_FOOTER = 1;
- 2翼馆、Adapter getItemCount()方法為腳部做調(diào)整
@Override
public int getItemCount() {
return mDatas.size() == 0 ? 0 : mDatas.size() + 1;
}
- 3割以、C利用getItemViewType做視圖判斷
@Override
public int getItemViewType(int position) {
if (position + 1 == getItemCount()) {
return TYPE_FOOTER;
} else {
return TYPE_ITEM;
}
}
- 4、Adapter onCreateViewHolder 初始化視圖
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
View view = LayoutInflater.from(context).inflate(R.layout.item_base, parent,
false);
return new ItemViewHolder(view);
} else if (viewType == TYPE_FOOTER) {
View view = LayoutInflater.from(context).inflate(R.layout.item_foot, parent,
false);
return new FootViewHolder(view);
}
return null;
}
- 5应媚、Adapter onBindViewHolder 幫頂視圖做區(qū)分
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
if (holder instanceof ItemViewHolder) {
if (onItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getLayoutPosition();
onItemClickListener.onItemClick(holder.itemView, position);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int position = holder.getLayoutPosition();
onItemClickListener.onItemLongClick(holder.itemView, position);
return false;
}
});
}
ItemViewHolder itemViewHolder = (ItemViewHolder)holder;
itemViewHolder.mTvName.setText(mDatas.get(position));
}
}
- 6拳球、在Activity里面的scroll監(jiān)聽(tīng)做上拉要執(zhí)行的邏輯
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
Log.d("test", "StateChanged = " + newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
Log.d("test", "onScrolled");
int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
if (lastVisibleItemPosition + 1 == adapter.getItemCount()) {
Log.d("test", "loading executed");
boolean isRefreshing = swipeRefreshLayout.isRefreshing();
if (isRefreshing) {
adapter.notifyItemRemoved(adapter.getItemCount());
return;
}
if (!isLoading) {
isLoading = true;
handler.postDelayed(new Runnable() {
@Override
public void run() {
mDatas.add("上拉 加載 很多數(shù)據(jù)");
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
adapter.notifyItemRemoved(adapter.getItemCount());
Log.d("test", "load more completed");
isLoading = false;
}
}, 1000);
}
}
}
});
大概就是這么些步驟,珍特,下面附上代碼祝峻。
public class RefreshActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private SwipeRefreshLayout swipeRefreshLayout;
boolean isLoading;
private List<String> mDatas = new ArrayList<>();
private RecyclerViewAdapter adapter = new RecyclerViewAdapter(this, mDatas);
private Handler handler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_refresh);
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
initView();
initData();
}
public void initView() {
swipeRefreshLayout.setColorSchemeResources(
R.color.google_blue,
R.color.google_green,
R.color.google_red,
R.color.google_yellow
);
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
}
});
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() { // 下拉加載新數(shù)據(jù)
handler.postDelayed(new Runnable() {
@Override
public void run() {
mDatas.add(0,"下拉刷新出來(lái)的新數(shù)據(jù)");
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
adapter.notifyItemRemoved(adapter.getItemCount());
}
}, 2000);
}
});
final LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
Log.d("test", "StateChanged = " + newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
Log.d("test", "onScrolled");
int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
if (lastVisibleItemPosition + 1 == adapter.getItemCount()) {
Log.d("test", "loading executed");
boolean isRefreshing = swipeRefreshLayout.isRefreshing();
if (isRefreshing) {
adapter.notifyItemRemoved(adapter.getItemCount());
return;
}
if (!isLoading) {
isLoading = true;
handler.postDelayed(new Runnable() {
@Override
public void run() {
mDatas.add("上拉 加載 很多數(shù)據(jù)");
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
adapter.notifyItemRemoved(adapter.getItemCount());
Log.d("test", "load more completed");
isLoading = false;
}
}, 1000);
}
}
}
});
//添加點(diǎn)擊事件
adapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Log.d("test", "item position = " + position);
}
@Override
public void onItemLongClick(View view, int position) {
}
});
}
public void initData() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
getData();
}
}, 1500);
}
/**
* 獲取測(cè)試數(shù)據(jù)
*/
private void getData() {
for (int i = 0; i < 20; i++) {
mDatas.add("數(shù)據(jù) "+i);
}
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
adapter.notifyItemRemoved(adapter.getItemCount());
}
}
.
.
頁(yè)面布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"></android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
.
.
item布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="6dp"
android:orientation="vertical"
app:cardBackgroundColor="@color/colorPrimary"
app:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true"
app:contentPadding="6dp">
<TextView
android:id="@+id/mTvName"
android:layout_width="match_parent"
android:layout_height="36dp"
android:gravity="center"
android:textColor="#ffffff"
android:text="你好色彩" />
</android.support.v7.widget.CardView>
.
.
Adapter
public class RecyclerViewAdapter extends Adapter<ViewHolder> {
private static final int TYPE_ITEM = 0;
private static final int TYPE_FOOTER = 1;
private Context context;
private List<String> mDatas;
public RecyclerViewAdapter(Context context, List<String> mDatas) {
this.context = context;
this.mDatas = mDatas;
}
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
@Override
public int getItemCount() {
return mDatas.size() == 0 ? 0 : mDatas.size() + 1;
}
@Override
public int getItemViewType(int position) {
if (position + 1 == getItemCount()) {
return TYPE_FOOTER;
} else {
return TYPE_ITEM;
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
View view = LayoutInflater.from(context).inflate(R.layout.item_base, parent,
false);
return new ItemViewHolder(view);
} else if (viewType == TYPE_FOOTER) {
View view = LayoutInflater.from(context).inflate(R.layout.item_foot, parent,
false);
return new FootViewHolder(view);
}
return null;
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
if (holder instanceof ItemViewHolder) {
if (onItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getLayoutPosition();
onItemClickListener.onItemClick(holder.itemView, position);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int position = holder.getLayoutPosition();
onItemClickListener.onItemLongClick(holder.itemView, position);
return false;
}
});
}
ItemViewHolder itemViewHolder = (ItemViewHolder)holder;
itemViewHolder.mTvName.setText(mDatas.get(position));
}
}
static class ItemViewHolder extends ViewHolder {
TextView mTvName;
public ItemViewHolder(View view) {
super(view);
mTvName = (TextView) view.findViewById(R.id.mTvName);
}
}
static class FootViewHolder extends ViewHolder {
public FootViewHolder(View view) {
super(view);
}
}
九、其他
1扎筒、反序
比如服務(wù)器給我們返回一組日期從早到晚的數(shù)組莱找,我們也用的很開(kāi)心,但是突然說(shuō)要降序嗜桌,我們不用自己寫(xiě)比較器奥溺,只需要利用下面代碼反序展示就好
mLinearLayoutManager = new LinearLayoutManager(this);
// 兩者一起使用,才能使得反轉(zhuǎn)后從上方開(kāi)始展示
mLinearLayoutManager.setReverseLayout(true);//列表翻轉(zhuǎn)
mLinearLayoutManager.setStackFromEnd(true);//列表再底部開(kāi)始展示骨宠,反轉(zhuǎn)后由上面開(kāi)始展示
rvGroupList.setLayoutManager(mLinearLayoutManager);
2浮定、跳轉(zhuǎn)到指定位置
在Adapter里面添加如下方法,需要地方調(diào)用即可
public static void moveToPosition(LinearLayoutManager manager, int position) {
manager.scrollToPositionWithOffset(position, 0);
manager.setStackFromEnd(true);
}
調(diào)用示例
mHideMsgLogAdapter.moveToPosition(mLinearLayoutManager,messList.size()-1);
3层亿、RecyclerView自身無(wú)效的解決辦法
有時(shí)候桦卒,我們的RecyclerView只是展示數(shù)據(jù),不需要item點(diǎn)擊匿又。
但是整個(gè)RecyclerView有需要實(shí)現(xiàn)點(diǎn)擊事件執(zhí)行一定的操作方灾。
我們發(fā)現(xiàn),直接對(duì)RecyclerView直接setOnClickListener是無(wú)效的碌更。
既然默認(rèn)無(wú)效裕偿,那就只能從Touch機(jī)制入手了。
比如
mRvRecent.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// 即可點(diǎn)擊整個(gè)RecyclerView
}
return false;
}
});
4痛单、RecyclerView中的Edittext監(jiān)聽(tīng)
當(dāng)我們對(duì)RecyclerView中的Edittext進(jìn)行內(nèi)容監(jiān)聽(tīng)的時(shí)候嘿棘。
很可能會(huì)出現(xiàn)position亂掉的問(wèn)題。
那么解決辦法就是給Edittext設(shè)置Tag旭绒。
if (holder.mEtContent.getTag() instanceof TextWatcher) {
holder.mEtContent.removeTextChangedListener((TextWatcher) holder.mEtContent.getTag());
}
holder.mEtContent.setText(foosBean.content);
TextWatcher textWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
String cont = editable.toString().trim();
mList.get(position).content = cont;
// 如果倍數(shù)輸入框有值鸟妙,那么就把置為選中狀態(tài)
if (!TextUtils.isEmpty(cont) && !cont.equals("0")) {
foosBean.isSelect = true;
} else {
foosBean.isSelect = false;
}
}
};
holder.mEtContent.addTextChangedListener(textWatcher);
holder.mEtContent.setTag(textWatcher);
.
.
就到這里吧焦人。
如有興趣,可另外看下他篇:
MD系列2圆仔、ToolBar+DrawerLayout + NavigationView
Md系列3、CoordinatorLayout 里 Toobar和TabLayout等發(fā)生的一系列故事
本篇完蔫劣。
參考:
RecyclerView下拉刷新上拉加載
Android RecyclerView 使用完全解析 體驗(yàn)藝術(shù)般的控件
使用SwipeRefreshLayout和RecyclerView實(shí)現(xiàn)仿“簡(jiǎn)書(shū)”下拉刷新和上拉加載更多