From 06c15fd9e6d4335583512377f1b8401b9ea3ccf6 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Fri, 3 May 2019 11:06:04 +0200 Subject: [PATCH] Simplify empty view handling on recycler views --- .../fragment/AllEpisodesFragment.java | 17 ++-- .../fragment/FavoriteEpisodesFragment.java | 4 +- .../fragment/NewEpisodesFragment.java | 4 +- .../antennapod/fragment/QueueFragment.java | 19 ++-- .../antennapod/view/EmptyViewHandler.java | 96 +++++++++++++------ .../view/SimpleAdapterDataObserver.java | 41 ++++++++ .../main/res/layout/all_episodes_fragment.xml | 4 - app/src/main/res/layout/empty_view_layout.xml | 1 + app/src/main/res/layout/queue_fragment.xml | 4 - 9 files changed, 131 insertions(+), 59 deletions(-) create mode 100644 app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index 5dca2aa84..43c9b1c8c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -51,6 +51,7 @@ import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; +import de.danoeh.antennapod.view.EmptyViewHandler; import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -76,7 +77,7 @@ public class AllEpisodesFragment extends Fragment { RecyclerView recyclerView; AllEpisodesRecycleAdapter listAdapter; private ProgressBar progLoading; - private View emptyView; + EmptyViewHandler emptyView; List episodes; private List downloaderList; @@ -334,10 +335,10 @@ public class AllEpisodesFragment extends Fragment { onFragmentLoaded(); } - emptyView = root.findViewById(R.id.emptyView); - emptyView.setVisibility(View.GONE); - ((TextView)emptyView.findViewById(R.id.emptyViewTitle)).setText(R.string.no_all_episodes_head_label); - ((TextView)emptyView.findViewById(R.id.emptyViewMessage)).setText(R.string.no_all_episodes_label); + emptyView = new EmptyViewHandler(getContext()); + emptyView.attachToRecyclerView(recyclerView); + emptyView.setTitle(R.string.no_all_episodes_head_label); + emptyView.setMessage(R.string.no_all_episodes_label); return root; } @@ -350,14 +351,14 @@ public class AllEpisodesFragment extends Fragment { new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes()); listAdapter.setHasStableIds(true); recyclerView.setAdapter(listAdapter); + emptyView.updateAdapter(listAdapter); } - emptyView.setVisibility(View.GONE); recyclerView.setVisibility(View.VISIBLE); listAdapter.notifyDataSetChanged(); } else { listAdapter = null; recyclerView.setVisibility(View.GONE); - emptyView.setVisibility(View.VISIBLE); + emptyView.updateAdapter(listAdapter); } restoreScrollPosition(); @@ -497,7 +498,7 @@ public class AllEpisodesFragment extends Fragment { } if (viewsCreated && !itemsLoaded) { recyclerView.setVisibility(View.GONE); - emptyView.setVisibility(View.GONE); + emptyView.hide(); progLoading.setVisibility(View.VISIBLE); } disposable = Observable.fromCallable(this::loadData) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java index cda89bbd3..d4a662d43 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java @@ -51,8 +51,8 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = super.onCreateViewHelper(inflater, container, savedInstanceState, R.layout.all_episodes_fragment); - ((TextView)root.findViewById(R.id.emptyViewTitle)).setText(R.string.no_fav_episodes_head_label); - ((TextView)root.findViewById(R.id.emptyViewMessage)).setText(R.string.no_fav_episodes_label); + emptyView.setTitle(R.string.no_fav_episodes_head_label); + emptyView.setMessage(R.string.no_fav_episodes_label); ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java index c2b61bf75..5751855c7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -46,8 +46,8 @@ public class NewEpisodesFragment extends AllEpisodesFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = super.onCreateViewHelper(inflater, container, savedInstanceState, R.layout.all_episodes_fragment); - ((TextView)root.findViewById(R.id.emptyViewTitle)).setText(R.string.no_new_episodes_head_label); - ((TextView)root.findViewById(R.id.emptyViewMessage)).setText(R.string.no_new_episodes_label); + emptyView.setTitle(R.string.no_new_episodes_head_label); + emptyView.setMessage(R.string.no_new_episodes_label); ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java index 58bfa3e08..13580be4f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -52,6 +52,7 @@ import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.QueueSorter; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; +import de.danoeh.antennapod.view.EmptyViewHandler; import de.greenrobot.event.EventBus; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -72,7 +73,7 @@ public class QueueFragment extends Fragment { private TextView infoBar; private RecyclerView recyclerView; private QueueRecyclerAdapter recyclerAdapter; - private View emptyView; + private EmptyViewHandler emptyView; private ProgressBar progLoading; private List queue; @@ -502,11 +503,11 @@ public class QueueFragment extends Fragment { } ); itemTouchHelper.attachToRecyclerView(recyclerView); - //empty view - emptyView = root.findViewById(R.id.emptyView); - emptyView.setVisibility(View.GONE); - ((TextView)emptyView.findViewById(R.id.emptyViewTitle)).setText(R.string.no_items_header_label); - ((TextView)emptyView.findViewById(R.id.emptyViewMessage)).setText(R.string.no_items_label); + + emptyView = new EmptyViewHandler(getContext()); + emptyView.attachToRecyclerView(recyclerView); + emptyView.setTitle(R.string.no_items_header_label); + emptyView.setMessage(R.string.no_items_label); progLoading = root.findViewById(R.id.progLoading); progLoading.setVisibility(View.VISIBLE); @@ -522,13 +523,13 @@ public class QueueFragment extends Fragment { new DefaultActionButtonCallback(activity), itemTouchHelper); recyclerAdapter.setHasStableIds(true); recyclerView.setAdapter(recyclerAdapter); + emptyView.updateAdapter(recyclerAdapter); } - emptyView.setVisibility(View.GONE); recyclerView.setVisibility(View.VISIBLE); } else { recyclerAdapter = null; recyclerView.setVisibility(View.GONE); - emptyView.setVisibility(View.VISIBLE); + emptyView.updateAdapter(recyclerAdapter); } if (restoreScrollPosition) { @@ -641,7 +642,7 @@ public class QueueFragment extends Fragment { } if (queue == null) { recyclerView.setVisibility(View.GONE); - emptyView.setVisibility(View.GONE); + emptyView.hide(); progLoading.setVisibility(View.VISIBLE); } disposable = Observable.fromCallable(DBReader::getQueue) diff --git a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java index e3fd63235..42c11bc8e 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java @@ -1,59 +1,95 @@ package de.danoeh.antennapod.view; -import android.app.Activity; import android.content.Context; +import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; import android.widget.ListView; +import android.widget.RelativeLayout; import android.widget.TextView; import de.danoeh.antennapod.R; -public class EmptyViewHandler extends View { - private Activity activity; - private int title; - private int message; +public class EmptyViewHandler { + private boolean layoutAdded = false; + private RecyclerView recyclerView; + private RecyclerView.Adapter adapter; + + private final View emptyView; + private final TextView tvTitle; + private final TextView tvMessage; public EmptyViewHandler(Context context) { - super(context); - this.setActivity((Activity) context); - } - - public int getTitle() { - return title; + emptyView = View.inflate(context, R.layout.empty_view_layout, null); + tvTitle = emptyView.findViewById(R.id.emptyViewTitle); + tvMessage = emptyView.findViewById(R.id.emptyViewMessage); } public void setTitle(int title) { - this.title = title; - } - - public int getMessage() { - return message; + tvTitle.setText(title); } public void setMessage(int message) { - this.message = message; + tvMessage.setText(message); } - public void attachToListView(ListView listView){ + public void hide() { + emptyView.setVisibility(View.GONE); + } - View emptyView = getActivity().getLayoutInflater().inflate(R.layout.empty_view_layout, null); + public void attachToListView(ListView listView) { + if (layoutAdded) { + throw new IllegalStateException("Can not attach to ListView multiple times"); + } + layoutAdded = true; ((ViewGroup) listView.getParent()).addView(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() { - return activity; + public void attachToRecyclerView(RecyclerView recyclerView) { + 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) { - this.activity = activity; + public void updateAdapter(RecyclerView.Adapter adapter) { + 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); } } diff --git a/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java b/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java new file mode 100644 index 000000000..45c3a35bd --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java @@ -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(); + } +} diff --git a/app/src/main/res/layout/all_episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml index 099216007..89f900a1f 100644 --- a/app/src/main/res/layout/all_episodes_fragment.xml +++ b/app/src/main/res/layout/all_episodes_fragment.xml @@ -17,10 +17,6 @@ tools:itemCount="13" tools:listitem="@layout/new_episodes_listitem" /> - - - -