Merge "Allow ActionBarShadowController to attach to Views."

This commit is contained in:
TreeHugger Robot
2019-02-01 15:50:52 +00:00
committed by Android (Google) Code Review
2 changed files with 43 additions and 36 deletions

View File

@@ -27,7 +27,6 @@ import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.recyclerview.widget.RecyclerView;
/**
* UI controller that adds a shadow appear/disappear animation to action bar scroll.
@@ -41,40 +40,36 @@ public class ActionBarShadowController implements LifecycleObserver {
@VisibleForTesting
ScrollChangeWatcher mScrollChangeWatcher;
private RecyclerView mRecyclerView;
private View mScrollView;
private boolean mIsScrollWatcherAttached;
/**
* Wire up the animation to to an {@link Activity}. Shadow will be applied to activity's
* action bar.
*/
public static ActionBarShadowController attachToRecyclerView(
Activity activity, Lifecycle lifecycle, RecyclerView recyclerView) {
return new ActionBarShadowController(activity, lifecycle, recyclerView);
public static ActionBarShadowController attachToView(
Activity activity, Lifecycle lifecycle, View scrollView) {
return new ActionBarShadowController(activity, lifecycle, scrollView);
}
/**
* Wire up the animation to to a {@link View}. Shadow will be applied to the view.
*/
public static ActionBarShadowController attachToRecyclerView(
View anchorView, Lifecycle lifecycle, RecyclerView recyclerView) {
return new ActionBarShadowController(anchorView, lifecycle, recyclerView);
public static ActionBarShadowController attachToView(
View anchorView, Lifecycle lifecycle, View scrollView) {
return new ActionBarShadowController(anchorView, lifecycle, scrollView);
}
private ActionBarShadowController(Activity activity, Lifecycle lifecycle,
RecyclerView recyclerView) {
mScrollChangeWatcher =
new ActionBarShadowController.ScrollChangeWatcher(activity);
mRecyclerView = recyclerView;
private ActionBarShadowController(Activity activity, Lifecycle lifecycle, View scrollView) {
mScrollChangeWatcher = new ActionBarShadowController.ScrollChangeWatcher(activity);
mScrollView = scrollView;
attachScrollWatcher();
lifecycle.addObserver(this);
}
private ActionBarShadowController(View anchorView, Lifecycle lifecycle,
RecyclerView recyclerView) {
mScrollChangeWatcher =
new ActionBarShadowController.ScrollChangeWatcher(anchorView);
mRecyclerView = recyclerView;
private ActionBarShadowController(View anchorView, Lifecycle lifecycle, View scrollView) {
mScrollChangeWatcher = new ActionBarShadowController.ScrollChangeWatcher(anchorView);
mScrollView = scrollView;
attachScrollWatcher();
lifecycle.addObserver(this);
}
@@ -83,21 +78,21 @@ public class ActionBarShadowController implements LifecycleObserver {
private void attachScrollWatcher() {
if (!mIsScrollWatcherAttached) {
mIsScrollWatcherAttached = true;
mRecyclerView.addOnScrollListener(mScrollChangeWatcher);
mScrollChangeWatcher.updateDropShadow(mRecyclerView);
mScrollView.setOnScrollChangeListener(mScrollChangeWatcher);
mScrollChangeWatcher.updateDropShadow(mScrollView);
}
}
@OnLifecycleEvent(ON_STOP)
private void detachScrollWatcher() {
mRecyclerView.removeOnScrollListener(mScrollChangeWatcher);
mScrollView.setOnScrollChangeListener(null);
mIsScrollWatcherAttached = false;
}
/**
* Update the drop shadow as the scrollable entity is scrolled.
*/
final class ScrollChangeWatcher extends RecyclerView.OnScrollListener {
final class ScrollChangeWatcher implements View.OnScrollChangeListener {
private final Activity mActivity;
private final View mAnchorView;
@@ -112,9 +107,9 @@ public class ActionBarShadowController implements LifecycleObserver {
mActivity = null;
}
// RecyclerView scrolled.
@Override
public void onScrolled(RecyclerView view, int dx, int dy) {
public void onScrollChange(View view, int scrollX, int scrollY, int oldScrollX,
int oldScrollY) {
updateDropShadow(view);
}

View File

@@ -22,6 +22,7 @@ import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -49,6 +50,8 @@ public class ActionBarShadowControllerTest {
@Mock
private RecyclerView mRecyclerView;
@Mock
private View mScrollView;
@Mock
private Activity mActivity;
@Mock
private ActionBar mActionBar;
@@ -66,51 +69,60 @@ public class ActionBarShadowControllerTest {
}
@Test
public void attachToRecyclerView_shouldAddScrollWatcherAndUpdateActionBar() {
public void attachToView_shouldAddScrollWatcherAndUpdateActionBar() {
when(mRecyclerView.canScrollVertically(-1)).thenReturn(false);
ActionBarShadowController.attachToRecyclerView(mActivity, mLifecycle, mRecyclerView);
ActionBarShadowController.attachToView(mActivity, mLifecycle, mRecyclerView);
verify(mActionBar).setElevation(ActionBarShadowController.ELEVATION_LOW);
}
@Test
public void attachToRecyclerView_customViewAsActionBar_shouldUpdateElevationOnScroll() {
public void attachToView_scrollView_shouldAddScrollWatcherAndUpdateActionBar() {
when(mScrollView.canScrollVertically(-1)).thenReturn(false);
ActionBarShadowController.attachToView(mActivity, mLifecycle, mScrollView);
verify(mActionBar).setElevation(ActionBarShadowController.ELEVATION_LOW);
}
@Test
public void attachToView_customViewAsActionBar_shouldUpdateElevationOnScroll() {
// Setup
mView.setElevation(50);
when(mRecyclerView.canScrollVertically(-1)).thenReturn(false);
final ActionBarShadowController controller =
ActionBarShadowController.attachToRecyclerView(mView, mLifecycle, mRecyclerView);
ActionBarShadowController.attachToView(mView, mLifecycle, mRecyclerView);
assertThat(mView.getElevation()).isEqualTo(ActionBarShadowController.ELEVATION_LOW);
// Scroll
when(mRecyclerView.canScrollVertically(-1)).thenReturn(true);
controller.mScrollChangeWatcher.onScrolled(mRecyclerView, 10 /* dx */, 10 /* dy */);
controller.mScrollChangeWatcher.onScrollChange(mRecyclerView, 10, 10, 0, 0);
assertThat(mView.getElevation()).isEqualTo(ActionBarShadowController.ELEVATION_HIGH);
}
@Test
public void attachToRecyclerView_lifecycleChange_shouldAttachDetach() {
ActionBarShadowController.attachToRecyclerView(mActivity, mLifecycle, mRecyclerView);
public void attachToView_lifecycleChange_shouldAttachDetach() {
ActionBarShadowController.attachToView(mActivity, mLifecycle, mRecyclerView);
verify(mRecyclerView).addOnScrollListener(any());
verify(mRecyclerView).setOnScrollChangeListener(any());
mLifecycle.handleLifecycleEvent(ON_START);
mLifecycle.handleLifecycleEvent(ON_STOP);
verify(mRecyclerView).removeOnScrollListener(any());
verify(mRecyclerView).setOnScrollChangeListener(isNull());
mLifecycle.handleLifecycleEvent(ON_START);
verify(mRecyclerView, times(2)).addOnScrollListener(any());
verify(mRecyclerView, times(3)).setOnScrollChangeListener(any());
}
@Test
public void onScrolled_nullAnchorViewAndActivity_shouldNotCrash() {
final Activity activity = null;
final ActionBarShadowController controller =
ActionBarShadowController.attachToRecyclerView(activity, mLifecycle, mRecyclerView);
ActionBarShadowController.attachToView(activity, mLifecycle, mRecyclerView);
// Scroll
controller.mScrollChangeWatcher.onScrolled(mRecyclerView, 10 /* dx */, 10 /* dy */);
controller.mScrollChangeWatcher.onScrollChange(mRecyclerView, 10, 10, 0, 0);
// no crash
}
}