mirror of
https://github.com/material-components/material-components-android.git
synced 2026-02-20 08:39:55 +08:00
188 lines
8.1 KiB
Java
188 lines
8.1 KiB
Java
/*
|
|
* Copyright 2023 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.google.android.material.carousel;
|
|
|
|
import static com.google.android.material.carousel.CarouselHelper.createDataSetWithSize;
|
|
import static com.google.android.material.carousel.CarouselHelper.getTestCenteredKeylineState;
|
|
import static com.google.android.material.carousel.CarouselHelper.scrollHorizontallyBy;
|
|
import static com.google.android.material.carousel.CarouselHelper.scrollToPosition;
|
|
import static com.google.android.material.carousel.CarouselHelper.setAdapterItems;
|
|
import static com.google.android.material.carousel.CarouselHelper.setViewSize;
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
|
|
import android.content.Context;
|
|
import androidx.recyclerview.widget.RecyclerView;
|
|
import android.view.View;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.test.core.app.ApplicationProvider;
|
|
import com.google.android.material.carousel.CarouselHelper.CarouselTestAdapter;
|
|
import com.google.android.material.carousel.CarouselHelper.WrappedCarouselLayoutManager;
|
|
import com.google.android.material.carousel.CarouselStrategy.StrategyType;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.robolectric.RobolectricTestRunner;
|
|
|
|
/** Tests for {@link CarouselSnapHelper}. */
|
|
@RunWith(RobolectricTestRunner.class)
|
|
public class CarouselSnapHelperTest {
|
|
RecyclerView recyclerView;
|
|
WrappedCarouselLayoutManager layoutManager;
|
|
CarouselTestAdapter adapter;
|
|
|
|
private static final int DEFAULT_RECYCLER_VIEW_WIDTH = 1320;
|
|
private static final int DEFAULT_RECYCLER_VIEW_HEIGHT = 200;
|
|
private static final int DEFAULT_ITEM_WIDTH = 450;
|
|
private static final int DEFAULT_ITEM_HEIGHT = 200;
|
|
|
|
private final Context context = ApplicationProvider.getApplicationContext();
|
|
|
|
@Before
|
|
public void setUp() throws Throwable {
|
|
recyclerView = new RecyclerView(context);
|
|
setViewSize(recyclerView, DEFAULT_RECYCLER_VIEW_WIDTH, DEFAULT_RECYCLER_VIEW_HEIGHT);
|
|
|
|
layoutManager = new WrappedCarouselLayoutManager();
|
|
adapter = new CarouselTestAdapter(DEFAULT_ITEM_WIDTH, DEFAULT_ITEM_HEIGHT);
|
|
|
|
recyclerView.setLayoutManager(layoutManager);
|
|
recyclerView.setAdapter(adapter);
|
|
layoutManager.setCarouselStrategy(
|
|
new CarouselStrategy() {
|
|
@Override
|
|
public KeylineState onFirstChildMeasuredWithMargins(
|
|
@NonNull Carousel carousel, @NonNull View child) {
|
|
return getTestCenteredKeylineState();
|
|
}
|
|
});
|
|
setAdapterItems(recyclerView, layoutManager, adapter, createDataSetWithSize(10));
|
|
}
|
|
|
|
@Test
|
|
public void testSnap_snapsCorrectView() throws Throwable {
|
|
CarouselSnapHelper snapHelper = new CarouselSnapHelper();
|
|
snapHelper.attachToRecyclerView(recyclerView);
|
|
|
|
// Scroll to position to set the horizontal scroll offset to position 3
|
|
scrollToPosition(recyclerView, layoutManager, 3);
|
|
assertThat(layoutManager.getPosition(snapHelper.findSnapView(layoutManager))).isEqualTo(3);
|
|
|
|
// The snap view should still be the item at position 3 with a small scroll offset
|
|
scrollHorizontallyBy(recyclerView, layoutManager, 50);
|
|
assertThat(layoutManager.getPosition(snapHelper.findSnapView(layoutManager))).isEqualTo(3);
|
|
|
|
// Similarly, the snap view should still be the item at position 3 with a small scroll offset
|
|
scrollHorizontallyBy(recyclerView, layoutManager, -100);
|
|
assertThat(layoutManager.getPosition(snapHelper.findSnapView(layoutManager))).isEqualTo(3);
|
|
|
|
// If scrolled enough, the snap view should be the item at position 4.
|
|
scrollHorizontallyBy(recyclerView, layoutManager, DEFAULT_ITEM_WIDTH);
|
|
assertThat(layoutManager.getPosition(snapHelper.findSnapView(layoutManager))).isEqualTo(4);
|
|
}
|
|
|
|
@Test
|
|
public void testSnap_correctDistance() throws Throwable {
|
|
CarouselSnapHelper snapHelper = new CarouselSnapHelper();
|
|
snapHelper.attachToRecyclerView(recyclerView);
|
|
|
|
// Scroll to position to set the horizontal scroll offset to position 3
|
|
scrollToPosition(recyclerView, layoutManager, 3);
|
|
|
|
int[] distance =
|
|
snapHelper.calculateDistanceToFinalSnap(
|
|
layoutManager, snapHelper.findSnapView(layoutManager));
|
|
assertThat(distance[0]).isEqualTo(0);
|
|
|
|
// The snap view should still be the item at position 3 with a small scroll offset
|
|
scrollHorizontallyBy(recyclerView, layoutManager, 50);
|
|
distance =
|
|
snapHelper.calculateDistanceToFinalSnap(
|
|
layoutManager, snapHelper.findSnapView(layoutManager));
|
|
assertThat(distance[0]).isEqualTo(-50);
|
|
|
|
// Similarly, the snap view should still be the item at position 3 with a small scroll offset
|
|
scrollHorizontallyBy(recyclerView, layoutManager, -100);
|
|
distance =
|
|
snapHelper.calculateDistanceToFinalSnap(
|
|
layoutManager, snapHelper.findSnapView(layoutManager));
|
|
assertThat(distance[0]).isEqualTo(50);
|
|
|
|
int horizontalScrollBefore = layoutManager.scrollOffset;
|
|
// If scrolled enough, the snap view should be the item at position 4.
|
|
// We scrolled by the item width, so the snap distance should still be 50.
|
|
scrollHorizontallyBy(recyclerView, layoutManager, DEFAULT_ITEM_WIDTH);
|
|
int horizontalScrollAfter = layoutManager.scrollOffset;
|
|
|
|
distance =
|
|
snapHelper.calculateDistanceToFinalSnap(
|
|
layoutManager, snapHelper.findSnapView(layoutManager));
|
|
|
|
// When shifting from position 3 -> position 4, the target keyline that we are snapping to
|
|
// will also shift. We must account for this in the snap distance; the snap will be 50 (the
|
|
// original snap distance if keylines stayed the same) minus the difference in focal keyline
|
|
// location between keyline states.
|
|
KeylineStateList stateList =
|
|
KeylineStateList.from(
|
|
layoutManager, getTestCenteredKeylineState(), 0, 0, 0, StrategyType.CONTAINED);
|
|
KeylineState target1 =
|
|
stateList.getShiftedState(
|
|
horizontalScrollBefore,
|
|
layoutManager.minScroll,
|
|
layoutManager.maxScroll,
|
|
true);
|
|
float firstTargetKeylineLoc = target1.getFirstFocalKeyline().loc;
|
|
KeylineState target2 =
|
|
stateList.getShiftedState(
|
|
horizontalScrollAfter,
|
|
layoutManager.minScroll,
|
|
layoutManager.maxScroll,
|
|
true);
|
|
float secondTargetKeylineLoc = target2.getFirstFocalKeyline().loc;
|
|
|
|
assertThat(distance[0]).isEqualTo(50 - (int) (secondTargetKeylineLoc - firstTargetKeylineLoc));
|
|
}
|
|
|
|
@Test
|
|
public void testSnapHelper_consumesFling() throws Throwable {
|
|
CarouselSnapHelper snapHelper = new CarouselSnapHelper();
|
|
snapHelper.attachToRecyclerView(recyclerView);
|
|
|
|
scrollToPosition(recyclerView, layoutManager, 3);
|
|
|
|
// Set horizontal scroll offset to be halfway between position 3 and 4.
|
|
scrollHorizontallyBy(recyclerView, layoutManager, DEFAULT_ITEM_WIDTH / 2);
|
|
|
|
// Velocity is negative, so closest item before keyline is position 3. Actual
|
|
// velocity is not taken re: flinging.
|
|
int position = snapHelper.findTargetSnapPosition(layoutManager, -1000, 0);
|
|
assertThat(position).isEqualTo(3);
|
|
|
|
// Velocity is positive, so closest item after keyline is position 4. Actual
|
|
// velocity is not taken re: flinging.
|
|
position = snapHelper.findTargetSnapPosition(layoutManager, 1000, 0);
|
|
assertThat(position).isEqualTo(4);
|
|
}
|
|
|
|
@Test
|
|
public void testEnablingFling_doesNotConsumeFling() throws Throwable {
|
|
CarouselSnapHelper snapHelper = new CarouselSnapHelper(false);
|
|
snapHelper.attachToRecyclerView(recyclerView);
|
|
|
|
int position = snapHelper.findTargetSnapPosition(layoutManager, -1, 0);
|
|
assertThat(position).isEqualTo(RecyclerView.NO_POSITION);
|
|
}
|
|
}
|