Simplify empty view handling on recycler views

This commit is contained in:
ByteHamster 2019-05-03 11:06:04 +02:00
parent 7a921e0024
commit 06c15fd9e6
9 changed files with 131 additions and 59 deletions

View File

@ -51,6 +51,7 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.view.EmptyViewHandler;
import de.greenrobot.event.EventBus; import de.greenrobot.event.EventBus;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
@ -76,7 +77,7 @@ public class AllEpisodesFragment extends Fragment {
RecyclerView recyclerView; RecyclerView recyclerView;
AllEpisodesRecycleAdapter listAdapter; AllEpisodesRecycleAdapter listAdapter;
private ProgressBar progLoading; private ProgressBar progLoading;
private View emptyView; EmptyViewHandler emptyView;
List<FeedItem> episodes; List<FeedItem> episodes;
private List<Downloader> downloaderList; private List<Downloader> downloaderList;
@ -334,10 +335,10 @@ public class AllEpisodesFragment extends Fragment {
onFragmentLoaded(); onFragmentLoaded();
} }
emptyView = root.findViewById(R.id.emptyView); emptyView = new EmptyViewHandler(getContext());
emptyView.setVisibility(View.GONE); emptyView.attachToRecyclerView(recyclerView);
((TextView)emptyView.findViewById(R.id.emptyViewTitle)).setText(R.string.no_all_episodes_head_label); emptyView.setTitle(R.string.no_all_episodes_head_label);
((TextView)emptyView.findViewById(R.id.emptyViewMessage)).setText(R.string.no_all_episodes_label); emptyView.setMessage(R.string.no_all_episodes_label);
return root; return root;
} }
@ -350,14 +351,14 @@ public class AllEpisodesFragment extends Fragment {
new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes()); new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes());
listAdapter.setHasStableIds(true); listAdapter.setHasStableIds(true);
recyclerView.setAdapter(listAdapter); recyclerView.setAdapter(listAdapter);
emptyView.updateAdapter(listAdapter);
} }
emptyView.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.VISIBLE);
listAdapter.notifyDataSetChanged(); listAdapter.notifyDataSetChanged();
} else { } else {
listAdapter = null; listAdapter = null;
recyclerView.setVisibility(View.GONE); recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.VISIBLE); emptyView.updateAdapter(listAdapter);
} }
restoreScrollPosition(); restoreScrollPosition();
@ -497,7 +498,7 @@ public class AllEpisodesFragment extends Fragment {
} }
if (viewsCreated && !itemsLoaded) { if (viewsCreated && !itemsLoaded) {
recyclerView.setVisibility(View.GONE); recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.GONE); emptyView.hide();
progLoading.setVisibility(View.VISIBLE); progLoading.setVisibility(View.VISIBLE);
} }
disposable = Observable.fromCallable(this::loadData) disposable = Observable.fromCallable(this::loadData)

View File

@ -51,8 +51,8 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = super.onCreateViewHelper(inflater, container, savedInstanceState, View root = super.onCreateViewHelper(inflater, container, savedInstanceState,
R.layout.all_episodes_fragment); R.layout.all_episodes_fragment);
((TextView)root.findViewById(R.id.emptyViewTitle)).setText(R.string.no_fav_episodes_head_label); emptyView.setTitle(R.string.no_fav_episodes_head_label);
((TextView)root.findViewById(R.id.emptyViewMessage)).setText(R.string.no_fav_episodes_label); emptyView.setMessage(R.string.no_fav_episodes_label);
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override @Override

View File

@ -46,8 +46,8 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = super.onCreateViewHelper(inflater, container, savedInstanceState, View root = super.onCreateViewHelper(inflater, container, savedInstanceState,
R.layout.all_episodes_fragment); R.layout.all_episodes_fragment);
((TextView)root.findViewById(R.id.emptyViewTitle)).setText(R.string.no_new_episodes_head_label); emptyView.setTitle(R.string.no_new_episodes_head_label);
((TextView)root.findViewById(R.id.emptyViewMessage)).setText(R.string.no_new_episodes_label); emptyView.setMessage(R.string.no_new_episodes_label);
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override @Override

View File

@ -52,6 +52,7 @@ import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.QueueSorter; import de.danoeh.antennapod.core.util.QueueSorter;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.view.EmptyViewHandler;
import de.greenrobot.event.EventBus; import de.greenrobot.event.EventBus;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
@ -72,7 +73,7 @@ public class QueueFragment extends Fragment {
private TextView infoBar; private TextView infoBar;
private RecyclerView recyclerView; private RecyclerView recyclerView;
private QueueRecyclerAdapter recyclerAdapter; private QueueRecyclerAdapter recyclerAdapter;
private View emptyView; private EmptyViewHandler emptyView;
private ProgressBar progLoading; private ProgressBar progLoading;
private List<FeedItem> queue; private List<FeedItem> queue;
@ -502,11 +503,11 @@ public class QueueFragment extends Fragment {
} }
); );
itemTouchHelper.attachToRecyclerView(recyclerView); itemTouchHelper.attachToRecyclerView(recyclerView);
//empty view
emptyView = root.findViewById(R.id.emptyView); emptyView = new EmptyViewHandler(getContext());
emptyView.setVisibility(View.GONE); emptyView.attachToRecyclerView(recyclerView);
((TextView)emptyView.findViewById(R.id.emptyViewTitle)).setText(R.string.no_items_header_label); emptyView.setTitle(R.string.no_items_header_label);
((TextView)emptyView.findViewById(R.id.emptyViewMessage)).setText(R.string.no_items_label); emptyView.setMessage(R.string.no_items_label);
progLoading = root.findViewById(R.id.progLoading); progLoading = root.findViewById(R.id.progLoading);
progLoading.setVisibility(View.VISIBLE); progLoading.setVisibility(View.VISIBLE);
@ -522,13 +523,13 @@ public class QueueFragment extends Fragment {
new DefaultActionButtonCallback(activity), itemTouchHelper); new DefaultActionButtonCallback(activity), itemTouchHelper);
recyclerAdapter.setHasStableIds(true); recyclerAdapter.setHasStableIds(true);
recyclerView.setAdapter(recyclerAdapter); recyclerView.setAdapter(recyclerAdapter);
emptyView.updateAdapter(recyclerAdapter);
} }
emptyView.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.VISIBLE);
} else { } else {
recyclerAdapter = null; recyclerAdapter = null;
recyclerView.setVisibility(View.GONE); recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.VISIBLE); emptyView.updateAdapter(recyclerAdapter);
} }
if (restoreScrollPosition) { if (restoreScrollPosition) {
@ -641,7 +642,7 @@ public class QueueFragment extends Fragment {
} }
if (queue == null) { if (queue == null) {
recyclerView.setVisibility(View.GONE); recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.GONE); emptyView.hide();
progLoading.setVisibility(View.VISIBLE); progLoading.setVisibility(View.VISIBLE);
} }
disposable = Observable.fromCallable(DBReader::getQueue) disposable = Observable.fromCallable(DBReader::getQueue)

View File

@ -1,59 +1,95 @@
package de.danoeh.antennapod.view; package de.danoeh.antennapod.view;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ListView; import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import de.danoeh.antennapod.R; import de.danoeh.antennapod.R;
public class EmptyViewHandler extends View { public class EmptyViewHandler {
private Activity activity; private boolean layoutAdded = false;
private int title; private RecyclerView recyclerView;
private int message; private RecyclerView.Adapter adapter;
private final View emptyView;
private final TextView tvTitle;
private final TextView tvMessage;
public EmptyViewHandler(Context context) { public EmptyViewHandler(Context context) {
super(context); emptyView = View.inflate(context, R.layout.empty_view_layout, null);
this.setActivity((Activity) context); tvTitle = emptyView.findViewById(R.id.emptyViewTitle);
} tvMessage = emptyView.findViewById(R.id.emptyViewMessage);
public int getTitle() {
return title;
} }
public void setTitle(int title) { public void setTitle(int title) {
this.title = title; tvTitle.setText(title);
}
public int getMessage() {
return message;
} }
public void setMessage(int message) { public void setMessage(int message) {
this.message = message; tvMessage.setText(message);
}
public void hide() {
emptyView.setVisibility(View.GONE);
} }
public void attachToListView(ListView listView) { public void attachToListView(ListView listView) {
if (layoutAdded) {
View emptyView = getActivity().getLayoutInflater().inflate(R.layout.empty_view_layout, null); throw new IllegalStateException("Can not attach to ListView multiple times");
}
layoutAdded = true;
((ViewGroup) listView.getParent()).addView(emptyView); ((ViewGroup) listView.getParent()).addView(emptyView);
listView.setEmptyView(emptyView); listView.setEmptyView(emptyView);
TextView tvTitle = (TextView) emptyView.findViewById(R.id.emptyViewTitle);
tvTitle.setText(title);
TextView tvMessage = (TextView) emptyView.findViewById(R.id.emptyViewMessage);
tvMessage.setText(message);
} }
public Activity getActivity() { public void attachToRecyclerView(RecyclerView recyclerView) {
return activity; if (layoutAdded) {
throw new IllegalStateException("Can not attach to ListView multiple times");
}
layoutAdded = true;
this.recyclerView = recyclerView;
ViewGroup parent = ((ViewGroup) recyclerView.getParent());
parent.addView(emptyView);
updateAdapter(recyclerView.getAdapter());
if (parent instanceof RelativeLayout) {
RelativeLayout.LayoutParams layoutParams =
(RelativeLayout.LayoutParams)emptyView.getLayoutParams();
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
emptyView.setLayoutParams(layoutParams);
}
} }
public void setActivity(Activity activity) { public void updateAdapter(RecyclerView.Adapter adapter) {
this.activity = activity; if (this.adapter != null) {
this.adapter.unregisterAdapterDataObserver(adapterObserver);
}
this.adapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(adapterObserver);
}
updateVisibility();
}
private final SimpleAdapterDataObserver adapterObserver = new SimpleAdapterDataObserver() {
@Override
public void anythingChanged() {
updateVisibility();
}
};
private void updateVisibility() {
boolean empty;
if (adapter == null) {
empty = true;
} else {
empty = adapter.getItemCount() == 0;
}
recyclerView.setVisibility(empty ? View.GONE : View.VISIBLE);
emptyView.setVisibility(empty ? View.VISIBLE : View.GONE);
} }
} }

View File

@ -0,0 +1,41 @@
package de.danoeh.antennapod.view;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
/**
* AdapterDataObserver that relays all events to the method anythingChanged().
*/
public abstract class SimpleAdapterDataObserver extends RecyclerView.AdapterDataObserver {
public abstract void anythingChanged();
@Override
public void onChanged() {
anythingChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
anythingChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
anythingChanged();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
anythingChanged();
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
anythingChanged();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
anythingChanged();
}
}

View File

@ -17,10 +17,6 @@
tools:itemCount="13" tools:itemCount="13"
tools:listitem="@layout/new_episodes_listitem" /> tools:listitem="@layout/new_episodes_listitem" />
<include
android:id="@+id/emptyView"
layout="@layout/empty_view_layout"/>
<ProgressBar <ProgressBar
android:id="@+id/progLoading" android:id="@+id/progLoading"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -4,6 +4,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:gravity="center" android:gravity="center"
android:layout_centerInParent="true"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<TextView <TextView

View File

@ -27,10 +27,6 @@
android:layout_below="@id/divider" android:layout_below="@id/divider"
android:scrollbars="vertical"/> android:scrollbars="vertical"/>
<include
android:id="@+id/emptyView"
layout="@layout/empty_view_layout"/>
<ProgressBar <ProgressBar
android:id="@+id/progLoading" android:id="@+id/progLoading"
android:layout_width="wrap_content" android:layout_width="wrap_content"