Add fastscroll to lists with RecyclerView

This commit is contained in:
Synced Synapse 2019-01-10 18:40:58 +00:00 committed by Martijn Brekhof
parent 6446ed7ad6
commit f7a63ae9a7
17 changed files with 103 additions and 41 deletions

View File

@ -113,6 +113,7 @@ dependencies {
implementation 'org.jmdns:jmdns:3.5.1' implementation 'org.jmdns:jmdns:3.5.1'
implementation 'at.blogc:expandabletextview:1.0.3' implementation 'at.blogc:expandabletextview:1.0.3'
implementation 'com.sothree.slidinguppanel:library:3.3.1' implementation 'com.sothree.slidinguppanel:library:3.3.1'
implementation 'com.simplecityapps:recyclerview-fastscroll:1.0.20'
androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test:rules:1.0.1' androidTestImplementation 'com.android.support.test:rules:1.0.1'

View File

@ -20,11 +20,15 @@ import android.database.Cursor;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
abstract public class RecyclerViewCursorAdapter extends RecyclerView.Adapter<RecyclerViewCursorAdapter.CursorViewHolder> { import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
private boolean dataValid; abstract public class RecyclerViewCursorAdapter
extends RecyclerView.Adapter<RecyclerViewCursorAdapter.CursorViewHolder>
implements FastScrollRecyclerView.SectionedAdapter {
protected boolean dataValid;
private int rowIDColumn; private int rowIDColumn;
private Cursor cursor; protected Cursor cursor;
@Override @Override
public void onBindViewHolder(CursorViewHolder holder, int position) { public void onBindViewHolder(CursorViewHolder holder, int position) {
@ -59,6 +63,25 @@ abstract public class RecyclerViewCursorAdapter extends RecyclerView.Adapter<Rec
return cursor.getLong(rowIDColumn); return cursor.getLong(rowIDColumn);
} }
public String getSectionName(int position) {
if (!dataValid) {
throw new IllegalStateException("Cursor is in an invalid state.");
}
if (!cursor.moveToPosition(position)) {
throw new IllegalStateException("Could not move cursor to position " + position);
}
return cursor.getString(getSectionColumnIdx()).substring(0, 1).toUpperCase();
}
/**
* Should return the cursor column index that contains the corresponding field to be used
* on the fastscroller. Should be a string!
*
* @return Cursor column index of the field to show in the fastscroller
*/
abstract protected int getSectionColumnIdx();
public void swapCursor(Cursor newCursor) { public void swapCursor(Cursor newCursor) {
if (newCursor == cursor) { if (newCursor == cursor) {
return; return;

View File

@ -87,7 +87,7 @@ public class AlbumListFragment extends AbstractCursorListFragment {
/** /**
* Use this to display all albums for a specific genre * Use this to display all albums for a specific genre
* @param genreId * @param genreId genre id
*/ */
public void setGenre(int genreId) { public void setGenre(int genreId) {
Bundle args = new Bundle(); Bundle args = new Bundle();
@ -216,12 +216,12 @@ public class AlbumListFragment extends AbstractCursorListFragment {
} }
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Context ctx) {
super.onAttach(activity); super.onAttach(ctx);
try { try {
listenerActivity = (OnAlbumSelectedListener) activity; listenerActivity = (OnAlbumSelectedListener) ctx;
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnAlbumSelectedListener"); throw new ClassCastException(ctx.toString() + " must implement OnAlbumSelectedListener");
} }
setSupportsSearch(true); setSupportsSearch(true);
@ -317,6 +317,8 @@ public class AlbumListFragment extends AbstractCursorListFragment {
popupMenu.show(); popupMenu.show();
} }
}; };
protected int getSectionColumnIdx() { return AlbumListQuery.TITLE; }
} }
/** /**

View File

@ -94,12 +94,12 @@ public class ArtistListFragment extends AbstractCursorListFragment {
} }
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Context context) {
super.onAttach(activity); super.onAttach(context);
try { try {
listenerActivity = (OnArtistSelectedListener) activity; listenerActivity = (OnArtistSelectedListener) context;
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnArtistSelectedListener"); throw new ClassCastException(context.toString() + " must implement OnArtistSelectedListener");
} }
setSupportsSearch(true); setSupportsSearch(true);
} }
@ -154,7 +154,7 @@ public class ArtistListFragment extends AbstractCursorListFragment {
@Override @Override
public CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(fragment.getContext()) View view = LayoutInflater.from(fragment.getContext())
.inflate(R.layout.grid_item_artist, parent, false); .inflate(R.layout.grid_item_artist, parent, false);
return new ViewHolder(view, fragment.getContext(), hostManager, artWidth, artHeight, artistlistItemMenuClickListener); return new ViewHolder(view, fragment.getContext(), hostManager, artWidth, artHeight, artistlistItemMenuClickListener);
} }
@ -186,6 +186,8 @@ public class ArtistListFragment extends AbstractCursorListFragment {
popupMenu.show(); popupMenu.show();
} }
}; };
protected int getSectionColumnIdx() { return ArtistListQuery.ARTIST; }
} }
/** /**

View File

@ -90,12 +90,12 @@ public class AudioGenresListFragment extends AbstractCursorListFragment {
} }
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Context ctx) {
super.onAttach(activity); super.onAttach(ctx);
try { try {
listenerActivity = (OnAudioGenreSelectedListener) activity; listenerActivity = (OnAudioGenreSelectedListener) ctx;
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnAudioGenreSelectedListener"); throw new ClassCastException(ctx.toString() + " must implement OnAudioGenreSelectedListener");
} }
setSupportsSearch(true); setSupportsSearch(true);
} }
@ -119,10 +119,10 @@ public class AudioGenresListFragment extends AbstractCursorListFragment {
String SORT = MediaContract.AudioGenres.TITLE + " COLLATE NOCASE ASC"; String SORT = MediaContract.AudioGenres.TITLE + " COLLATE NOCASE ASC";
final int ID = 0; int ID = 0;
final int GENREID = 1; int GENREID = 1;
final int TITLE = 2; int TITLE = 2;
final int THUMBNAIL = 3; int THUMBNAIL = 3;
} }
private class AudioGenresAdapter extends RecyclerViewCursorAdapter { private class AudioGenresAdapter extends RecyclerViewCursorAdapter {
@ -148,6 +148,8 @@ public class AudioGenresListFragment extends AbstractCursorListFragment {
return new ViewHolder(view, getContext(), hostManager, artWidth, artHeight, genrelistItemMenuClickListener); return new ViewHolder(view, getContext(), hostManager, artWidth, artHeight, genrelistItemMenuClickListener);
} }
protected int getSectionColumnIdx() { return AudioGenreListQuery.TITLE; }
} }
/** /**

View File

@ -89,12 +89,12 @@ public class MusicVideoListFragment extends AbstractCursorListFragment {
} }
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Context ctx) {
super.onAttach(activity); super.onAttach(ctx);
try { try {
listenerActivity = (OnMusicVideoSelectedListener) activity; listenerActivity = (OnMusicVideoSelectedListener) ctx;
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnMusicVideoSelectedListener"); throw new ClassCastException(ctx.toString() + " must implement OnMusicVideoSelectedListener");
} }
setSupportsSearch(true); setSupportsSearch(true);
} }
@ -159,6 +159,7 @@ public class MusicVideoListFragment extends AbstractCursorListFragment {
.inflate(R.layout.grid_item_music_video, parent, false); .inflate(R.layout.grid_item_music_video, parent, false);
return new ViewHolder(view, context, hostManager, artWidth, artHeight); return new ViewHolder(view, context, hostManager, artWidth, artHeight);
} }
protected int getSectionColumnIdx() { return MusicVideosListQuery.TITLE; }
} }
/** /**

View File

@ -153,9 +153,9 @@ public class SongsListFragment extends AbstractCursorListFragment {
} }
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Context context) {
setSupportsSearch(true); setSupportsSearch(true);
super.onAttach(activity); super.onAttach(context);
} }
@Override @Override
@ -272,6 +272,8 @@ public class SongsListFragment extends AbstractCursorListFragment {
return new SongViewHolder(view, context, hostManager, artWidth, artHeight, return new SongViewHolder(view, context, hostManager, artWidth, artHeight,
contextMenuClickListener); contextMenuClickListener);
} }
protected int getSectionColumnIdx() { return SongsListQuery.TITLE; }
} }
private static class AlbumSongsAdapter extends RecyclerViewCursorAdapter { private static class AlbumSongsAdapter extends RecyclerViewCursorAdapter {
@ -290,6 +292,8 @@ public class SongsListFragment extends AbstractCursorListFragment {
return new AlbumViewHolder(view, contextMenuClickListener); return new AlbumViewHolder(view, contextMenuClickListener);
} }
protected int getSectionColumnIdx() { return AlbumSongsListQuery.TITLE; }
} }
/** /**

View File

@ -45,6 +45,7 @@ import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.ui.AbstractCursorListFragment; import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.AbstractFragment; import org.xbmc.kore.ui.AbstractFragment;
import org.xbmc.kore.ui.RecyclerViewCursorAdapter; import org.xbmc.kore.ui.RecyclerViewCursorAdapter;
import org.xbmc.kore.ui.sections.audio.SongsListFragment;
import org.xbmc.kore.utils.LogUtils; import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils; import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils; import org.xbmc.kore.utils.Utils;
@ -129,12 +130,12 @@ public class MovieListFragment extends AbstractCursorListFragment {
} }
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Context ctx) {
super.onAttach(activity); super.onAttach(ctx);
try { try {
listenerActivity = (OnMovieSelectedListener) activity; listenerActivity = (OnMovieSelectedListener) ctx;
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnMovieSelectedListener"); throw new ClassCastException(ctx.toString() + " must implement OnMovieSelectedListener");
} }
setSupportsSearch(true); setSupportsSearch(true);
} }
@ -344,6 +345,8 @@ public class MovieListFragment extends AbstractCursorListFragment {
return new ViewHolder(view, getContext(), themeAccentColor, hostManager, artWidth, artHeight); return new ViewHolder(view, getContext(), themeAccentColor, hostManager, artWidth, artHeight);
} }
protected int getSectionColumnIdx() { return MovieListQuery.TITLE; }
} }
/** /**

View File

@ -47,6 +47,7 @@ import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.ui.AbstractCursorListFragment; import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.AbstractFragment; import org.xbmc.kore.ui.AbstractFragment;
import org.xbmc.kore.ui.RecyclerViewCursorAdapter; import org.xbmc.kore.ui.RecyclerViewCursorAdapter;
import org.xbmc.kore.ui.sections.audio.SongsListFragment;
import org.xbmc.kore.utils.LogUtils; import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.MediaPlayerUtils; import org.xbmc.kore.utils.MediaPlayerUtils;
import org.xbmc.kore.utils.UIUtils; import org.xbmc.kore.utils.UIUtils;
@ -250,6 +251,8 @@ public class TVShowEpisodeListFragment extends AbstractCursorListFragment {
contextlistItemMenuClickListener, hostManager, contextlistItemMenuClickListener, hostManager,
artWidth, artHeight); artWidth, artHeight);
} }
protected int getSectionColumnIdx() { return EpisodesListQuery.TITLE; }
} }
/** /**

View File

@ -47,6 +47,7 @@ import org.xbmc.kore.service.library.LibrarySyncService;
import org.xbmc.kore.ui.AbstractCursorListFragment; import org.xbmc.kore.ui.AbstractCursorListFragment;
import org.xbmc.kore.ui.AbstractInfoFragment; import org.xbmc.kore.ui.AbstractInfoFragment;
import org.xbmc.kore.ui.RecyclerViewCursorAdapter; import org.xbmc.kore.ui.RecyclerViewCursorAdapter;
import org.xbmc.kore.ui.sections.audio.SongsListFragment;
import org.xbmc.kore.utils.LogUtils; import org.xbmc.kore.utils.LogUtils;
import org.xbmc.kore.utils.UIUtils; import org.xbmc.kore.utils.UIUtils;
import org.xbmc.kore.utils.Utils; import org.xbmc.kore.utils.Utils;
@ -133,12 +134,12 @@ public class TVShowListFragment extends AbstractCursorListFragment {
} }
@Override @Override
public void onAttach(Activity activity) { public void onAttach(Context ctx) {
super.onAttach(activity); super.onAttach(ctx);
try { try {
listenerActivity = (OnTVShowSelectedListener) activity; listenerActivity = (OnTVShowSelectedListener) ctx;
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnTVShowSelectedListener"); throw new ClassCastException(ctx.toString() + " must implement OnTVShowSelectedListener");
} }
setSupportsSearch(true); setSupportsSearch(true);
} }
@ -354,6 +355,8 @@ public class TVShowListFragment extends AbstractCursorListFragment {
return viewHolder; return viewHolder;
} }
protected int getSectionColumnIdx() { return TVShowListQuery.TITLE; }
} }
/** /**

View File

@ -26,6 +26,8 @@ import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
/** /**
* <p>A Recycler view using a grid layout that supports auto sizing and showing an empty view when the adapter * <p>A Recycler view using a grid layout that supports auto sizing and showing an empty view when the adapter
* has no items. * has no items.
@ -40,7 +42,7 @@ import android.view.View;
* *
* Inspired by <a href="http://blog.sqisland.com/2014/12/recyclerview-autofit-grid.html">RecyclerView: Autofit grid</a> * Inspired by <a href="http://blog.sqisland.com/2014/12/recyclerview-autofit-grid.html">RecyclerView: Autofit grid</a>
*/ */
public class RecyclerViewEmptyViewSupport extends RecyclerView { public class RecyclerViewEmptyViewSupport extends FastScrollRecyclerView {
public final static int AUTO_FIT = -1; public final static int AUTO_FIT = -1;

View File

@ -15,8 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<FrameLayout <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">

View File

@ -44,6 +44,7 @@
<attr name="colorFinished" format="reference|color" /> <attr name="colorFinished" format="reference|color" />
<attr name="separatorColor" format="reference|color" /> <attr name="separatorColor" format="reference|color" />
<attr name="fastscrollHandleColor" format="reference|color" />
<attr name="drawerBackgroundColor" format="reference|color" /> <attr name="drawerBackgroundColor" format="reference|color" />

View File

@ -63,6 +63,9 @@
<color name="dark_separator">#ff505050</color> <color name="dark_separator">#ff505050</color>
<color name="light_separator">#ffafafaf</color> <color name="light_separator">#ffafafaf</color>
<color name="dark_fastscroll_handle">#ff505050</color>
<color name="light_fastscroll_handle">#ff606060</color>
<!-- Drawer colors --> <!-- Drawer colors -->
<color name="dark_drawer_background">@color/dark_content_background</color> <color name="dark_drawer_background">@color/dark_content_background</color>
<color name="light_drawer_background">@color/light_content_background</color> <color name="light_drawer_background">@color/light_content_background</color>

View File

@ -35,7 +35,7 @@
<dimen name="small_icon_size">24dp</dimen> <dimen name="small_icon_size">24dp</dimen>
<dimen name="medium_icon_size">32dp</dimen> <dimen name="medium_icon_size">32dp</dimen>
<dimen name="card_corner_radius">2dp</dimen> <dimen name="card_corner_radius">4dp</dimen>
<dimen name="default_card_elevation">2dp</dimen> <dimen name="default_card_elevation">2dp</dimen>
<dimen name="default_card_margin">2dp</dimen> <dimen name="default_card_margin">2dp</dimen>
@ -43,7 +43,10 @@
<dimen name="text_size_medium">14sp</dimen> <dimen name="text_size_medium">14sp</dimen>
<dimen name="text_size_large">16sp</dimen> <dimen name="text_size_large">16sp</dimen>
<dimen name="text_size_xlarge">20sp</dimen> <dimen name="text_size_xlarge">20sp</dimen>
<dimen name="text_size_xxlarge">34sp</dimen> <dimen name="text_size_xxlarge">32sp</dimen>
<dimen name="fastscroll_popup_size">80dp</dimen>
<dimen name="fastscroll_text_size">36sp</dimen>
<dimen name="default_list_item_height">56dp</dimen> <dimen name="default_list_item_height">56dp</dimen>

View File

@ -55,14 +55,22 @@
</style> </style>
<style name="GridLayoutRecyclerView"> <style name="GridLayoutRecyclerView">
<item name="fastScrollPopupBgColor">?attr/colorPrimary</item>
<item name="fastScrollPopupTextColor">?attr/textColorOverPrimary</item>
<item name="fastScrollPopupTextSize">@dimen/fastscroll_text_size</item>
<item name="fastScrollPopupBackgroundSize">@dimen/fastscroll_popup_size</item>
<item name="fastScrollThumbColor">?attr/colorPrimary</item>
<item name="fastScrollThumbInactiveColor">?attr/fastscrollHandleColor</item>
<item name="fastScrollPopupPosition">adjacent</item>
<item name="android:clipToPadding">false</item> <item name="android:clipToPadding">false</item>
<item name="android:scrollbars">vertical</item>
<item name="android:paddingBottom">@dimen/default_padding</item> <item name="android:paddingBottom">@dimen/default_padding</item>
<item name="android:paddingLeft">@dimen/default_card_margin</item> <item name="android:paddingLeft">@dimen/default_card_margin</item>
<item name="android:columnWidth">@dimen/default_grid_column_width</item> <item name="android:columnWidth">@dimen/default_grid_column_width</item>
</style> </style>
<style name="Widget.CardView"> <style name="Widget.CardView">
<item name="cardCornerRadius">@dimen/card_corner_radius</item>
<item name="cardElevation">@dimen/default_card_elevation</item> <item name="cardElevation">@dimen/default_card_elevation</item>
<item name="cardBackgroundColor">?attr/appCardBackgroundColor</item> <item name="cardBackgroundColor">?attr/appCardBackgroundColor</item>
<item name="android:layout_marginRight">@dimen/default_card_margin</item> <item name="android:layout_marginRight">@dimen/default_card_margin</item>

View File

@ -86,6 +86,7 @@
<item name="drawerBackgroundColor">@color/dark_drawer_background</item> <item name="drawerBackgroundColor">@color/dark_drawer_background</item>
<item name="separatorColor">@color/dark_separator</item> <item name="separatorColor">@color/dark_separator</item>
<item name="fastscrollHandleColor">@color/dark_fastscroll_handle</item>
<item name="remoteButtonColorFilter">@color/white</item> <item name="remoteButtonColorFilter">@color/white</item>
<item name="defaultButtonColorFilter">@color/white</item> <item name="defaultButtonColorFilter">@color/white</item>
@ -225,6 +226,7 @@
<item name="drawerBackgroundColor">@color/light_drawer_background</item> <item name="drawerBackgroundColor">@color/light_drawer_background</item>
<item name="separatorColor">@color/light_separator</item> <item name="separatorColor">@color/light_separator</item>
<item name="fastscrollHandleColor">@color/light_fastscroll_handle</item>
<item name="remoteButtonColorFilter">@color/black_dim_54pct</item> <item name="remoteButtonColorFilter">@color/black_dim_54pct</item>
<item name="defaultButtonColorFilter">@color/black_dim_54pct</item> <item name="defaultButtonColorFilter">@color/black_dim_54pct</item>