Last active
March 14, 2016 14:13
-
-
Save imminent/4576886 to your computer and use it in GitHub Desktop.
Quick return bar for ListView
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<FrameLayout android:id="@android:id/empty" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_gravity="center" | |
android:visibility="gone" /> | |
<ListView | |
android:id="@android:id/list" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:cacheColorHint="@android:color/white" | |
android:drawSelectorOnTop="false" /> | |
<LinearLayout style="@style/StickyItem" | |
android:id="@+id/quick_return_bar" | |
android:layout_gravity="bottom" | |
android:gravity="center" | |
android:orientation="horizontal" /> | |
</FrameLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.os.Bundle; | |
import android.view.LayoutInflater; | |
import android.view.View; | |
import android.view.ViewGroup; | |
import android.widget.AbsListView; | |
import android.widget.ListView; | |
import android.support.v4.app.ListFragment; | |
import com.nineoldandroids.animation.Animator; | |
import com.nineoldandroids.animation.ObjectAnimator; | |
/** | |
* {@link android.support.v4.app.ListFragment} that hosts a quick return bar. The bar can be found at | |
* {@link com.keepandshare.android.R.id#quick_return_bar} and Views can be added to it. | |
* @author Dandré Allison | |
* @see <a href="https://plus.google.com/u/0/113735310430199015092/posts/1Sb549FvpJt">Quick Return post</a> | |
*/ | |
@SuppressWarnings("UnusedDeclaration") | |
public class QuickReturnListFragment extends ListFragment implements AbsListView.OnScrollListener { | |
public QuickReturnListFragment() { } | |
/* Fragment lifecycle methods */ | |
@Override | |
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle) { | |
final ViewGroup root_view = (ViewGroup) inflater | |
.inflate(R.layout.fragment_quick_return, container, false); | |
// Gathers views | |
final View _quick_return_bar = root_view.findViewById(R.id.quick_return_bar); | |
// Sets up animation | |
_quick_return_bar_return_animator = ObjectAnimator.ofFloat(_quick_return_bar, | |
"translationY", | |
0); | |
_quick_return_bar_return_animator.addListener( | |
new Animator.AnimatorListener() { | |
@Override | |
public void onAnimationStart(Animator animator) { | |
_state = QuickReturnState.RETURNING; | |
} | |
@Override | |
public void onAnimationEnd(Animator animator) { | |
_state = QuickReturnState.ON_SCREEN; | |
} | |
@Override | |
public void onAnimationCancel(Animator animator) { | |
_state = QuickReturnState.OFF_SCREEN; | |
} | |
@Override | |
public void onAnimationRepeat(Animator animator) { | |
} | |
} | |
); | |
_quick_return_bar_hide_animator = ObjectAnimator.ofFloat(_quick_return_bar, | |
"translationY", | |
getResources().getDimension(R.dimen.quick_return_bar_height)); | |
_quick_return_bar_hide_animator.addListener( | |
new Animator.AnimatorListener() { | |
@Override | |
public void onAnimationStart(Animator animator) { | |
_state = QuickReturnState.HIDING; | |
} | |
@Override | |
public void onAnimationEnd(Animator animator) { | |
_state = QuickReturnState.OFF_SCREEN; | |
} | |
@Override | |
public void onAnimationCancel(Animator animator) { | |
_state = QuickReturnState.ON_SCREEN; | |
} | |
@Override | |
public void onAnimationRepeat(Animator animator) { } | |
} | |
); | |
return root_view; | |
} | |
@Override | |
public void onViewCreated(View view, Bundle icicle) { | |
super.onViewCreated(view, icicle); | |
final ListView list_view = getListView(); | |
list_view.setOnScrollListener(this); | |
_last_first_child = list_view.getFirstVisiblePosition(); | |
final View first_child = list_view.getChildAt(_last_first_child); | |
_last_y = first_child == null ? 0 : first_child.getTop(); | |
} | |
/* AbsListView.OnScrollListener */ | |
@Override | |
public void onScroll(AbsListView list_view, int first_visible_child, int _, int __) { | |
// Computes the scroll Y position | |
final View first_child = list_view.getChildAt(first_visible_child); | |
int y_position = first_child == null? 0 : first_child.getTop(); | |
switch (_state) { | |
case OFF_SCREEN: | |
// * Return quick return bar if first visible child is the same AND it's Y position increases | |
// * Return quick return bar if first visible child changes to a previous sibling | |
// * AND only if the quick return bar isn't already returning | |
if (!quickReturnBarIsReturning() | |
&& (first_visible_child == _last_first_child && y_position > _last_y) | |
|| first_visible_child < _last_first_child) | |
_quick_return_bar_return_animator.start(); | |
break; | |
case ON_SCREEN: | |
// * Hide quick return bar if first visible child is the same AND it's Y position decreases | |
// * Hide quick return bar if first visible child changes to a later sibling | |
// * AND only if the quick return bar isn't already going away | |
if (!quickReturnBarIsGoingAway() | |
&& (first_visible_child == _last_first_child && y_position < _last_y) | |
|| first_visible_child > _last_first_child) | |
_quick_return_bar_hide_animator.start(); | |
break; | |
case RETURNING: | |
// * Cancel return of quick return bar if first visible child is the same AND it's Y position decreases | |
// * Cancel return of quick return bar if first visible child changes to a later sibling | |
if ((first_visible_child == _last_first_child && y_position < _last_y) | |
|| first_visible_child > _last_first_child) { | |
_quick_return_bar_return_animator.cancel(); | |
_quick_return_bar_hide_animator.start(); | |
} | |
break; | |
case HIDING: | |
// * Cancel hide of quick return bar if first visible child is the same AND it's Y position increases | |
// * Cancel hide of quick return bar if first visible child changes to a previous sibling | |
if ((first_visible_child == _last_first_child && y_position > _last_y) | |
|| first_visible_child < _last_first_child) { | |
_quick_return_bar_hide_animator.cancel(); | |
_quick_return_bar_return_animator.start(); | |
} | |
break; | |
} | |
_last_first_child = first_visible_child; | |
_last_y = y_position; | |
} | |
@Override | |
public void onScrollStateChanged(AbsListView _, int __) { } | |
/* Transition status checks */ | |
/** | |
* Checks if the quick return bar is transitioning back onto the screen. | |
* @return {@code true} indicates that the quick return bar is returning | |
*/ | |
private boolean quickReturnBarIsReturning() { | |
// This should be equivalent to checking that the quick return bar is RETURNING | |
return _quick_return_bar_return_animator.isRunning() || _quick_return_bar_return_animator.isStarted(); | |
} | |
/** | |
* Checks if the quick return bar is transitioning off of the screen. | |
* @return {@code true} indicates that the quick return bar is going away | |
*/ | |
private boolean quickReturnBarIsGoingAway() { | |
return _quick_return_bar_hide_animator.isRunning() || _quick_return_bar_hide_animator.isStarted(); | |
} | |
/** | |
* Represents the state of the quick return bar. | |
*/ | |
private static enum QuickReturnState { | |
/** Stable state indicating that the quick return bar is visible on screen */ | |
ON_SCREEN, | |
/** Stable state indicating that the quick return bar is hidden off screen */ | |
OFF_SCREEN, | |
/** Transitive state indicating that the quick return bar is coming onto the screen */ | |
RETURNING, | |
/** Transitive state indicating that the quick return bar is going off of the screen */ | |
HIDING | |
} | |
/** The current state of the quick return bar */ | |
private QuickReturnState _state = QuickReturnState.ON_SCREEN; | |
/** Tracks the last seen y-position of the first visible child */ | |
private int _last_y; | |
/** Tracks the last seen first visible child */ | |
private int _last_first_child; | |
/** Animates the quick return bar off of the screen */ | |
private ObjectAnimator _quick_return_bar_hide_animator; | |
/** Animates the quick return bar onto the screen */ | |
private ObjectAnimator _quick_return_bar_return_animator; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment