Android应用开发中,经常需要使用到界面的下拉刷新和上拉加载,在support v7之前,一般都是用ListView去做列表视图,而ListView对EmptyView是提供支持的,并且有许多开源库拓展了ListView使其支持了下拉刷新和上拉加载,但是许多这种拓展都会在ListView现实EmptyView时失效,原因就是ListView实际上已经被Gone了。
在support v7之后,google提供了一个神器,RecyclerView,相信大部分人都已经不陌生了,RecyclerView提供了更强大的Recycler机制,能够完美实现ListView的基础上,使开发者的代码更简洁,效率更高,并且能实现各种不同的布局。但是凡事都有个但是,RecyclerView目前为止并没有提供Header,Footer,EmptyView的内部支持,所以大部分的开发者的方案是使用其他拓展RecyclerView或者Layout来实现,google提供的SwipeLayout可以实现下拉刷新的功能,但是如果要实现上拉加载或者更改header都是比较麻烦的。
但是现在有许多的开源方案可以实现,比如SwipeToLoadLayout,虽然这个开源项目的star并不多,但是它是我发现的最方便实现下拉刷新和上拉加载的开源库了。建议大家也可以使用试试。
美中不足的是,SwipeToLoadLayout并没有提供EmptyView的内部支持,相信大部分的这类开源库都没有支持吧。但是没关系,我们可以自己去实现它,并且附带实现自动开启关闭LoadMore的功能(在Empty时就应该自动关闭Loadmore)注:希望你在阅读下面的代码之前先已经研究并使用过SwipeToLoadLayout了
1、首先我们需要添加一个EmptyView在swipe_target里面
1 <?xml version="1.0" encoding="utf-8"?> 2 <com.duoniu.benfu.widget.layout.SwipeToLoadLayoutEmptySupport 3 android:id="@+id/swipeToLoadLayout" 4 xmlns:android="http://schemas.android.com/apk/res/android" 5 xmlns:app="http://schemas.android.com/apk/res-auto" 6 android:orientation="vertical" android:layout_width="match_parent" 7 android:layout_height="wrap_content" 8 app:swipe_style="classic"> 9 10 <include 11 android:id="@+id/swipe_refresh_header" 12 layout="@layout/layout_logo_header"/> 13 14 15 <RelativeLayout 16 android:id="@id/swipe_target" 17 android:layout_width="match_parent" 18 android:layout_height="wrap_content"> 19 20 <com.duoniu.benfu.widget.recyclerview.RecyclerViewEmptySupport 21 android:id="@+id/recyclerView" 22 android:layout_width="match_parent" 23 android:layout_height="wrap_content"> 24 25 </com.duoniu.benfu.widget.recyclerview.RecyclerViewEmptySupport> 26 27 <include layout="@layout/empty_or_error" 28 android:id="@+id/emptyView" 29 android:visibility="gone"/> 30 </RelativeLayout> 31 32 33 34 <include 35 android:id="@+id/swipe_load_more_footer" 36 layout="@layout/layout_classic_footer"/> 37 38 </com.duoniu.benfu.widget.layout.SwipeToLoadLayoutEmptySupport>
可以看到,我们的swipe_target不在只是单独的一个RecyclerView了,而且一个RelativeLayout,里面放了一个RecyclerView和一个EmptyView.当然,这个EmptyView你可以任意实现,默认visibility是gone了。
2、拓展RecyclerView
为了使RecycleView能够支持EmptyView,我们需要监听到RecyclerView的item数量变换来控制EmptyView的现实
1 public class RecyclerViewEmptySupport extends RecyclerView { 2 3 private View emptyView; 4 5 private WeakReference<SwipeToLoadLayout> weakSwipeToLoadLayout; 6 7 private AdapterDataObserver emptyObserver = new AdapterDataObserver() { 8 @Override 9 public void onChanged() { 10 Adapter<?> adapter = getAdapter(); 11 if (adapter != null && emptyView != null){ 12 if (adapter.getItemCount() == 0){ 13 emptyView.setVisibility(View.VISIBLE); 14 } 15 else{ 16 emptyView.setVisibility(View.GONE); 17 } 18 if (weakSwipeToLoadLayout != null && weakSwipeToLoadLayout.get() != null){ 19 weakSwipeToLoadLayout.get().setLoadMoreEnabled(adapter.getItemCount() > 0); 20 } 21 } 22 } 23 }; 24 25 public RecyclerViewEmptySupport(Context context) { 26 super(context); 27 } 28 29 public RecyclerViewEmptySupport(Context context, AttributeSet attrs) { 30 super(context, attrs); 31 } 32 33 public RecyclerViewEmptySupport(Context context, AttributeSet attrs, int defStyle) { 34 super(context, attrs, defStyle); 35 } 36 37 38 39 @Override 40 public void setAdapter(Adapter adapter) { 41 super.setAdapter(adapter); 42 if (adapter != null){ 43 adapter.registerAdapterDataObserver(emptyObserver); 44 } 45 emptyObserver.onChanged(); 46 } 47 48 public void setEmptyView(View emptyView) { 49 this.emptyView = emptyView; 50 } 51 52 public void setSwipeToLoadLayout(SwipeToLoadLayout swipeToLoadLayout) { 53 this.weakSwipeToLoadLayout = new WeakReference<>(swipeToLoadLayout); 54 } 55 56 }
我们使用了sdk中的AdapterDataObserver来监听数据变化,在onChange中实现如果item已经为0了就现实emptyView,反之不显示,另外,如果item已经为0了就禁用SwipeToLoadLayout的加载更多功能。
3、拓展SwipeToLoadLayout
细心的朋友会发现RecyclerViewEmptySupport的emptyView字段是从外部set的,很明显,RecyclerView是没法知道它边上有个兄弟View的,所以需要父布局去set
1 public class SwipeToLoadLayoutEmptySupport extends SwipeToLoadLayout { 2 3 4 public SwipeToLoadLayoutEmptySupport(Context context) { 5 super(context); 6 } 7 8 public SwipeToLoadLayoutEmptySupport(Context context, AttributeSet attrs) { 9 super(context, attrs); 10 } 11 12 public SwipeToLoadLayoutEmptySupport(Context context, AttributeSet attrs, int defStyleAttr) { 13 super(context, attrs, defStyleAttr); 14 } 15 16 @Override 17 protected void onFinishInflate() { 18 super.onFinishInflate(); 19 View targetView = this.findViewById(R.id.swipe_target); 20 if (targetView != null) { 21 View emptyView = targetView.findViewById(R.id.emptyView); 22 View recyclerView = targetView.findViewById(R.id.recyclerView); 23 if (recyclerView instanceof RecyclerViewEmptySupport){ 24 RecyclerViewEmptySupport recyclerViewEmptySupport = ((RecyclerViewEmptySupport) recyclerView); 25 recyclerViewEmptySupport.setSwipeToLoadLayout(this); 26 if (emptyView != null && recyclerView != null) { 27 recyclerViewEmptySupport.setEmptyView(emptyView); 28 } 29 } 30 31 } 32 33 } 34 }
4、使用SwipeToLoadLayoutEmptySupport
其实到此为止,我们已经拓展完成了,接下来就是使用了,使用上并没有什么技巧,就只是在所有需要上拉加载,下拉刷新以及空视图的地方include一下我们刚开始写的布局即可(是不是感觉很方便?)
浙公网安备 33010602011771号