mirror of
https://github.com/material-components/material-components-android.git
synced 2026-02-20 08:39:55 +08:00
Resolves https://github.com/material-components/material-components-android/pull/3849 GIT_ORIGIN_REV_ID=47bb2fcc8031740e5a7d4f741f1ada2af025e126 PiperOrigin-RevId: 632154861
602 lines
22 KiB
Java
602 lines
22 KiB
Java
/*
|
|
* Copyright (C) 2016 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
|
|
*
|
|
* http://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.tabs;
|
|
|
|
import static androidx.test.espresso.Espresso.onView;
|
|
import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
|
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
|
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
|
import static com.google.android.material.testutils.TabLayoutActions.selectTab;
|
|
import static com.google.android.material.testutils.TabLayoutActions.setScrollPosition;
|
|
import static com.google.android.material.testutils.TabLayoutActions.setTabMode;
|
|
import static com.google.android.material.testutils.TestUtilsActions.setLayoutDirection;
|
|
import static org.hamcrest.CoreMatchers.containsString;
|
|
import static org.junit.Assert.assertEquals;
|
|
import static org.junit.Assert.assertFalse;
|
|
import static org.junit.Assert.assertNotNull;
|
|
import static org.junit.Assert.assertNull;
|
|
import static org.junit.Assert.assertTrue;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.ArgumentMatchers.eq;
|
|
import static org.mockito.Mockito.mock;
|
|
import static org.mockito.Mockito.times;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.AnimatorListenerAdapter;
|
|
import android.annotation.TargetApi;
|
|
import android.os.Build;
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
import android.view.InflateException;
|
|
import android.view.LayoutInflater;
|
|
import android.view.MotionEvent;
|
|
import android.view.PointerIcon;
|
|
import android.view.View;
|
|
import androidx.core.view.ViewCompat;
|
|
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
|
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
|
|
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat;
|
|
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat;
|
|
import androidx.test.annotation.UiThreadTest;
|
|
import androidx.test.espresso.Espresso;
|
|
import androidx.test.espresso.IdlingRegistry;
|
|
import androidx.test.espresso.IdlingResource;
|
|
import androidx.test.filters.SdkSuppress;
|
|
import androidx.test.filters.SmallTest;
|
|
import androidx.test.rule.ActivityTestRule;
|
|
import androidx.test.runner.AndroidJUnit4;
|
|
import com.google.android.material.tabs.TabLayout.Tab;
|
|
import com.google.android.material.tabs.TabLayout.TabView;
|
|
import com.google.android.material.testapp.R;
|
|
import com.google.android.material.testutils.AccessibilityUtils;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import org.junit.Rule;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
|
|
@SmallTest
|
|
@RunWith(AndroidJUnit4.class)
|
|
public class TabLayoutTest {
|
|
|
|
@Rule
|
|
public final ActivityTestRule<AppCompatActivity> activityTestRule =
|
|
new ActivityTestRule<>(AppCompatActivity.class);
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
public void testInflateTabLayoutWithTabItems() {
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
final TabLayout tabLayout = (TabLayout) inflater.inflate(R.layout.design_tabs_items, null);
|
|
|
|
assertEquals(3, tabLayout.getTabCount());
|
|
|
|
// Tab 0 has text, but no icon or custom view
|
|
TabLayout.Tab tab = tabLayout.getTabAt(0);
|
|
assertEquals(
|
|
activityTestRule.getActivity().getString(R.string.tab_layout_text),
|
|
tab.getText().toString());
|
|
assertNull(tab.getIcon());
|
|
assertNull(tab.getCustomView());
|
|
|
|
// Tab 1 has an icon, but no text or custom view
|
|
tab = tabLayout.getTabAt(1);
|
|
assertNull(tab.getText());
|
|
assertNotNull(tab.getIcon());
|
|
assertNull(tab.getCustomView());
|
|
|
|
// Tab 2 has a custom view, but no text or icon
|
|
tab = tabLayout.getTabAt(2);
|
|
assertNull(tab.getText());
|
|
assertNull(tab.getIcon());
|
|
assertNotNull(tab.getCustomView());
|
|
assertEquals(R.id.my_custom_tab, tab.getCustomView().getId());
|
|
}
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
public void testInflateTabLayoutWithNonTabItem() throws Throwable {
|
|
try {
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
inflater.inflate(R.layout.design_tabs_with_non_tabitems, null);
|
|
} catch (Throwable throwable) {
|
|
assertTrue(
|
|
throwable instanceof InflateException || throwable instanceof IllegalArgumentException);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
public void testTabWithCustomLayoutSelection1() {
|
|
final TabLayout.OnTabSelectedListener mockListener =
|
|
mock(TabLayout.OnTabSelectedListener.class);
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
|
|
final TabLayout tabLayout = (TabLayout) inflater.inflate(R.layout.design_tabs, null);
|
|
tabLayout.addOnTabSelectedListener(mockListener);
|
|
final TabLayout.Tab tab = tabLayout.newTab();
|
|
tab.setCustomView(R.layout.design_tab_item_custom);
|
|
tabLayout.addTab(tab);
|
|
verify(mockListener, times(1)).onTabSelected(eq(tab));
|
|
verify(mockListener, times(0)).onTabUnselected(any(TabLayout.Tab.class));
|
|
|
|
assertNotNull("Tab has custom view", tab.getCustomView());
|
|
assertEquals("First tab is selected", 0, tabLayout.getSelectedTabPosition());
|
|
assertTabCustomViewSelected(tabLayout);
|
|
}
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
public void testTabWithCustomLayoutSelection2() {
|
|
final TabLayout.OnTabSelectedListener mockListener =
|
|
mock(TabLayout.OnTabSelectedListener.class);
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
|
|
final TabLayout tabLayout = (TabLayout) inflater.inflate(R.layout.design_tabs, null);
|
|
tabLayout.addOnTabSelectedListener(mockListener);
|
|
final TabLayout.Tab tab = tabLayout.newTab();
|
|
tabLayout.addTab(tab);
|
|
verify(mockListener, times(1)).onTabSelected(eq(tab));
|
|
verify(mockListener, times(0)).onTabUnselected(any(TabLayout.Tab.class));
|
|
tab.setCustomView(R.layout.design_tab_item_custom);
|
|
|
|
assertNotNull("Tab has custom view", tab.getCustomView());
|
|
assertEquals("First tab is selected", 0, tabLayout.getSelectedTabPosition());
|
|
assertTabCustomViewSelected(tabLayout);
|
|
}
|
|
|
|
@Test
|
|
public void testTabWithIdIsFound() throws Throwable {
|
|
AppCompatActivity activity = activityTestRule.getActivity();
|
|
int id = ViewCompat.generateViewId();
|
|
activityTestRule.runOnUiThread(
|
|
new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
activity.setContentView(R.layout.design_tabs);
|
|
TabLayout tabs = activity.findViewById(R.id.tabs);
|
|
TabLayout.Tab tab = tabs.newTab().setId(id).setText("test text");
|
|
tabs.addTab(tab);
|
|
}
|
|
});
|
|
|
|
Espresso.onIdle();
|
|
|
|
onView(withId(id)).check(matches(hasDescendant(withText(containsString("test text")))));
|
|
}
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
public void testMultipleTabsWithCustomLayoutSelection1() {
|
|
final TabLayout.OnTabSelectedListener mockListener =
|
|
mock(TabLayout.OnTabSelectedListener.class);
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
final TabLayout tabs = (TabLayout) inflater.inflate(R.layout.design_tabs, null);
|
|
tabs.addOnTabSelectedListener(mockListener);
|
|
|
|
final TabLayout.Tab tab1 = tabs.newTab().setCustomView(R.layout.design_tab_item_custom);
|
|
tabs.addTab(tab1);
|
|
verify(mockListener, times(1)).onTabSelected(eq(tab1));
|
|
verify(mockListener, times(0)).onTabUnselected(any(TabLayout.Tab.class));
|
|
final TabLayout.Tab tab2 = tabs.newTab().setCustomView(R.layout.design_tab_item_custom);
|
|
tabs.addTab(tab2, true);
|
|
verify(mockListener, times(1)).onTabSelected(eq(tab2));
|
|
verify(mockListener, times(1)).onTabUnselected(eq(tab1));
|
|
final TabLayout.Tab tab3 = tabs.newTab().setCustomView(R.layout.design_tab_item_custom);
|
|
tabs.addTab(tab3);
|
|
verifyNoMoreInteractions(mockListener);
|
|
|
|
assertEquals("Second tab is selected", 1, tabs.getSelectedTabPosition());
|
|
assertTabCustomViewSelected(tabs);
|
|
}
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
public void testTabSelectionSelectTab() {
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
final TabLayout tabs = (TabLayout) inflater.inflate(R.layout.design_tabs, null);
|
|
|
|
final TabLayout.Tab tab1 = tabs.newTab();
|
|
tabs.addTab(tab1);
|
|
assertTrue(tab1.isSelected());
|
|
final TabLayout.Tab tab2 = tabs.newTab();
|
|
tabs.addTab(tab2);
|
|
assertFalse(tab2.isSelected());
|
|
final TabLayout.Tab tab3 = tabs.newTab();
|
|
tabs.addTab(tab3);
|
|
assertFalse(tab3.isSelected());
|
|
|
|
tabs.selectTab(tab3);
|
|
assertTrue(tab3.isSelected());
|
|
assertFalse(tab1.isSelected());
|
|
assertFalse(tab2.isSelected());
|
|
assertEquals(2, tabs.indicatorPosition);
|
|
|
|
tabs.selectTab(tab2);
|
|
assertTrue(tab2.isSelected());
|
|
assertFalse(tab1.isSelected());
|
|
assertFalse(tab3.isSelected());
|
|
assertEquals(1, tabs.indicatorPosition);
|
|
|
|
tabs.selectTab(tab1);
|
|
assertTrue(tab1.isSelected());
|
|
assertFalse(tab2.isSelected());
|
|
assertFalse(tab3.isSelected());
|
|
assertEquals(0, tabs.indicatorPosition);
|
|
}
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
public void testTabSelectionAddTab() {
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
final TabLayout tabs = (TabLayout) inflater.inflate(R.layout.design_tabs, null);
|
|
|
|
final TabLayout.Tab tab1 = tabs.newTab();
|
|
tabs.addTab(tab1);
|
|
assertTrue(tab1.isSelected());
|
|
final TabLayout.Tab tab2 = tabs.newTab();
|
|
tabs.addTab(tab2, false);
|
|
assertFalse(tab2.isSelected());
|
|
final TabLayout.Tab tab3 = tabs.newTab();
|
|
tabs.addTab(tab3, true);
|
|
assertTrue(tab3.isSelected());
|
|
final TabLayout.Tab tab4 = tabs.newTab();
|
|
tabs.addTab(tab4);
|
|
assertFalse(tab4.isSelected());
|
|
}
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
public void testTabSelectionRemoveTab() {
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
final TabLayout tabs = (TabLayout) inflater.inflate(R.layout.design_tabs, null);
|
|
|
|
final TabLayout.Tab tab1 = tabs.newTab();
|
|
tabs.addTab(tab1);
|
|
assertTrue(tab1.isSelected());
|
|
final TabLayout.Tab tab2 = tabs.newTab();
|
|
tabs.addTab(tab2);
|
|
assertFalse(tab2.isSelected());
|
|
final TabLayout.Tab tab3 = tabs.newTab();
|
|
tabs.addTab(tab3, true);
|
|
assertTrue(tab3.isSelected());
|
|
|
|
tabs.removeTab(tab3);
|
|
assertTrue(tab2.isSelected());
|
|
|
|
tabs.removeTab(tab1);
|
|
assertTrue(tab2.isSelected());
|
|
}
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
public void testTabSelectionNewTab() {
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
final TabLayout tabs = (TabLayout) inflater.inflate(R.layout.design_tabs, null);
|
|
|
|
final TabLayout.Tab tab1 = tabs.newTab();
|
|
assertFalse(tab1.isSelected());
|
|
tabs.addTab(tab1);
|
|
assertTrue(tab1.isSelected());
|
|
|
|
tabs.addTab(tabs.newTab());
|
|
tabs.addTab(tabs.newTab());
|
|
|
|
tabs.removeAllTabs();
|
|
assertFalse(tabs.newTab().isSelected());
|
|
}
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
public void testMultipleTabsWithCustomLayoutSelection2() {
|
|
final TabLayout.OnTabSelectedListener mockListener =
|
|
mock(TabLayout.OnTabSelectedListener.class);
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
final TabLayout tabs = (TabLayout) inflater.inflate(R.layout.design_tabs, null);
|
|
tabs.addOnTabSelectedListener(mockListener);
|
|
|
|
final TabLayout.Tab tab1 = tabs.newTab();
|
|
tabs.addTab(tab1);
|
|
verify(mockListener, times(1)).onTabSelected(eq(tab1));
|
|
verify(mockListener, times(0)).onTabUnselected(any(TabLayout.Tab.class));
|
|
final TabLayout.Tab tab2 = tabs.newTab();
|
|
tabs.addTab(tab2, true);
|
|
verify(mockListener, times(1)).onTabSelected(eq(tab2));
|
|
verify(mockListener, times(1)).onTabUnselected(eq(tab1));
|
|
final TabLayout.Tab tab3 = tabs.newTab();
|
|
tabs.addTab(tab3);
|
|
verifyNoMoreInteractions(mockListener);
|
|
|
|
tabs.getTabAt(0).setCustomView(R.layout.design_tab_item_custom);
|
|
tabs.getTabAt(1).setCustomView(R.layout.design_tab_item_custom);
|
|
tabs.getTabAt(2).setCustomView(R.layout.design_tab_item_custom);
|
|
|
|
assertEquals("Second tab is selected", 1, tabs.getSelectedTabPosition());
|
|
assertTabCustomViewSelected(tabs);
|
|
}
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
|
|
@TargetApi(Build.VERSION_CODES.N)
|
|
public void testPointerIcon() {
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
final TabLayout tabLayout = (TabLayout) inflater.inflate(R.layout.design_tabs_items, null);
|
|
final PointerIcon expectedIcon =
|
|
PointerIcon.getSystemIcon(activityTestRule.getActivity(), PointerIcon.TYPE_HAND);
|
|
|
|
final int tabCount = tabLayout.getTabCount();
|
|
assertEquals(3, tabCount);
|
|
|
|
final MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
|
|
for (int i = 0; i < tabCount; i++) {
|
|
assertEquals(expectedIcon, tabLayout.getTabAt(i).view.onResolvePointerIcon(event, 0));
|
|
}
|
|
}
|
|
|
|
private static void assertTabCustomViewSelected(final TabLayout tabLayout) {
|
|
for (int i = 0, count = tabLayout.getTabCount(); i < count; i++) {
|
|
final TabLayout.Tab tab = tabLayout.getTabAt(i);
|
|
final View tabCustomView = tab.getCustomView();
|
|
if (tabCustomView != null) {
|
|
assertEquals(tab.isSelected(), tabCustomView.isSelected());
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void setScrollPositionLtr() throws Throwable {
|
|
testSetScrollPosition(true);
|
|
}
|
|
|
|
@Test
|
|
public void setScrollPositionRtl() throws Throwable {
|
|
testSetScrollPosition(false);
|
|
}
|
|
|
|
@Test
|
|
public void testModeAuto() throws Throwable {
|
|
activityTestRule.runOnUiThread(
|
|
() -> activityTestRule.getActivity().setContentView(R.layout.design_tabs_fixed_width));
|
|
final TabLayout tabs = activityTestRule.getActivity().findViewById(R.id.tabs);
|
|
|
|
final TabLayoutScrollIdlingResource idler = new TabLayoutScrollIdlingResource(tabs);
|
|
IdlingRegistry.getInstance().register(idler);
|
|
|
|
onView(withId(R.id.tabs)).perform(setTabMode(TabLayout.MODE_AUTO));
|
|
|
|
// Make sure tabs are scrolled all the way to the start
|
|
onView(withId(R.id.tabs)).perform(selectTab(0));
|
|
|
|
onView(withId(R.id.tabs))
|
|
.check(
|
|
(view, notFoundException) -> {
|
|
if (!(view instanceof TabLayout)) {
|
|
throw notFoundException;
|
|
}
|
|
|
|
TabLayout tabs1 = (TabLayout) view;
|
|
|
|
assertEquals(TabLayout.MODE_AUTO, tabs1.getTabMode());
|
|
int tabWidth = 0;
|
|
for (int i = 0; i < tabs1.getTabCount(); i++) {
|
|
Tab tab = tabs1.getTabAt(i);
|
|
tabWidth += tab.view.getMeasuredWidth();
|
|
}
|
|
|
|
// In MODE_AUTO, the total width of tabs can exceed the width of the parent
|
|
// TabLayout
|
|
assertTrue(tabWidth > tabs1.getMeasuredWidth());
|
|
});
|
|
|
|
// Make sure tabs are scrolled all the way to the end
|
|
onView(withId(R.id.tabs))
|
|
.perform(selectTab(7))
|
|
.check(
|
|
(view, notFoundException) -> {
|
|
if (!(view instanceof TabLayout)) {
|
|
throw notFoundException;
|
|
}
|
|
|
|
assertTrue(view.getScrollX() > view.getMeasuredWidth());
|
|
});
|
|
|
|
IdlingRegistry.getInstance().unregister(idler);
|
|
}
|
|
|
|
@Test
|
|
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
|
|
public void initializesAccessibilityNodeInfo() {
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
final TabLayout tabs = (TabLayout) inflater.inflate(R.layout.design_tabs, null);
|
|
|
|
final TabLayout.Tab tab1 = tabs.newTab();
|
|
tabs.addTab(tab1);
|
|
final TabLayout.Tab tab2 = tabs.newTab();
|
|
tabs.addTab(tab2, true);
|
|
|
|
tabs.getTabAt(0).setCustomView(R.layout.design_tab_item_custom);
|
|
tabs.getTabAt(1).setCustomView(R.layout.design_tab_item_custom);
|
|
|
|
AccessibilityNodeInfoCompat groupInfoCompat = AccessibilityNodeInfoCompat.obtain();
|
|
ViewCompat.onInitializeAccessibilityNodeInfo(tabs, groupInfoCompat);
|
|
|
|
CollectionInfoCompat collectionInfo = groupInfoCompat.getCollectionInfo();
|
|
assertEquals(2, collectionInfo.getColumnCount());
|
|
assertEquals(1, collectionInfo.getRowCount());
|
|
|
|
TabView secondChild = tabs.getTabAt(1).view;
|
|
secondChild.setSelected(true);
|
|
AccessibilityNodeInfoCompat tabInfoCompat = AccessibilityNodeInfoCompat.obtain();
|
|
ViewCompat.onInitializeAccessibilityNodeInfo(secondChild, tabInfoCompat);
|
|
|
|
// A tab that is currently selected won't be clickable
|
|
assertFalse(tabInfoCompat.isClickable());
|
|
assertFalse(
|
|
AccessibilityUtils.hasAction(tabInfoCompat, AccessibilityActionCompat.ACTION_CLICK));
|
|
|
|
CollectionItemInfoCompat itemInfo = tabInfoCompat.getCollectionItemInfo();
|
|
assertEquals(1, itemInfo.getColumnIndex());
|
|
assertEquals(0, itemInfo.getRowIndex());
|
|
assertTrue(itemInfo.isSelected());
|
|
}
|
|
|
|
private void testSetScrollPosition(final boolean isLtr) throws Throwable {
|
|
activityTestRule.runOnUiThread(
|
|
() -> activityTestRule.getActivity().setContentView(R.layout.design_tabs_fixed_width));
|
|
final TabLayout tabs = activityTestRule.getActivity().findViewById(R.id.tabs);
|
|
assertEquals(TabLayout.MODE_SCROLLABLE, tabs.getTabMode());
|
|
|
|
final TabLayoutScrollIdlingResource idler = new TabLayoutScrollIdlingResource(tabs);
|
|
Espresso.registerIdlingResources(idler);
|
|
|
|
// We're going to call setScrollPosition() incrementally, as if scrolling between one tab
|
|
// and the next. Use the middle tab for best results. The positionOffsets should be in the
|
|
// range [0, 1), so the final call will wrap to 0 but use the next tab's position.
|
|
final int middleTab = tabs.getTabCount() / 2;
|
|
final int[] positions = {middleTab, middleTab, middleTab, middleTab, middleTab + 1};
|
|
final float[] positionOffsets = {0f, .25f, .5f, .75f, 0f};
|
|
|
|
// Set layout direction
|
|
onView(withId(R.id.tabs))
|
|
.perform(
|
|
setLayoutDirection(
|
|
isLtr ? ViewCompat.LAYOUT_DIRECTION_LTR : ViewCompat.LAYOUT_DIRECTION_RTL));
|
|
// Make sure it's scrolled all the way to the start
|
|
onView(withId(R.id.tabs)).perform(selectTab(0));
|
|
|
|
// Perform a series of setScrollPosition() calls
|
|
final AtomicInteger lastScrollX = new AtomicInteger(tabs.getScrollX());
|
|
for (int i = 0; i < positions.length; i++) {
|
|
onView(withId(R.id.tabs))
|
|
.perform(setScrollPosition(positions[i], positionOffsets[i]))
|
|
.check(
|
|
(view, notFoundException) -> {
|
|
if (view == null) {
|
|
throw notFoundException;
|
|
}
|
|
// Verify increasing or decreasing scroll X values
|
|
int sx = view.getScrollX();
|
|
assertTrue(isLtr ? sx > lastScrollX.get() : sx < lastScrollX.get());
|
|
lastScrollX.set(sx);
|
|
});
|
|
}
|
|
|
|
Espresso.unregisterIdlingResources(idler);
|
|
}
|
|
|
|
@Test
|
|
@UiThreadTest
|
|
public void testSetCustomTabReplacesCustomView() {
|
|
final LayoutInflater inflater = LayoutInflater.from(activityTestRule.getActivity());
|
|
final TabLayout tabLayout = (TabLayout) inflater.inflate(R.layout.design_tabs, null);
|
|
final TabLayout.Tab tab = tabLayout.newTab();
|
|
tab.setCustomView(R.layout.design_tab_item_custom);
|
|
tabLayout.addTab(tab);
|
|
tab.setCustomView(R.layout.design_tab_item_custom_alternate);
|
|
|
|
assertNull(tabLayout.findViewById(R.id.my_custom_tab));
|
|
assertNotNull(tabLayout.findViewById(R.id.my_custom_alternate_tab));
|
|
}
|
|
|
|
/**
|
|
* Tests that the indicator animation still functions as intended when modifying the animator's
|
|
* update listener, instead of removing/recreating the animator itself.
|
|
*/
|
|
@Test
|
|
public void testIndicatorAnimator_worksAfterReplacingUpdateListener() throws Throwable {
|
|
activityTestRule.runOnUiThread(
|
|
() -> activityTestRule.getActivity().setContentView(R.layout.design_tabs_items));
|
|
final TabLayout tabs = activityTestRule.getActivity().findViewById(R.id.tabs);
|
|
|
|
onView(withId(R.id.tabs)).perform(setTabMode(TabLayout.MODE_FIXED));
|
|
|
|
final TabLayoutScrollIdlingResource idler = new TabLayoutScrollIdlingResource(tabs);
|
|
IdlingRegistry.getInstance().register(idler);
|
|
|
|
// We need to click a tab once to set up the indicator animation (so that it's not still null).
|
|
onView(withId(R.id.tabs)).perform(selectTab(1));
|
|
|
|
// Select new tab. This action should modify the listener on the animator.
|
|
onView(withId(R.id.tabs)).perform(selectTab(2));
|
|
|
|
onView(withId(R.id.tabs))
|
|
.check(
|
|
(view, notFoundException) -> {
|
|
if (view == null) {
|
|
throw notFoundException;
|
|
}
|
|
|
|
TabLayout tabs1 = (TabLayout) view;
|
|
|
|
int tabTwoLeft = tabs1.getTabAt(/* index= */ 2).view.getLeft();
|
|
int tabTwoRight = tabs1.getTabAt(/* index= */ 2).view.getRight();
|
|
|
|
assertEquals(tabs1.tabSelectedIndicator.getBounds().left, tabTwoLeft);
|
|
assertEquals(tabs1.tabSelectedIndicator.getBounds().right, tabTwoRight);
|
|
assertEquals(2, tabs.indicatorPosition);
|
|
});
|
|
|
|
IdlingRegistry.getInstance().unregister(idler);
|
|
}
|
|
|
|
static class TabLayoutScrollIdlingResource implements IdlingResource {
|
|
|
|
private boolean isIdle = true;
|
|
private ResourceCallback callback;
|
|
|
|
TabLayoutScrollIdlingResource(final TabLayout tabLayout) {
|
|
tabLayout.setScrollAnimatorListener(
|
|
new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationStart(Animator animator) {
|
|
setIdle(false);
|
|
}
|
|
|
|
@Override
|
|
public void onAnimationEnd(Animator animator) {
|
|
setIdle(true);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public String getName() {
|
|
return "TabLayoutScrollIdlingResource";
|
|
}
|
|
|
|
@Override
|
|
public boolean isIdleNow() {
|
|
return isIdle;
|
|
}
|
|
|
|
@Override
|
|
public void registerIdleTransitionCallback(ResourceCallback callback) {
|
|
this.callback = callback;
|
|
}
|
|
|
|
private void setIdle(boolean idle) {
|
|
boolean wasIdle = isIdle;
|
|
isIdle = idle;
|
|
if (isIdle && !wasIdle && callback != null) {
|
|
callback.onTransitionToIdle();
|
|
}
|
|
}
|
|
}
|
|
}
|