From ebb030da99785161852a8cf3fc3d62b602758165 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 19 Aug 2015 18:56:01 -0700 Subject: Put back fast scrolling for artists, podcasts, and playlists --- .../daneren2005/dsub/adapter/ArtistAdapter.java | 14 +- .../daneren2005/dsub/adapter/PlaylistAdapter.java | 14 +- .../dsub/adapter/PodcastChannelAdapter.java | 14 +- .../dsub/fragments/SelectRecyclerFragment.java | 11 +- .../github/daneren2005/dsub/view/FastScroller.java | 208 +++++++++++++++++++++ 5 files changed, 257 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/github/daneren2005/dsub/view/FastScroller.java (limited to 'app/src/main/java/github/daneren2005') diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java index 7461af69..9116e587 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/ArtistAdapter.java @@ -30,9 +30,10 @@ import github.daneren2005.dsub.domain.Artist; import github.daneren2005.dsub.domain.MusicFolder; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.view.ArtistView; +import github.daneren2005.dsub.view.FastScroller; import github.daneren2005.dsub.view.UpdateView; -public class ArtistAdapter extends SectionAdapter { +public class ArtistAdapter extends SectionAdapter implements FastScroller.BubbleTextGetter { public static int VIEW_TYPE_ARTIST = 4; private List musicFolders; @@ -122,6 +123,17 @@ public class ArtistAdapter extends SectionAdapter { return VIEW_TYPE_ARTIST; } + @Override + public String getTextToShowInBubble(int position) { + Artist artist = getItemForPosition(position); + + if(artist == null) { + return ""; + } else { + return artist.getName().substring(0, 1); + } + } + public interface OnMusicFolderChanged { void onMusicFolderChanged(MusicFolder musicFolder); } diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java index 4221677e..d7a9e8d7 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/PlaylistAdapter.java @@ -21,10 +21,11 @@ import java.util.List; import android.view.ViewGroup; import github.daneren2005.dsub.domain.Playlist; import github.daneren2005.dsub.util.ImageLoader; +import github.daneren2005.dsub.view.FastScroller; import github.daneren2005.dsub.view.PlaylistView; import github.daneren2005.dsub.view.UpdateView; -public class PlaylistAdapter extends SectionAdapter { +public class PlaylistAdapter extends SectionAdapter implements FastScroller.BubbleTextGetter { public static int VIEW_TYPE_PLAYLIST = 1; private ImageLoader imageLoader; @@ -58,4 +59,15 @@ public class PlaylistAdapter extends SectionAdapter { public int getItemViewType(Playlist playlist) { return VIEW_TYPE_PLAYLIST; } + + @Override + public String getTextToShowInBubble(int position) { + Playlist playlist = getItemForPosition(position); + + if(playlist == null) { + return ""; + } else { + return playlist.getName().substring(0, 1); + } + } } diff --git a/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java b/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java index dc94178d..c1b132c7 100644 --- a/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java +++ b/app/src/main/java/github/daneren2005/dsub/adapter/PodcastChannelAdapter.java @@ -17,12 +17,13 @@ package github.daneren2005.dsub.adapter; import android.content.Context; import android.view.ViewGroup; import github.daneren2005.dsub.domain.PodcastChannel; +import github.daneren2005.dsub.view.FastScroller; import github.daneren2005.dsub.view.PodcastChannelView; import github.daneren2005.dsub.view.UpdateView; import java.util.List; -public class PodcastChannelAdapter extends SectionAdapter{ +public class PodcastChannelAdapter extends SectionAdapter implements FastScroller.BubbleTextGetter { public static int VIEW_TYPE_PODCAST = 1; public PodcastChannelAdapter(Context context, List podcasts, OnItemClickedListener listener) { @@ -44,4 +45,15 @@ public class PodcastChannelAdapter extends SectionAdapter{ public int getItemViewType(PodcastChannel item) { return VIEW_TYPE_PODCAST; } + + @Override + public String getTextToShowInBubble(int position) { + PodcastChannel podcast = getItemForPosition(position); + + if(podcast == null) { + return ""; + } else { + return podcast.getName().substring(0, 1); + } + } } diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java index 9dc3e363..adf22484 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectRecyclerFragment.java @@ -15,7 +15,6 @@ package github.daneren2005.dsub.fragments; -import android.content.Context; import android.os.Bundle; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.RecyclerView; @@ -38,10 +37,12 @@ import github.daneren2005.dsub.service.MusicServiceFactory; import github.daneren2005.dsub.util.Constants; import github.daneren2005.dsub.util.ProgressListener; import github.daneren2005.dsub.util.TabBackgroundTask; +import github.daneren2005.dsub.view.FastScroller; public abstract class SelectRecyclerFragment extends SubsonicFragment implements SectionAdapter.OnItemClickedListener { private static final String TAG = SelectRecyclerFragment.class.getSimpleName(); protected RecyclerView recyclerView; + protected FastScroller fastScroller; protected SectionAdapter adapter; protected UpdateTask currentTask; protected List objects; @@ -77,6 +78,7 @@ public abstract class SelectRecyclerFragment extends SubsonicFragment impleme refreshLayout.setOnRefreshListener(this); recyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_recycler); + fastScroller = (FastScroller) rootView.findViewById(R.id.fragment_fast_scroller); setupLayoutManager(); if(pullToRefresh) { @@ -184,6 +186,13 @@ public abstract class SelectRecyclerFragment extends SubsonicFragment impleme public void done(List result) { if (result != null && !result.isEmpty()) { recyclerView.setAdapter(adapter = getAdapter(result)); + if(adapter instanceof FastScroller.BubbleTextGetter) { + if(!fastScroller.isAttached()) { + fastScroller.attachRecyclerView(recyclerView); + } + } else if(fastScroller.isAttached()) { + fastScroller.detachRecyclerView(); + } onFinishRefresh(); recyclerView.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/github/daneren2005/dsub/view/FastScroller.java b/app/src/main/java/github/daneren2005/dsub/view/FastScroller.java new file mode 100644 index 00000000..ee7aef4b --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/view/FastScroller.java @@ -0,0 +1,208 @@ +/* + This file is part of Subsonic. + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + Copyright 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.view; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import github.daneren2005.dsub.R; + +import static android.support.v7.widget.RecyclerView.OnScrollListener; + +public class FastScroller extends LinearLayout { + private static final int BUBBLE_ANIMATION_DURATION = 100; + private static final int TRACK_SNAP_RANGE = 5; + + private TextView bubble; + private View handle; + private RecyclerView recyclerView; + private final ScrollListener scrollListener = new ScrollListener(); + private int height; + + private ObjectAnimator currentAnimator = null; + + public FastScroller(final Context context,final AttributeSet attrs,final int defStyleAttr) { + super(context,attrs,defStyleAttr); + initialise(context); + } + + public FastScroller(final Context context) { + super(context); + initialise(context); + } + + public FastScroller(final Context context,final AttributeSet attrs) { + super(context,attrs); + initialise(context); + } + + private void initialise(Context context) { + setOrientation(HORIZONTAL); + setClipChildren(false); + LayoutInflater inflater = LayoutInflater.from(context); + inflater.inflate(R.layout.fast_scroller,this,true); + bubble = (TextView)findViewById(R.id.fastscroller_bubble); + handle = findViewById(R.id.fastscroller_handle); + bubble.setVisibility(INVISIBLE); + setVisibility(GONE); + } + + @Override + protected void onSizeChanged(int w,int h,int oldw,int oldh) { + super.onSizeChanged(w,h,oldw,oldh); + height = h; + } + + @Override + public boolean onTouchEvent(@NonNull MotionEvent event) { + final int action = event.getAction(); + switch(action) + { + case MotionEvent.ACTION_DOWN: + if(event.getX()=height-TRACK_SNAP_RANGE) + proportion = 1f; + else + proportion = y/(float)height; + int targetPos = getValueInRange(0,itemCount-1,(int)(proportion*(float)itemCount)); + ((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(targetPos,0); + String bubbleText = ((BubbleTextGetter)recyclerView.getAdapter()).getTextToShowInBubble(targetPos); + bubble.setText(bubbleText); + } + } + + private int getValueInRange(int min,int max,int value) { + int minimum = Math.max(min,value); + return Math.min(minimum,max); + } + + private void setBubbleAndHandlePosition(float y) { + int bubbleHeight = bubble.getHeight(); + int handleHeight = handle.getHeight(); + handle.setY(getValueInRange(0,height-handleHeight,(int)(y-handleHeight/2))); + bubble.setY(getValueInRange(0,height-bubbleHeight-handleHeight/2,(int)(y-bubbleHeight))); + } + + private void showBubble() { + AnimatorSet animatorSet = new AnimatorSet(); + bubble.setVisibility(VISIBLE); + if(currentAnimator != null) + currentAnimator.cancel(); + currentAnimator = ObjectAnimator.ofFloat(bubble,"alpha",0f,1f).setDuration(BUBBLE_ANIMATION_DURATION); + currentAnimator.start(); + } + + private void hideBubble() { + if(currentAnimator != null) + currentAnimator.cancel(); + currentAnimator = ObjectAnimator.ofFloat(bubble,"alpha",1f,0f).setDuration(BUBBLE_ANIMATION_DURATION); + currentAnimator.addListener(new AnimatorListenerAdapter(){ + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + bubble.setVisibility(INVISIBLE); + currentAnimator = null; + } + + @Override + public void onAnimationCancel(Animator animation) { + super.onAnimationCancel(animation); + bubble.setVisibility(INVISIBLE); + currentAnimator = null; + } + }); + currentAnimator.start(); + } + + private class ScrollListener extends OnScrollListener { + @Override + public void onScrolled(RecyclerView rv,int dx,int dy) { + View firstVisibleView = recyclerView.getChildAt(0); + int firstVisiblePosition = recyclerView.getChildPosition(firstVisibleView); + int visibleRange = recyclerView.getChildCount(); + int lastVisiblePosition = firstVisiblePosition+visibleRange; + int itemCount = recyclerView.getAdapter().getItemCount(); + int position; + if(firstVisiblePosition == 0) + position = 0; + else if(lastVisiblePosition == itemCount) + position = itemCount; + else + position = (int)(((float)firstVisiblePosition/(((float)itemCount-(float)visibleRange)))*(float)itemCount); + float proportion = (float)position/(float)itemCount; + setBubbleAndHandlePosition(height*proportion); + } + } + + public interface BubbleTextGetter { + String getTextToShowInBubble(int position); + } +} -- cgit v1.2.3