這一年來公司做的項目主要是電商,市場用到了列表的顯示,也遇到了一些坑,今天咱們來聊一下,如何用RecyclerView進行設置空列表界面的提示.
RecyclerView是listView的升級版,其實在日常的開發中,有很多地方我們都在使用RecyclerView,為什么這樣說,RecyclerView除了列表之外,還能替代ScrollView.RecyclerView實現了NestedScrollingChild,可以配合官方提供的support-design包實現很多很炫的效果。
這幾年google一直推崇其設計理念MaterialDesign,同時也出了許多方便的包,如RecyclerView、CardView、SupportDesign包里的控件,可以說,這兩年google給研發人員帶來了春天,很多很炫的效果,直接拿來就用,這樣說下去就扯遠了,咱們先看看什么是RecyclerView。RecyclerView在android.support.v7.widget包中。
compile 'com.android.support:appcompat-v7:24.2.1'
RecyclerView其實就是listview的升級版,具有更高靈活、擴展,但也往往因此失去了listview很多封裝好的一些接口,如setHeader、setFooter、setEmptyView,因此你會發現RecyclerView不支持setEmptyView,咱們先來看一下listView的setEmptyView是怎么做的,咱們也可以借鑒一下listView的做法做一個。
Paste_Image.png
從上圖所得,ListView 繼承于AbsListView,而AbsListView繼承與AdapterView。
我們可以直接去AdapterView 看其源碼是怎樣實現setEmptyView的。
/** * 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 amp;amp; 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.
從源碼上看,不就是隱藏顯示嘛,沒錯,但要做到隱藏顯示也不容易,因此,我們對我們的RecyclerView進行一番改造,看一下,是否能達到我們想要的效果。
lt;ffzxcom.mytest.recyclerviewemptyviewdemo.way1.RecyclerViewEmptySupport android:id=quot;@ id/recycler_viewquot; android:layout_width=quot;match_parentquot; android:layout_height=quot;match_parentquot;gt; lt;/ffzxcom.mytest.recyclerviewemptyviewdemo.way1.RecyclerViewEmptySupportgt; lt;TextView android:id=quot;@ id/empty_viewquot; android:layout_width=quot;wrap_contentquot; android:layout_height=quot;wrap_contentquot; android:layout_centerInParent=quot;truequot; android:drawablePadding=quot;10dpquot; android:drawableTop=quot;@mipmap/empty_viewquot; android:text=quot;@string/no_data_tipsquot; android:gravity=quot;centerquot; android:visibility=quot;gonequot; /gt;
根據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() { Adapterlt;?gt; adapter = getAdapter(); //這種寫發跟之前我們之前看到的ListView的是一樣的,判斷數據為空否,在進行顯示或者隱藏 if (adapter != null amp;amp; 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里進行判斷,不就是隱藏顯示嘛,當然,還有第二種做法。
我們知道,RecyclerView的出現,更大程度給了開發者去自定義自己希望的布局,RecyclerView可以通過引入不同的ViewType進行不同的列表顯示,舉個列子:及時通訊的聊天記錄,一般都會有左邊的布局跟右邊的布局,那么,就有兩個不同的ViewType了,根據不同情況進行引入不同的ViewType。好,那么,我們也可以根據我們的情況進行引入布局啊,例如,如果數據為空的時候,那我能不能在我們的Adapter里面引入一個emptyView這樣的布局。
/** * Created by shenminjie on 2016/10/20. * 適配器,模擬列表 */ public class EmptyAdapter extends RecyclerView.Adapterlt;RecyclerView.ViewHoldergt; { /** * 數據 */ private Listlt;Stringgt; 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(Listlt;Stringgt; 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,感受一下兩種方式的不同。
Paste_Image.png
Paste_Image.png
https://github.com/shenminjie/blog_demo
Tags: ListView RecyclerView
文章來源:http://www.jianshu.com/p/d6fa7bbe80af