Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android 打造真正的下拉刷新上拉加載recyclerview(二):添加刪除頭尾部

android 打造真正的下拉刷新上拉加載recyclerview(二):添加刪除頭尾部

編輯:關於Android編程

前言

在上一篇文章中,我們介紹了下拉刷新上拉加載RecyclerView的使用,從這篇開始,我將對這個項目的具體實現詳細介紹,這篇首先介紹添加刪除頭尾部的實現。

大家都知道recyclerview並不能像listview一樣,可以直接使用addHeaderView和addFooterView添加頭部和尾部,而在實際的項目中,我們常常需要實現這些功能。既然recyclerview不提供,那我們就自己寫一個呗。

但是啊,添加有了,刪除頭尾部的介紹網上比較少。看過鴻洋大神這篇文章的同學想必都知道,給recyclerview添加頭尾部,其實就是 在adapter中使用鍵值數組對存儲頭部和尾部,addHeaderView其實就是往頭部的鍵值對數組中添加view。那麼removeHeaderView呢?當然就是從頭部的鍵值對數組中刪除view啦!!沒錯,就是這麼簡單。

這時候,有同學把網上的代碼拷下來,開始在adapter中添加removeHeaderView方法,恩,完美。添加View0,添加View1,刪除View1,哦!刪除成功,添加View2,哎?怎麼添加的是View1??我的View2呢?

不要問我怎麼知道的,因為我就這樣干過。

添加刪除頭尾部

那麼我們還是先一步步來介紹下頭尾部的實現吧,recyclerview的adapter需要實現以下幾個方法:

創建ViewHolder:onCreateViewHolder(ViewGroup parent, int viewType); 綁定數據:onBindViewHolder(RecyclerView.ViewHolder holder, int position); 獲得item的總數:getItemCount();

恩,跟ListView還是有點不一樣的。在ListView的時候,當我們需要展示幾種類型的item時,我們會使用到一個方法:

獲得item的類型:getItemViewType(int position);

幸運的是,recyclerview中也提供這個方法,有這個方法就好辦了,我們把頭尾部當成是一種item不就好了嘛,用鍵值對數組保存頭尾部,類型type作為key,view作為value。

因此思路就是:

使用鍵值對數組(ArrayList、HashMap、SparseArrayCompat等)保存頭尾部 在getItemViewType方法中,判斷是否頭尾部,是的話返回對應的key,也就是type 在onCreateViewHolder中,判斷是否是頭尾部,是的話返回對應的ViewHolder 在onBindViewHolder中,判斷是否是頭尾部,是的話不處理

大概就是這樣,下面我們看看這部分代碼,一個一個來:

首先,鍵值對保存頭尾部:

protected SparseArrayCompat mHeaderViews = new SparseArrayCompat<>();
protected SparseArrayCompat mFooterViews = new SparseArrayCompat<>();

然後,getItemCount方法,返回item數量+頭尾部總數:

@Override
public int getItemCount() {
    return getRealItemCount()+getHeadersCount()+getFootersCount();
}

再然後,getItemViewType方法:

@Override
public int getItemViewType(int position) {
    //如果是頭部,返回對應的type
    if (isHeaderPosition(position)) {
        return mHeaderViews.keyAt(position);
    }
    //如果是尾部,返回對應的type
    if (isFooterPosition(position)) {
        return mFooterViews.keyAt(position - getHeadersCount() - getRealItemCount());
    }
    //如果是item,返回真正的adapter的getItemViewType方法
   return mRealAdapter.getItemViewType(position - getHeadersCount());
}

再再然後,onCreateViewHolder方法:

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//        如果是頭部
    if (isHeaderType(viewType)) {
        int headerPosition = mHeaderViews.indexOfKey(viewType);
        View headerView = mHeaderViews.valueAt(headerPosition);
        return createHeaderAndFooterViewHolder(headerView);
    }
//        如果是尾部
    if (isFooterType(viewType)) {
        int footerPosition = mFooterViews.indexOfKey(viewType);
        View footerView = mFooterViews.valueAt(footerPosition);
        return createHeaderAndFooterViewHolder(footerView);
    }
    return mRealAdapter.onCreateViewHolder(parent, viewType);
}

再再再然後,onBindViewHolder方法:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (isHeaderPosition(position) || isFooterPosition(position)) {

    } else {
        mRealAdapter.onBindViewHolder(holder, realPosition);
    }
}

再再再再然後,添加addHeaderView和addFooterView方法:

public void addHeaderView(View view) {
    mHeaderViews.put(BASE_ITEM_TYPE_HEADER++, view);
    notifyDataSetChanged();
}

public void addFooterView(View view) {
    mFooterViews.put(BASE_ITEM_TYPE_FOOTER ++,view);
    notifyDataSetChanged();
}

注意:這裡如果你使用了網上的代碼,比如鴻洋大神的:

private static final int BASE_ITEM_TYPE_HEADER = 100000;

public void addHeaderView(View view)
{
    mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER, view);
}

如果你是這樣寫的,那就會造成前言部分說了View2沒有添加成功的問題了。

那麼是為什麼呢?我們來推演一番:

添加View0,type是100000; 添加View1,type是100001; 刪除View1; 添加View2,type是100001;

啊哦,getItemViewType拿到的type還是原來view1的type,那自然View2會變成View1了。那怎麼辦呢?我們用自增不就好了嘛。

恩,接著上面,繼續加上刪除頭尾部的方法:

public void removeHeaderView(View view) {
    int index = mHeaderViews.indexOfValue(view);
    if (index < 0) return;
    mHeaderViews.removeAt(index);
    notifyDataSetChanged();
}

public void removeFooterView(View view) {
    int index = mFooterViews.indexOfValue(view);
    if (index < 0) return;
    mFooterViews.removeAt(index);
    notifyDataSetChanged();
}

恩,這下應該沒問題了。

等等!LinearLayoutManager是沒問題了,GridLayoutManager和StaggeredGridLayoutManager呢?其實想想就知道,肯定會有問題的嘛~~問題就是頭部和尾部的寬度會和item一樣長,而不是我們希望的填滿recyclerview的寬度,每個頭尾部單獨占一行。

當然解決辦法鴻洋大大也說過了,網上也有一大堆:

/**解決GridLayoutManager問題*/
    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        mRealAdapter.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
            gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    if (isHeaderPosition(position) || isFooterPosition(position))
                        return gridLayoutManager.getSpanCount();
                    return 1;
                }
            });
        }

    }

    /**解決瀑布流布局問題*/
    @Override
    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
        mRealAdapter.onViewAttachedToWindow(holder);
        int position = holder.getLayoutPosition();
        if (isHeaderPosition(position) || isFooterPosition(position)) {
            ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
            if (lp != null && lp instanceof StaggeredGridLayoutManager.LayoutParams) {
                StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) lp;
                layoutParams.setFullSpan(true);
            }
        }
    }

搞定~

好了,再看下效果圖吧:

頭尾部

Grid頭尾部

Staggred頭尾部

當然,我們要做的是封裝一個可以添加刪除頭尾部的RecyclerView,這算哪門子的封裝啊?我們當然要把這些個方法封裝好一點,使用起來也爽嘛。

一提起封裝,我腦子裡就冒出個東西:泛型

泛型真是個好東西啊,那個靈活,那個爽,啧啧啧,嘿嘿……好了,關於泛型這裡就不詳細介紹了,我們還是來說下封裝吧。(一本正經臉)<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxibG9ja3F1b3RlPg0KCTxwPtTav6rKvLfi17DWrsewo6zPyM7KzsrX1Ly6o7ombGRxdW87yOe5+8rHxOPKudPDo6zE48/r1PXDtNPDo78mcmRxdW87PC9wPg0KCTxwPiZsZHF1bzvEx7u508PLtaO/tbHIu8rHu7m6zdStwLTSu9H5c2V0QWRhcHRlcqOsz/FsaXN0dmlld9K70fnWsb3T08NhZGRIZWFkZXJWaWV3sKGjoSZyZHF1bzs8L3A+DQoJPHA+JmxkcXVvO7b3o6zDu87KzOKjrL+qyrywySZyZHF1bzs8L3A+DQo8L2Jsb2NrcXVvdGU+DQq8zLPQUmVjeWNsZXJWaWV3o6zW2NC0c2V0QWRhcHRlcre9t6gszO2802FkZEhlYWRlclZpZXe1yLe9t6ijuyDTw7unyejWw7XEYWRhcHRlcqOs1/fOqtXm1f21xGFkYXB0ZXIobVJlYWxBZGFwdGVyKbSryOvNt86ysr9BZGFwdGVyo7sNCjxwPs/Iv7S/tLXa0ruyvTo8L3A+DQo8cHJlIGNsYXNzPQ=="brush:java;"> protected HeaderAndFooterAdapter mAdapter; protected Adapter mRealAdapter; @Override public void setAdapter(Adapter adapter) { mRealAdapter = adapter; if (adapter instanceof HeaderAndFooterAdapter) { mAdapter = (HeaderAndFooterAdapter) adapter; } else { mAdapter = new HeaderAndFooterAdapter(getContext(),adapter); } super.setAdapter(mAdapter); } public void addHeaderView(View view) { if (null == view) { throw new IllegalArgumentException("the view to add must not be null !"); } else if (mAdapter == null) { throw new IllegalStateException("u must set a adapter first !"); } else { mAdapter.addHeaderView(view); } } public void addFooterView(View view) { if (null == view) { throw new IllegalArgumentException("the view to add must not be null !"); } else if (mAdapter == null) { throw new IllegalStateException("u must set a adapter first !"); } else { mAdapter.addFooterView(view); } } public void removeHeaderView(View view) { if (null == view) { throw new IllegalArgumentException("the view to remove must not be null !"); } else if (mAdapter == null) { throw new IllegalStateException("u must set a adapter first !"); } else { mAdapter.removeHeaderView(view); } } public void removeFooterView(View view) { if (null == view) { throw new IllegalArgumentException("the view to remove must not be null !"); } else if (mAdapter == null) { throw new IllegalStateException("u must set a adapter first !"); } else { mAdapter.removeFooterView(view); } }

完美,再看看第二步,使用泛型就好啦~

public class HeaderAndFooterAdapter extends RecyclerView.Adapter {
    protected T mRealAdapter;
    protected Context mContext;

    public HeaderAndFooterAdapter(Context mContext, T mRealAdapter) {
        super();
        this.mContext = mContext;
        this.mRealAdapter = mRealAdapter;
    }

    public T getRealAdapter() {
        return mRealAdapter;
    }

    //其他方法上面已介紹,這裡就省略了。

}

好了,這樣就可以了。使用的時候,你還是按照你原來的方式setAdapter,不用管其他,想addHeaderView就直接調HeaderAndFooterRecyclerView的addHeaderView方法就好啦,和ListView是一樣樣的。

添加點擊事件

本來添加點擊事件我不想寫了,因為網上太多太多例子了,但想到都說了這麼多了,再說一下也沒什麼啦。

其實添加點擊事件就是給每個item添加一個點擊監聽嘛,然後傳入position就夠了,注意:因為我們添加了頭部和尾部,position需要處理一下,不然到時候對用戶來說,你的position就是不准的。

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
   if (isHeaderPosition(position) || isFooterPosition(position)) {

    } else {
        final int realPosition = position - getHeadersCount();
        mRealAdapter.onBindViewHolder(holder, realPosition);
        if (mOnItemClickListener != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mOnItemClickListener.OnItemClick(realPosition);
                }
            });
        }
        if (mOnItemLongClickListener != null) {
            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    return mOnItemLongClickListener.onItemLongClick(realPosition);
                }
            });
        }
    }
}

這樣就可以了,點擊和長按就加上去了,接下來跟addHeaderView一樣,加幾個方法用來調用就可以了。

在adapter中加入:

protected OnItemClickListener mOnItemClickListener;
protected OnItemLongClickListener mOnItemLongClickListener;

public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
    this.mOnItemClickListener = onItemClickListener;
}

public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) {
    this.mOnItemLongClickListener = onItemLongClickListener;
}

在HeaderAndFooterRecyclerView中加入:

 public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
    if (null == mAdapter) {
        throw new IllegalStateException("u must set a adapter first !");
    } else {
        mAdapter.setOnItemClickListener(onItemClickListener);
    }
}

public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) {
    if (null == mAdapter) {
        throw new IllegalStateException("u must set a adapter first !");
    } else {
        mAdapter.setOnItemLongClickListener(onItemLongClickListener);
    }
}

搞定,至此,WZMRecyclerView的頭尾部相關就介紹完了,其實WZMRecyclerView的HeaderAndFooterRecyclerView中還封裝了EmptyView的實現,這裡就不介紹了,有興趣的同學可以去看看源碼。

源碼地址:https://github.com/whichname/WZMRecyclerView

有意見或建議或疑問等等,歡迎提出~~

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved