這一年來公司作的項目主要是電商,市場用到了列表的顯示,也遇到了一些坑,今天我們來聊一下,如何用RecyclerView進行設置空列表界面的提示.android
RecyclerView是listView的升級版,其實在平常的開發中,有不少地方咱們都在使用RecyclerView,爲何這樣說,RecyclerView除了列表以外,還能替代ScrollView.RecyclerView實現了NestedScrollingChild,能夠配合官方提供的support-design包實現不少很炫的效果。git
這幾年google一直推崇其設計理念MaterialDesign,同時也出了許多方便的包,如RecyclerView、CardView、SupportDesign包裏的控件,能夠說,這兩年google給研發人員帶來了春天,不少很炫的效果,直接拿來就用,這樣說下去就扯遠了,我們先看看什麼是RecyclerView。RecyclerView在android.support.v7.widget包中。github
compile 'com.android.support:appcompat-v7:24.2.1'
複製代碼
RecyclerView其實就是listview的升級版,具備更高靈活、擴展,但也每每所以失去了listview不少封裝好的一些接口,如setHeader、setFooter、setEmptyView,所以你會發現RecyclerView不支持setEmptyView,我們先來看一下listView的setEmptyView是怎麼作的,我們也能夠借鑑一下listView的作法作一個。設計模式
從上圖所得,ListView 繼承於AbsListView,而AbsListView繼承與AdapterView。 咱們能夠直接去AdapterView 看其源碼是怎樣實現setEmptyView的。bash
/**
* Sets the view to show if the adapter is empty
*/
@android.view.RemotableViewMethod
public void setEmptyView(View emptyView) {
//這裏保存一個emptyView
mEmptyView = emptyView;
// If not explicitly specified this view is important for accessibility.
if (emptyView != null
&& emptyView.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
emptyView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
final T adapter = getAdapter();
//判斷是否adapter中的集合爲空
final boolean empty = ((adapter == null) || adapter.isEmpty());
updateEmptyStatus(empty);
}
/**
* Update the status of the list based on the empty parameter. If empty is true and
* we have an empty view, display it. In all the other cases, make sure that the listview
* is VISIBLE and that the empty view is GONE (if it's not null). */ private void updateEmptyStatus(boolean empty) { if (isInFilterMode()) { empty = false; } //這裏纔是真正判斷進行顯示與否 if (empty) { if (mEmptyView != null) { //1.若是ListView中有調用setEmptyView,同時adapter中的集合爲0的話,顯示emptyView,隱藏listView mEmptyView.setVisibility(View.VISIBLE); setVisibility(View.GONE); } else { //若是listView中沒有設置emptyView,讓listView顯示 // If the caller just removed our empty view, make sure the list view is visible setVisibility(View.VISIBLE); } // We are now GONE, so pending layouts will not be dispatched. // Force one here to make sure that the state of the list matches // the state of the adapter. if (mDataChanged) { this.onLayout(false, mLeft, mTop, mRight, mBottom); } } else { if (mEmptyView != null) mEmptyView.setVisibility(View.GONE); setVisibility(View.VISIBLE); } } 複製代碼
從源碼上可得,ListView中的setEmptyView(View),其實只是在內部進行一個判斷,若是Adapter裏面的isEmpty()爲true 而且listView裏面的mEmptyView不爲空,則顯示mEmptyView,同時隱藏自身的ListView.app
從源碼上看,不就是隱藏顯示嘛,沒錯,但要作到隱藏顯示也不容易,所以,咱們對咱們的RecyclerView進行一番改造,看一下,是否能達到咱們想要的效果。ide
<ffzxcom.mytest.recyclerviewemptyviewdemo.way1.RecyclerViewEmptySupport
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ffzxcom.mytest.recyclerviewemptyviewdemo.way1.RecyclerViewEmptySupport>
<TextView
android:id="@+id/empty_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:drawablePadding="10dp"
android:drawableTop="@mipmap/empty_view"
android:text="@string/no_data_tips"
android:gravity="center"
android:visibility="gone" />
複製代碼
根據ListView的作法,咱們能夠對RecyclerView進行重寫:函數
/**
* Created by shenminjie on 2016/10/19.
* 用來演示如何在RecyclerView裏面添加setEmptyView
*/
public class RecyclerViewEmptySupport extends RecyclerView {
/**
* 當數據爲空時展現的View
*/
private View mEmptyView;
/**
* 建立一個觀察者
* 爲何要在onChanged裏面寫?
* 由於每次notifyDataChanged的時候,系統都會調用這個觀察者的onChange函數
* 咱們大能夠在這個觀察者這裏判斷咱們的邏輯,就是顯示隱藏
*/
private AdapterDataObserver emptyObserver = new AdapterDataObserver() {
@Override
public void onChanged() {
Adapter<?> adapter = getAdapter();
//這種寫發跟以前咱們以前看到的ListView的是同樣的,判斷數據爲空否,在進行顯示或者隱藏
if (adapter != null && mEmptyView != null) {
if (adapter.getItemCount() == 0) {
mEmptyView.setVisibility(View.VISIBLE);
RecyclerViewEmptySupport.this.setVisibility(View.GONE);
} else {
mEmptyView.setVisibility(View.GONE);
RecyclerViewEmptySupport.this.setVisibility(View.VISIBLE);
}
}
}
};
public RecyclerViewEmptySupport(Context context) {
super(context);
}
public RecyclerViewEmptySupport(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public RecyclerViewEmptySupport(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 依賴注入
*
* @param emptyView 展現的空view
*/
public void setEmptyView(View emptyView) {
mEmptyView = emptyView;
}
@Override
public void setAdapter(Adapter adapter) {
super.setAdapter(adapter);
if (adapter != null) {
//這裏用了觀察者模式,同時把這個觀察者添加進去,
// 至於這個模式怎麼用,谷歌一下,很少講了,由於這個涉及到了Adapter的一些原理,感興趣能夠點進去看看源碼,仍是受益不淺的
adapter.registerAdapterDataObserver(emptyObserver);
}
//當setAdapter的時候也調一次
emptyObserver.onChanged();
}
}
複製代碼
咱們在RecyclerView裏面添加了一個成員變量emptyObserver,這個做用就是用於觀察每次Adapter進行數據刷新的時候都調用一次觀察者的onChange(),至於爲何會調,這個就說得有點遠了,建議你們若是想看源碼,能夠參考一下Android源碼設計模式(何紅輝,關愛民),裏面有很詳細的介紹android的一些設計原理。佈局
回到上面來,咱們看到咱們的onChange裏面的代碼,不就是跟ListView裏面的同樣嗎,沒錯,我想說的第一種方式就是借鑑了ListView的作法,固然,仍是其餘的作法,只是這個思路會有些不同,第一種的方法跟listview同樣,在xml裏面寫上emptyview,即使不用在RecyclerView裏寫,相信你們也知道能夠在activity裏進行判斷,不就是隱藏顯示嘛,固然,還有第二種作法。ui
咱們知道,RecyclerView的出現,更大程度給了開發者去自定義本身但願的佈局,RecyclerView能夠經過引入不一樣的ViewType進行不一樣的列表顯示,舉個列子:及時通信的聊天記錄,通常都會有左邊的佈局跟右邊的佈局,那麼,就有兩個不一樣的ViewType了,根據不一樣狀況進行引入不一樣的ViewType。好,那麼,咱們也能夠根據咱們的狀況進行引入佈局啊,例如,若是數據爲空的時候,那我能不能在咱們的Adapter裏面引入一個emptyView這樣的佈局。
/**
* Created by shenminjie on 2016/10/20.
* 適配器,模擬列表
*/
public class EmptyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
/**
* 數據
*/
private List<String> mDatas;
/**
* 點擊事件回調
*/
private OnItemSelectListener mListener;
/**
* viewType--分別爲item以及空view
*/
public static final int VIEW_TYPE_ITEM = 1;
public static final int VIEW_TYPE_EMPTY = 0;
public EmptyAdapter(List<String> datas) {
mDatas = datas;
}
/**
* 設置回調
*
* @param listener
*/
public void setOnItemSelectListener(OnItemSelectListener listener) {
mListener = listener;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//在這裏根據不一樣的viewType進行引入不一樣的佈局
if (viewType == VIEW_TYPE_EMPTY) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_empty_view_layout, parent, false);
return new RecyclerView.ViewHolder(view) {
};
}
//其餘的引入正常的
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_my_list_layout, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof MyViewHolder) {
MyViewHolder viewHolder = (MyViewHolder) holder;
viewHolder.setData(mDatas.get(position));
viewHolder.getItemView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.onItemSelectListener(v, position);
}
}
});
}
}
@Override
public int getItemCount() {
//同時這裏也須要添加判斷,若是mData.size()爲0的話,只引入一個佈局,就是emptyView
//那麼,這個recyclerView的itemCount爲1
if (mDatas.size() == 0) {
return 1;
}
//若是不爲0,按正常的流程跑
return mDatas.size();
}
@Override
public int getItemViewType(int position) {
//在這裏進行判斷,若是咱們的集合的長度爲0時,咱們就使用emptyView的佈局
if (mDatas.size() == 0) {
return VIEW_TYPE_EMPTY;
}
//若是有數據,則使用ITEM的佈局
return VIEW_TYPE_ITEM;
}
}
複製代碼
這種作法,就是把咱們的emptyView設置放進去Adapter,根據不一樣的狀況引入不一樣的佈局,跟第一的區別就是顯示與否都交給系統去處理,經過引入不一樣佈局的作法達到了顯示emptyView的效果。
相信你們都會在想,哪種方式更好用,這得看我的的需求,但我更傾向於用第二種,由於google這兩年提供了許多很好的資源給咱們開發者使用,最熱的莫過因而support-design包裏的一些新控件,tabLayout、toolbar、CoordinatorLayout等等,但若是想要更好的使用它們很炫的一些效果,得好好了解一下NestedScrollingParent這個接口,google提供了這些接口很好的處理了事件分發的處理,而RecyclerView均實現了這些接口,能很好的配合support-design使用其特效。感興趣的朋友能夠上去github下載demo,感覺一下兩種方式的不一樣。
https://github.com/shenminjie/blog_demo
複製代碼