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 'at.blogc:expandabletextview:1.0.3'
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:rules:1.0.1'

View File

@ -20,11 +20,15 @@ import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
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 Cursor cursor;
protected Cursor cursor;
@Override
public void onBindViewHolder(CursorViewHolder holder, int position) {
@ -59,6 +63,25 @@ abstract public class RecyclerViewCursorAdapter extends RecyclerView.Adapter<Rec
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) {
if (newCursor == cursor) {
return;

View File

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

View File

@ -94,12 +94,12 @@ public class ArtistListFragment extends AbstractCursorListFragment {
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
public void onAttach(Context context) {
super.onAttach(context);
try {
listenerActivity = (OnArtistSelectedListener) activity;
listenerActivity = (OnArtistSelectedListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnArtistSelectedListener");
throw new ClassCastException(context.toString() + " must implement OnArtistSelectedListener");
}
setSupportsSearch(true);
}
@ -154,7 +154,7 @@ public class ArtistListFragment extends AbstractCursorListFragment {
@Override
public CursorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
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);
}
@ -186,6 +186,8 @@ public class ArtistListFragment extends AbstractCursorListFragment {
popupMenu.show();
}
};
protected int getSectionColumnIdx() { return ArtistListQuery.ARTIST; }
}
/**

View File

@ -90,12 +90,12 @@ public class AudioGenresListFragment extends AbstractCursorListFragment {
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
public void onAttach(Context ctx) {
super.onAttach(ctx);
try {
listenerActivity = (OnAudioGenreSelectedListener) activity;
listenerActivity = (OnAudioGenreSelectedListener) ctx;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnAudioGenreSelectedListener");
throw new ClassCastException(ctx.toString() + " must implement OnAudioGenreSelectedListener");
}
setSupportsSearch(true);
}
@ -119,10 +119,10 @@ public class AudioGenresListFragment extends AbstractCursorListFragment {
String SORT = MediaContract.AudioGenres.TITLE + " COLLATE NOCASE ASC";
final int ID = 0;
final int GENREID = 1;
final int TITLE = 2;
final int THUMBNAIL = 3;
int ID = 0;
int GENREID = 1;
int TITLE = 2;
int THUMBNAIL = 3;
}
private class AudioGenresAdapter extends RecyclerViewCursorAdapter {
@ -148,6 +148,8 @@ public class AudioGenresListFragment extends AbstractCursorListFragment {
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
public void onAttach(Activity activity) {
super.onAttach(activity);
public void onAttach(Context ctx) {
super.onAttach(ctx);
try {
listenerActivity = (OnMusicVideoSelectedListener) activity;
listenerActivity = (OnMusicVideoSelectedListener) ctx;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnMusicVideoSelectedListener");
throw new ClassCastException(ctx.toString() + " must implement OnMusicVideoSelectedListener");
}
setSupportsSearch(true);
}
@ -159,6 +159,7 @@ public class MusicVideoListFragment extends AbstractCursorListFragment {
.inflate(R.layout.grid_item_music_video, parent, false);
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
public void onAttach(Activity activity) {
public void onAttach(Context context) {
setSupportsSearch(true);
super.onAttach(activity);
super.onAttach(context);
}
@Override
@ -272,6 +272,8 @@ public class SongsListFragment extends AbstractCursorListFragment {
return new SongViewHolder(view, context, hostManager, artWidth, artHeight,
contextMenuClickListener);
}
protected int getSectionColumnIdx() { return SongsListQuery.TITLE; }
}
private static class AlbumSongsAdapter extends RecyclerViewCursorAdapter {
@ -290,6 +292,8 @@ public class SongsListFragment extends AbstractCursorListFragment {
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.AbstractFragment;
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.UIUtils;
import org.xbmc.kore.utils.Utils;
@ -129,12 +130,12 @@ public class MovieListFragment extends AbstractCursorListFragment {
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
public void onAttach(Context ctx) {
super.onAttach(ctx);
try {
listenerActivity = (OnMovieSelectedListener) activity;
listenerActivity = (OnMovieSelectedListener) ctx;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnMovieSelectedListener");
throw new ClassCastException(ctx.toString() + " must implement OnMovieSelectedListener");
}
setSupportsSearch(true);
}
@ -344,6 +345,8 @@ public class MovieListFragment extends AbstractCursorListFragment {
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.AbstractFragment;
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.MediaPlayerUtils;
import org.xbmc.kore.utils.UIUtils;
@ -250,6 +251,8 @@ public class TVShowEpisodeListFragment extends AbstractCursorListFragment {
contextlistItemMenuClickListener, hostManager,
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.AbstractInfoFragment;
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.UIUtils;
import org.xbmc.kore.utils.Utils;
@ -133,12 +134,12 @@ public class TVShowListFragment extends AbstractCursorListFragment {
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
public void onAttach(Context ctx) {
super.onAttach(ctx);
try {
listenerActivity = (OnTVShowSelectedListener) activity;
listenerActivity = (OnTVShowSelectedListener) ctx;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnTVShowSelectedListener");
throw new ClassCastException(ctx.toString() + " must implement OnTVShowSelectedListener");
}
setSupportsSearch(true);
}
@ -354,6 +355,8 @@ public class TVShowListFragment extends AbstractCursorListFragment {
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.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
* 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>
*/
public class RecyclerViewEmptyViewSupport extends RecyclerView {
public class RecyclerViewEmptyViewSupport extends FastScrollRecyclerView {
public final static int AUTO_FIT = -1;

View File

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

View File

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

View File

@ -63,6 +63,9 @@
<color name="dark_separator">#ff505050</color>
<color name="light_separator">#ffafafaf</color>
<color name="dark_fastscroll_handle">#ff505050</color>
<color name="light_fastscroll_handle">#ff606060</color>
<!-- Drawer colors -->
<color name="dark_drawer_background">@color/dark_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="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_margin">2dp</dimen>
@ -43,7 +43,10 @@
<dimen name="text_size_medium">14sp</dimen>
<dimen name="text_size_large">16sp</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>

View File

@ -55,14 +55,22 @@
</style>
<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:scrollbars">vertical</item>
<item name="android:paddingBottom">@dimen/default_padding</item>
<item name="android:paddingLeft">@dimen/default_card_margin</item>
<item name="android:columnWidth">@dimen/default_grid_column_width</item>
</style>
<style name="Widget.CardView">
<item name="cardCornerRadius">@dimen/card_corner_radius</item>
<item name="cardElevation">@dimen/default_card_elevation</item>
<item name="cardBackgroundColor">?attr/appCardBackgroundColor</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="separatorColor">@color/dark_separator</item>
<item name="fastscrollHandleColor">@color/dark_fastscroll_handle</item>
<item name="remoteButtonColorFilter">@color/white</item>
<item name="defaultButtonColorFilter">@color/white</item>
@ -225,6 +226,7 @@
<item name="drawerBackgroundColor">@color/light_drawer_background</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="defaultButtonColorFilter">@color/black_dim_54pct</item>