前言
上一篇文章揭開RecyclerView的神秘面紗(二):處理RecyclerView的點擊事件中,筆者主要講述了處理RecyclerView點擊事件的幾種方法,處理點擊事件是為了實現(xiàn)與用戶的交互,那么實現(xiàn)什么交互呢?對于一個視圖數(shù)據(jù)來說,無非有如下可能:添加數(shù)據(jù)和刪除數(shù)據(jù)和修改數(shù)據(jù),接下來將講述怎么操作數(shù)據(jù)和添加分割線。本系列所有源碼看下方下載地址。
操作數(shù)據(jù)
首先我們看看官方提供了什么API給我們調用,我們看RecyclerView.Adapter中提供的四個方法如下:
//該方法用于當增加一個數(shù)據(jù)的時候,position表示新增數(shù)據(jù)顯示的位置
final void notifyItemInserted(int position)
//該方法用于刪除一個數(shù)據(jù)的時候,position表示數(shù)據(jù)刪除的位置
final void notifyItemRemoved(int position)
//該方法表示所在position對應的item位置不會改變,但是該item內容發(fā)生變化
final void notifyItemChanged(int position)
//該方法一般用于:適配器之前裝載的數(shù)據(jù)大部分已經過時了,需要重新更新數(shù)據(jù)
//調用該方法的時候,recyclerView會重新計算子item及所有子item重新布局
//出于效率考慮,官方建議用更加精確的方法(比如上面三個方法)來取代這個方法
final void notifyDataSetChanged()
為了直觀地看到各個方法的用法,我們對原有代碼進行修改,看看具體的運行結果如何。
首先對MyAdapter.java修改,新增三個方法:
//移除數(shù)據(jù)
public void removeData(int position) {
mDataSet.remove(position);
notifyItemRemoved(position);
}
//新增數(shù)據(jù)
public void addData(int position){
mDataSet.add(position,"Add One");
notifyItemInserted(position);
}
//更改某個位置的數(shù)據(jù)
public void changeData(int position){
mDataSet.set(position,"Item has changed"+ count++);
notifyItemChanged(position);
}
接下來我們在MainActivity.java中調用這三個方法:
mRecyclerView.addOnItemTouchListener(new RecyclerViewClickListener2(this, mRecyclerView,
new RecyclerViewClickListener2.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, "Click " + mData.get(position), Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position) {
//長按某個item后,將移除這個item
mAdapter.removeData(position);
}
}));
...
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_search:
Toast.makeText(MainActivity.this, "Search !", Toast.LENGTH_LONG).show();
break;
case R.id.action_add:
mAdapter.addData(1);
break;
case R.id.action_change:
mAdapter.changeData(2);
break;
}
return true;
}
});
接下來,我們看看選擇不同選項時的運行結果是怎樣的:



可以看出,數(shù)據(jù)都被正確地增加、刪除、修改成功了。我們再仔細觀察一下,每一個Item變化的時候都不是瞬間變化的,它都會有一個動畫效果,使得用戶體驗很好,其實這里面使用了RecyclerView默認提供的動畫效果:
//這一句不是必要的,因為RecyclerView會默認使用
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
還記得在第一節(jié)的時候提到過RecyclerView.ItemAnimator這個抽象類,它是用于控制Item的動畫效果的,而DefaultItemAnimator()正是它的一個實現(xiàn)類。這也說明了,我們可以自定義實現(xiàn)很多特效動畫。至于它的各種動畫效果,可以參考GitHub上的開源項目:RecyclerViewItemAnimators,這里就不展開來講了。
添加分割線
細心的同學會發(fā)現(xiàn),筆者的RecyclerView每個Item之間是用magin屬性來分開的,那么RecycleView有沒有像ListView那樣可以直接在xml屬性中添加android:divider呢?答案是沒有的,可能是考慮到RecycleView靈活多變的特點,官方沒有添加這個屬性,不過我們還是有辦法可以手動去添加。在第一節(jié)有提到過:RecyclerView.ItemDecoration這個抽象類,它是用于添加分割線的,按照慣例,我們看看這個抽象類里面有什么方法:
public static abstract class ItemDecoration {
public void onDraw(Canvas c, RecyclerView parent, State state) {
onDraw(c, parent);
}
public void onDrawOver(Canvas c, RecyclerView parent, State state) {
onDrawOver(c, parent);
}
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(),
parent);
}
}
省略了三個官方不提倡的用法,這里簡單說明一下以上各個方法的作用:
onDraw和onDrawOver,顯然,這兩個方法是用于繪制的,那么繪制分割線的邏輯可以放在這里面,它們二者的具體區(qū)別是:onDraw是在item view繪制之前調用,而onDrawOver是在item view繪制之后調用,因此我們一般選擇重寫其中一個方法即可。getItemOffsets,這個方法是告訴RecyclerView在繪制完一個item view的時候,應該留下多少空位,以便于繪制分割線。
既然知道了這三個方法的作用,那么我們來寫一個實現(xiàn)類,新建DividerItemDecoration(注:該類參考自Android官方):
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
//使用系統(tǒng)自帶的listDivider
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context,int orientation){
//使用TypeArray加載該系統(tǒng)資源
final TypedArray ta = context.obtainStyledAttributes(ATTRS);
mDivider = ta.getDrawable(0);
//緩存
ta.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation){
if(orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if(mOrientation == VERTICAL_LIST){
drawVertical(c,parent);
}else{
drawHorizontal(c,parent);
}
}
public void drawVertical(Canvas c,RecyclerView parent){
//獲取分割線的左邊距,即RecyclerView的padding值
final int left = parent.getPaddingLeft();
//分割線右邊距
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
//遍歷所有item view,為它們的下方繪制分割線
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.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left,top,right,bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
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 left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if(mOrientation == VERTICAL_LIST){
//設置偏移的高度是mDivider.getIntrinsicHeight,該高度正是分割線的高度
outRect.set(0,0,0,mDivider.getIntrinsicHeight());
}else{
outRect.set(0,0,mDivider.getIntrinsicWidth(),0);
}
}
}
接著在MainActivity.java添加如下代碼:
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
最后,我們把之前在itemlayout.xml添加的margin值去掉,那么我們看看運行結果:

可以看出,出現(xiàn)了淺灰色的分割線。這里面用的是系統(tǒng)自帶的分割線樣式,我們完全可以使用自定義樣式來取代系統(tǒng)的分割線,關于繪制分割線的更多知識,比如說網格布局的分割線的繪制,可以參考如下的博客:
關于RecyclerView的用法講解至此,縱觀整個使用流程,其靈活性、可定制性還是很高的,特別是結合另一個控件CardView,能使得它看起來更加優(yōu)雅,有興趣的可以去了解一下二者的結合使用。
源碼下載地址:
http://download.csdn.net/detail/a553181867/9518275
更多閱讀:
揭開RecyclerView的神秘面紗(一):RecyclerView的基本使用
揭開RecyclerView的神秘面紗(二):處理RecyclerView的點擊事件