diff --git a/app/src/main/java/net/kdt/pojavlaunch/launcheruiv3/VerticalTabLayout.java.z b/app/src/main/java/net/kdt/pojavlaunch/launcheruiv3/VerticalTabLayout.java.z deleted file mode 100644 index 0e11a8299..000000000 --- a/app/src/main/java/net/kdt/pojavlaunch/launcheruiv3/VerticalTabLayout.java.z +++ /dev/null @@ -1,1631 +0,0 @@ -/* - * Copyright (C) 2015 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 net.kdt.pojavlaunch.launcheruiv3; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.support.annotation.ColorInt; -import android.support.annotation.DrawableRes; -import android.support.annotation.IntDef; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.StringRes; -import android.support.design.R; -import android.support.v4.math.MathUtils; -import android.support.v4.view.GravityCompat; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewCompat; -import android.support.v4.view.ViewPager; -import android.support.v7.app.ActionBar; -import android.support.v7.internal.widget.TintManager; -import android.support.v7.widget.ViewUtils; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; -import android.widget.ScrollView; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Iterator; - -import static android.support.v4.view.ViewPager.SCROLL_STATE_DRAGGING; -import static android.support.v4.view.ViewPager.SCROLL_STATE_IDLE; -import static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING; - -/** - * VerticalTabLayout provides a horizontal layout to display tabs. - * - *
Population of the tabs to display is - * done through {@link Tab} instances. You create tabs via {@link #newTab()}. From there you can - * change the tab's label or icon via {@link Tab#setText(int)} and {@link Tab#setIcon(int)} - * respectively. To display the tab, you need to add it to the layout via one of the - * {@link #addTab(Tab)} methods. For example: - *
- * VerticalTabLayout VerticalTabLayout = ...; - * VerticalTabLayout.addTab(VerticalTabLayout.newTab().setText("Tab 1")); - * VerticalTabLayout.addTab(VerticalTabLayout.newTab().setText("Tab 2")); - * VerticalTabLayout.addTab(VerticalTabLayout.newTab().setText("Tab 3")); - *- * You should set a listener via {@link #setOnTabSelectedListener(OnTabSelectedListener)} to be - * notified when any tab's selection state has been changed. - *
- * If you're using a {@link android.support.v4.view.ViewPager} together - * with this layout, you can use {@link #setTabsFromPagerAdapter(PagerAdapter)} which will populate - * the tabs using the given {@link PagerAdapter}'s page titles. You should also use a - * {@link VerticalTabLayoutOnPageChangeListener} to forward the scroll and selection changes to this - * layout like so: - *
- * ViewPager viewPager = ...; - * VerticalTabLayout VerticalTabLayout = ...; - * viewPager.addOnPageChangeListener(new VerticalTabLayoutOnPageChangeListener(VerticalTabLayout)); - *- * - * @see Tabs - */ -public class VerticalTabLayout extends ScrollView { - - private static final int MAX_TAB_TEXT_LINES = 2; - - private static final int DEFAULT_HEIGHT = 48; // dps - private static final int TAB_MIN_WIDTH_MARGIN = 56; //dps - private static final int FIXED_WRAP_GUTTER_MIN = 16; //dps - private static final int MOTION_NON_ADJACENT_OFFSET = 24; - - private static final int ANIMATION_DURATION = 300; - - /** - * Scrollable tabs display a subset of tabs at any given moment, and can contain longer tab - * labels and a larger number of tabs. They are best used for browsing contexts in touch - * interfaces when users don’t need to directly compare the tab labels. - * - * @see #setTabMode(int) - * @see #getTabMode() - */ - public static final int MODE_SCROLLABLE = 0; - - /** - * Fixed tabs display all tabs concurrently and are best used with content that benefits from - * quick pivots between tabs. The maximum number of tabs is limited by the view’s width. - * Fixed tabs have equal width, based on the widest tab label. - * - * @see #setTabMode(int) - * @see #getTabMode() - */ - public static final int MODE_FIXED = 1; - - /** - * @hide - */ - @IntDef(value = {MODE_SCROLLABLE, MODE_FIXED}) - @Retention(RetentionPolicy.SOURCE) - public @interface Mode {} - - /** - * Gravity used to fill the {@link VerticalTabLayout} as much as possible. This option only takes effect - * when used with {@link #MODE_FIXED}. - * - * @see #setTabGravity(int) - * @see #getTabGravity() - */ - public static final int GRAVITY_FILL = 0; - - /** - * Gravity used to lay out the tabs in the center of the {@link VerticalTabLayout}. - * - * @see #setTabGravity(int) - * @see #getTabGravity() - */ - public static final int GRAVITY_CENTER = 1; - - /** - * @hide - */ - @IntDef(flag = true, value = {GRAVITY_FILL, GRAVITY_CENTER}) - @Retention(RetentionPolicy.SOURCE) - public @interface TabGravity {} - - /** - * Callback interface invoked when a tab's selection state changes. - */ - public interface OnTabSelectedListener { - - /** - * Called when a tab enters the selected state. - * - * @param tab The tab that was selected - */ - public void onTabSelected(Tab tab); - - /** - * Called when a tab exits the selected state. - * - * @param tab The tab that was unselected - */ - public void onTabUnselected(Tab tab); - - /** - * Called when a tab that is already selected is chosen again by the user. Some applications - * may use this action to return to the top level of a category. - * - * @param tab The tab that was reselected. - */ - public void onTabReselected(Tab tab); - } - - private final ArrayList
- * Calling this method does not update the selected tab, it is only used for drawing purposes.
- *
- * @param position current scroll position
- * @param positionOffset Value from [0, 1) indicating the offset from {@code position}.
- * @param updateSelectedText Whether to update the text's selected state.
- */
- public void setScrollPosition(int position, float positionOffset, boolean updateSelectedText) {
- if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) {
- return;
- }
- if (position < 0 || position >= mTabStrip.getChildCount()) {
- return;
- }
-
- // Set the indicator position and update the scroll to match
- mTabStrip.setIndicatorPositionFromTabPosition(position, positionOffset);
- scrollTo(calculateScrollXForTab(position, positionOffset), 0);
-
- // Update the 'selected state' view as we scroll
- if (updateSelectedText) {
- setSelectedTabView(Math.round(position + positionOffset));
- }
- }
-
- /**
- * Add a tab to this layout. The tab will be added at the end of the list.
- * If this is the first tab to be added it will become the selected tab.
- *
- * @param tab Tab to add
- */
- public void addTab(@NonNull Tab tab) {
- addTab(tab, mTabs.isEmpty());
- }
-
- /**
- * Add a tab to this layout. The tab will be inserted at This method will:
- * position
.
- * If this is the first tab to be added it will become the selected tab.
- *
- * @param tab The tab to add
- * @param position The new position of the tab
- */
- public void addTab(@NonNull Tab tab, int position) {
- addTab(tab, position, mTabs.isEmpty());
- }
-
- /**
- * Add a tab to this layout. The tab will be added at the end of the list.
- *
- * @param tab Tab to add
- * @param setSelected True if the added tab should become the selected tab.
- */
- public void addTab(@NonNull Tab tab, boolean setSelected) {
- if (tab.mParent != this) {
- throw new IllegalArgumentException("Tab belongs to a different VerticalTabLayout.");
- }
-
- addTabView(tab, setSelected);
- configureTab(tab, mTabs.size());
- if (setSelected) {
- tab.select();
- }
- }
-
- /**
- * Add a tab to this layout. The tab will be inserted at position
.
- *
- * @param tab The tab to add
- * @param position The new position of the tab
- * @param setSelected True if the added tab should become the selected tab.
- */
- public void addTab(@NonNull Tab tab, int position, boolean setSelected) {
- if (tab.mParent != this) {
- throw new IllegalArgumentException("Tab belongs to a different VerticalTabLayout.");
- }
-
- addTabView(tab, position, setSelected);
- configureTab(tab, position);
- if (setSelected) {
- tab.select();
- }
- }
-
- /**
- * Set the {@link android.support.design.widget.VerticalTabLayout.OnTabSelectedListener} that will
- * handle switching to and from tabs.
- *
- * @param onTabSelectedListener Listener to handle tab selection events
- */
- public void setOnTabSelectedListener(OnTabSelectedListener onTabSelectedListener) {
- mOnTabSelectedListener = onTabSelectedListener;
- }
-
- /**
- * Create and return a new {@link Tab}. You need to manually add this using
- * {@link #addTab(Tab)} or a related method.
- *
- * @return A new Tab
- * @see #addTab(Tab)
- */
- @NonNull
- public Tab newTab() {
- return new Tab(this);
- }
-
- /**
- * Returns the number of tabs currently registered with the action bar.
- *
- * @return Tab count
- */
- public int getTabCount() {
- return mTabs.size();
- }
-
- /**
- * Returns the tab at the specified index.
- */
- @Nullable
- public Tab getTabAt(int index) {
- return mTabs.get(index);
- }
-
- /**
- * Returns the position of the current selected tab.
- *
- * @return selected tab position, or {@code -1} if there isn't a selected tab.
- */
- public int getSelectedTabPosition() {
- return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
- }
-
- /**
- * Remove a tab from the layout. If the removed tab was selected it will be deselected
- * and another tab will be selected if present.
- *
- * @param tab The tab to remove
- */
- public void removeTab(Tab tab) {
- if (tab.mParent != this) {
- throw new IllegalArgumentException("Tab does not belong to this VerticalTabLayout.");
- }
-
- removeTabAt(tab.getPosition());
- }
-
- /**
- * Remove a tab from the layout. If the removed tab was selected it will be deselected
- * and another tab will be selected if present.
- *
- * @param position Position of the tab to remove
- */
- public void removeTabAt(int position) {
- final int selectedTabPosition = mSelectedTab != null ? mSelectedTab.getPosition() : 0;
- removeTabViewAt(position);
-
- Tab removedTab = mTabs.remove(position);
- if (removedTab != null) {
- removedTab.setPosition(Tab.INVALID_POSITION);
- }
-
- final int newTabCount = mTabs.size();
- for (int i = position; i < newTabCount; i++) {
- mTabs.get(i).setPosition(i);
- }
-
- if (selectedTabPosition == position) {
- selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
- }
- }
-
- /**
- * Remove all tabs from the action bar and deselect the current tab.
- */
- public void removeAllTabs() {
- // Remove all the views
- mTabStrip.removeAllViews();
-
- for (Iterator
- *
- *
- * @param mode one of {@link #MODE_FIXED} or {@link #MODE_SCROLLABLE}.
- */
- public void setTabMode(@Mode int mode) {
- if (mode != mMode) {
- mMode = mode;
- applyModeAndGravity();
- }
- }
-
- /**
- * Returns the current mode used by this {@link VerticalTabLayout}.
- *
- * @see #setTabMode(int)
- */
- @Mode
- public int getTabMode() {
- return mMode;
- }
-
- /**
- * Set the gravity to use when laying out the tabs.
- *
- * @param gravity one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}.
- */
- public void setTabGravity(@TabGravity int gravity) {
- if (mTabGravity != gravity) {
- mTabGravity = gravity;
- applyModeAndGravity();
- }
- }
-
- /**
- * The current gravity used for laying out tabs.
- *
- * @return one of {@link #GRAVITY_CENTER} or {@link #GRAVITY_FILL}.
- */
- @TabGravity
- public int getTabGravity() {
- return mTabGravity;
- }
-
- /**
- * Sets the text colors for the different states (normal, selected) used for the tabs.
- */
- public void setTabTextColors(@Nullable ColorStateList textColor) {
- if (mTabTextColors != textColor) {
- mTabTextColors = textColor;
- updateAllTabs();
- }
- }
-
- /**
- * Gets the text colors for the different states (normal, selected) used for the tabs.
- */
- @Nullable
- public ColorStateList getTabTextColors() {
- return mTabTextColors;
- }
-
- /**
- * Sets the text colors for the different states (normal, selected) used for the tabs.
- */
- public void setTabTextColors(int normalColor, int selectedColor) {
- setTabTextColors(createColorStateList(normalColor, selectedColor));
- }
-
- /**
- * The one-stop shop for setting up this {@link VerticalTabLayout} with a {@link ViewPager}.
- *
- *
- *
- *
- * Any existing tabs will be removed first. Each tab will have it's text set to the value - * returned from {@link PagerAdapter#getPageTitle(int)} - *
- * - * @param adapter the adapter to populate from - */ - public void setTabsFromPagerAdapter(@NonNull PagerAdapter adapter) { - removeAllTabs(); - for (int i = 0, count = adapter.getCount(); i < count; i++) { - addTab(newTab().setText(adapter.getPageTitle(i))); - } - } - - private void updateAllTabs() { - for (int i = 0, z = mTabStrip.getChildCount(); i < z; i++) { - updateTab(i); - } - } - - private TabView createTabView(Tab tab) { - final TabView tabView = new TabView(getContext(), tab); - tabView.setFocusable(true); - - if (mTabClickListener == null) { - mTabClickListener = new View.OnClickListener() { - @Override - public void onClick(View view) { - TabView tabView = (TabView) view; - tabView.getTab().select(); - } - }; - } - tabView.setOnClickListener(mTabClickListener); - return tabView; - } - - private void configureTab(Tab tab, int position) { - tab.setPosition(position); - mTabs.add(position, tab); - - final int count = mTabs.size(); - for (int i = position + 1; i < count; i++) { - mTabs.get(i).setPosition(i); - } - } - - private void updateTab(int position) { - final TabView view = (TabView) mTabStrip.getChildAt(position); - if (view != null) { - view.update(); - } - } - - private void addTabView(Tab tab, boolean setSelected) { - final TabView tabView = createTabView(tab); - mTabStrip.addView(tabView, createLayoutParamsForTabs()); - if (setSelected) { - tabView.setSelected(true); - } - } - - private void addTabView(Tab tab, int position, boolean setSelected) { - final TabView tabView = createTabView(tab); - mTabStrip.addView(tabView, position, createLayoutParamsForTabs()); - if (setSelected) { - tabView.setSelected(true); - } - } - - private LinearLayout.LayoutParams createLayoutParamsForTabs() { - final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - updateTabViewLayoutParams(lp); - return lp; - } - - private void updateTabViewLayoutParams(LinearLayout.LayoutParams lp) { - if (mMode == MODE_FIXED && mTabGravity == GRAVITY_FILL) { - lp.width = 0; - lp.weight = 1; - } else { - lp.width = LinearLayout.LayoutParams.WRAP_CONTENT; - lp.weight = 0; - } - } - - private int dpToPx(int dps) { - return Math.round(getResources().getDisplayMetrics().density * dps); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // If we have a MeasureSpec which allows us to decide our height, try and use the default - // height - final int idealHeight = dpToPx(DEFAULT_HEIGHT) + getPaddingTop() + getPaddingBottom(); - switch (MeasureSpec.getMode(heightMeasureSpec)) { - case MeasureSpec.AT_MOST: - heightMeasureSpec = MeasureSpec.makeMeasureSpec( - Math.min(idealHeight, MeasureSpec.getSize(heightMeasureSpec)), - MeasureSpec.EXACTLY); - break; - case MeasureSpec.UNSPECIFIED: - heightMeasureSpec = MeasureSpec.makeMeasureSpec(idealHeight, MeasureSpec.EXACTLY); - break; - } - - // Now super measure itself using the (possibly) modified height spec - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - if (mMode == MODE_FIXED && getChildCount() == 1) { - // If we're in fixed mode then we need to make the tab strip is the same width as us - // so we don't scroll - final View child = getChildAt(0); - final int width = getMeasuredWidth(); - - if (child.getMeasuredWidth() > width) { - // If the child is wider than us, re-measure it with a widthSpec set to exact our - // measure width - int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTop() - + getPaddingBottom(), child.getLayoutParams().height); - int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - } - - // Now update the tab max width. We do it here as the default tab min width is - // layout width - 56dp - int maxTabWidth = mRequestedTabMaxWidth; - final int defaultTabMaxWidth = getMeasuredWidth() - dpToPx(TAB_MIN_WIDTH_MARGIN); - if (maxTabWidth == 0 || maxTabWidth > defaultTabMaxWidth) { - // If the request tab max width is 0, or larger than our default, use the default - maxTabWidth = defaultTabMaxWidth; - } - - if (mTabMaxWidth != maxTabWidth) { - // If the tab max width has changed, re-measure - mTabMaxWidth = maxTabWidth; - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } - - private void removeTabViewAt(int position) { - mTabStrip.removeViewAt(position); - requestLayout(); - } - - private void animateToTab(int newPosition) { - if (newPosition == Tab.INVALID_POSITION) { - return; - } - - if (getWindowToken() == null || !ViewCompat.isLaidOut(this) - || mTabStrip.childrenNeedLayout()) { - // If we don't have a window token, or we haven't been laid out yet just draw the new - // position now - setScrollPosition(newPosition, 0f, true); - return; - } - - final int startScrollX = getScrollX(); - final int targetScrollX = calculateScrollXForTab(newPosition, 0); - - if (startScrollX != targetScrollX) { - if (mScrollAnimator == null) { - mScrollAnimator = ViewUtils.createAnimator(); - mScrollAnimator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR); - mScrollAnimator.setDuration(ANIMATION_DURATION); - mScrollAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimatorCompat animator) { - scrollTo(animator.getAnimatedIntValue(), 0); - } - }); - } - - mScrollAnimator.setIntValues(startScrollX, targetScrollX); - mScrollAnimator.start(); - } - - // Now animate the indicator - mTabStrip.animateIndicatorToPosition(newPosition, ANIMATION_DURATION); - } - - private void setSelectedTabView(int position) { - final int tabCount = mTabStrip.getChildCount(); - for (int i = 0; i < tabCount; i++) { - final View child = mTabStrip.getChildAt(i); - child.setSelected(i == position); - } - } - - void selectTab(Tab tab) { - selectTab(tab, true); - } - - void selectTab(Tab tab, boolean updateIndicator) { - if (mSelectedTab == tab) { - if (mSelectedTab != null) { - if (mOnTabSelectedListener != null) { - mOnTabSelectedListener.onTabReselected(mSelectedTab); - } - animateToTab(tab.getPosition()); - } - } else { - final int newPosition = tab != null ? tab.getPosition() : Tab.INVALID_POSITION; - setSelectedTabView(newPosition); - if (updateIndicator) { - if ((mSelectedTab == null || mSelectedTab.getPosition() == Tab.INVALID_POSITION) - && newPosition != Tab.INVALID_POSITION) { - // If we don't currently have a tab, just draw the indicator - setScrollPosition(newPosition, 0f, true); - } else { - animateToTab(newPosition); - } - } - if (mSelectedTab != null && mOnTabSelectedListener != null) { - mOnTabSelectedListener.onTabUnselected(mSelectedTab); - } - mSelectedTab = tab; - if (mSelectedTab != null && mOnTabSelectedListener != null) { - mOnTabSelectedListener.onTabSelected(mSelectedTab); - } - } - } - - private int calculateScrollXForTab(int position, float positionOffset) { - if (mMode == MODE_SCROLLABLE) { - final View selectedChild = mTabStrip.getChildAt(position); - final View nextChild = position + 1 < mTabStrip.getChildCount() - ? mTabStrip.getChildAt(position + 1) - : null; - final int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0; - final int nextWidth = nextChild != null ? nextChild.getWidth() : 0; - - return selectedChild.getLeft() - + ((int) ((selectedWidth + nextWidth) * positionOffset * 0.5f)) - + (selectedChild.getWidth() / 2) - - (getWidth() / 2); - } - return 0; - } - - private void applyModeAndGravity() { - int paddingStart = 0; - if (mMode == MODE_SCROLLABLE) { - // If we're scrollable, or fixed at start, inset using padding - paddingStart = Math.max(0, mContentInsetStart - mTabPaddingStart); - } - ViewCompat.setPaddingRelative(mTabStrip, paddingStart, 0, 0, 0); - - switch (mMode) { - case MODE_FIXED: - mTabStrip.setGravity(Gravity.CENTER_HORIZONTAL); - break; - case MODE_SCROLLABLE: - mTabStrip.setGravity(GravityCompat.START); - break; - } - - updateTabViewsLayoutParams(); - } - - private void updateTabViewsLayoutParams() { - for (int i = 0; i < mTabStrip.getChildCount(); i++) { - View child = mTabStrip.getChildAt(i); - updateTabViewLayoutParams((LinearLayout.LayoutParams) child.getLayoutParams()); - child.requestLayout(); - } - } - - /** - * A tab in this layout. Instances can be created via {@link #newTab()}. - */ - public static final class Tab { - - /** - * An invalid position for a tab. - * - * @see #getPosition() - */ - public static final int INVALID_POSITION = -1; - - private Object mTag; - private Drawable mIcon; - private CharSequence mText; - private CharSequence mContentDesc; - private int mPosition = INVALID_POSITION; - private View mCustomView; - - private final VerticalTabLayout mParent; - - Tab(VerticalTabLayout parent) { - mParent = parent; - } - - /** - * @return This Tab's tag object. - */ - @Nullable - public Object getTag() { - return mTag; - } - - /** - * Give this Tab an arbitrary object to hold for later use. - * - * @param tag Object to store - * @return The current instance for call chaining - */ - @NonNull - public Tab setTag(@Nullable Object tag) { - mTag = tag; - return this; - } - - - /** - * Returns the custom view used for this tab. - * - * @see #setCustomView(View) - * @see #setCustomView(int) - */ - @Nullable - public View getCustomView() { - return mCustomView; - } - - /** - * Set a custom view to be used for this tab. - *- * If the provided view contains a {@link TextView} with an ID of - * {@link android.R.id#text1} then that will be updated with the value given - * to {@link #setText(CharSequence)}. Similarly, if this layout contains an - * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with - * the value given to {@link #setIcon(Drawable)}. - *
- * - * @param view Custom view to be used as a tab. - * @return The current instance for call chaining - */ - @NonNull - public Tab setCustomView(@Nullable View view) { - mCustomView = view; - if (mPosition >= 0) { - mParent.updateTab(mPosition); - } - return this; - } - - /** - * Set a custom view to be used for this tab. - *- * If the inflated layout contains a {@link TextView} with an ID of - * {@link android.R.id#text1} then that will be updated with the value given - * to {@link #setText(CharSequence)}. Similarly, if this layout contains an - * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with - * the value given to {@link #setIcon(Drawable)}. - *
- * - * @param layoutResId A layout resource to inflate and use as a custom tab view - * @return The current instance for call chaining - */ - @NonNull - public Tab setCustomView(@LayoutRes int layoutResId) { - return setCustomView( - LayoutInflater.from(mParent.getContext()).inflate(layoutResId, null)); - } - - /** - * Return the icon associated with this tab. - * - * @return The tab's icon - */ - @Nullable - public Drawable getIcon() { - return mIcon; - } - - /** - * Return the current position of this tab in the action bar. - * - * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in - * the action bar. - */ - public int getPosition() { - return mPosition; - } - - void setPosition(int position) { - mPosition = position; - } - - /** - * Return the text of this tab. - * - * @return The tab's text - */ - @Nullable - public CharSequence getText() { - return mText; - } - - /** - * Set the icon displayed on this tab. - * - * @param icon The drawable to use as an icon - * @return The current instance for call chaining - */ - @NonNull - public Tab setIcon(@Nullable Drawable icon) { - mIcon = icon; - if (mPosition >= 0) { - mParent.updateTab(mPosition); - } - return this; - } - - /** - * Set the icon displayed on this tab. - * - * @param resId A resource ID referring to the icon that should be displayed - * @return The current instance for call chaining - */ - @NonNull - public Tab setIcon(@DrawableRes int resId) { - return setIcon(TintManager.getDrawable(mParent.getContext(), resId)); - } - - /** - * Set the text displayed on this tab. Text may be truncated if there is not room to display - * the entire string. - * - * @param text The text to display - * @return The current instance for call chaining - */ - @NonNull - public Tab setText(@Nullable CharSequence text) { - mText = text; - if (mPosition >= 0) { - mParent.updateTab(mPosition); - } - return this; - } - - /** - * Set the text displayed on this tab. Text may be truncated if there is not room to display - * the entire string. - * - * @param resId A resource ID referring to the text that should be displayed - * @return The current instance for call chaining - */ - @NonNull - public Tab setText(@StringRes int resId) { - return setText(mParent.getResources().getText(resId)); - } - - /** - * Select this tab. Only valid if the tab has been added to the action bar. - */ - public void select() { - mParent.selectTab(this); - } - - /** - * Returns true if this tab is currently selected. - */ - public boolean isSelected() { - return mParent.getSelectedTabPosition() == mPosition; - } - - /** - * Set a description of this tab's content for use in accessibility support. If no content - * description is provided the title will be used. - * - * @param resId A resource ID referring to the description text - * @return The current instance for call chaining - * @see #setContentDescription(CharSequence) - * @see #getContentDescription() - */ - @NonNull - public Tab setContentDescription(@StringRes int resId) { - return setContentDescription(mParent.getResources().getText(resId)); - } - - /** - * Set a description of this tab's content for use in accessibility support. If no content - * description is provided the title will be used. - * - * @param contentDesc Description of this tab's content - * @return The current instance for call chaining - * @see #setContentDescription(int) - * @see #getContentDescription() - */ - @NonNull - public Tab setContentDescription(@Nullable CharSequence contentDesc) { - mContentDesc = contentDesc; - if (mPosition >= 0) { - mParent.updateTab(mPosition); - } - return this; - } - - /** - * Gets a brief description of this tab's content for use in accessibility support. - * - * @return Description of this tab's content - * @see #setContentDescription(CharSequence) - * @see #setContentDescription(int) - */ - @Nullable - public CharSequence getContentDescription() { - return mContentDesc; - } - } - - class TabView extends LinearLayout implements OnLongClickListener { - private final Tab mTab; - private TextView mTextView; - private ImageView mIconView; - - private View mCustomView; - private TextView mCustomTextView; - private ImageView mCustomIconView; - - public TabView(Context context, Tab tab) { - super(context); - mTab = tab; - if (mTabBackgroundResId != 0) { - setBackgroundDrawable(TintManager.getDrawable(context, mTabBackgroundResId)); - } - ViewCompat.setPaddingRelative(this, mTabPaddingStart, mTabPaddingTop, - mTabPaddingEnd, mTabPaddingBottom); - setGravity(Gravity.CENTER); - update(); - } - - @Override - public void setSelected(boolean selected) { - final boolean changed = (isSelected() != selected); - super.setSelected(selected); - if (changed && selected) { - sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); - - if (mTextView != null) { - mTextView.setSelected(selected); - } - if (mIconView != null) { - mIconView.setSelected(selected); - } - } - } - - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) - @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); - // This view masquerades as an action bar tab. - event.setClassName(ActionBar.Tab.class.getName()); - } - - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - // This view masquerades as an action bar tab. - info.setClassName(ActionBar.Tab.class.getName()); - } - - @Override - public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - final int measuredWidth = getMeasuredWidth(); - if (measuredWidth < mTabMinWidth || measuredWidth > mTabMaxWidth) { - // Re-measure if we are outside our min or max width - widthMeasureSpec = MeasureSpec.makeMeasureSpec( - MathUtils.constrain(measuredWidth, mTabMinWidth, mTabMaxWidth), - MeasureSpec.EXACTLY); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } - - final void update() { - final Tab tab = mTab; - final View custom = tab.getCustomView(); - if (custom != null) { - final ViewParent customParent = custom.getParent(); - if (customParent != this) { - if (customParent != null) { - ((ViewGroup) customParent).removeView(custom); - } - addView(custom); - } - mCustomView = custom; - if (mTextView != null) { - mTextView.setVisibility(GONE); - } - if (mIconView != null) { - mIconView.setVisibility(GONE); - mIconView.setImageDrawable(null); - } - - mCustomTextView = (TextView) custom.findViewById(android.R.id.text1); - mCustomIconView = (ImageView) custom.findViewById(android.R.id.icon); - } else { - // We do not have a custom view. Remove one if it already exists - if (mCustomView != null) { - removeView(mCustomView); - mCustomView = null; - } - mCustomTextView = null; - mCustomIconView = null; - } - - if (mCustomView == null) { - // If there isn't a custom view, we'll us our own in-built layouts - if (mIconView == null) { - ImageView iconView = (ImageView) LayoutInflater.from(getContext()) - .inflate(R.layout.design_layout_tab_icon, this, false); - addView(iconView, 0); - mIconView = iconView; - } - if (mTextView == null) { - TextView textView = (TextView) LayoutInflater.from(getContext()) - .inflate(R.layout.design_layout_tab_text, this, false); - addView(textView); - mTextView = textView; - } - mTextView.setTextAppearance(getContext(), mTabTextAppearance); - if (mTabTextColors != null) { - mTextView.setTextColor(mTabTextColors); - } - updateTextAndIcon(tab, mTextView, mIconView); - } else { - // Else, we'll see if there is a TextView or ImageView present and update them - if (mCustomTextView != null || mCustomIconView != null) { - updateTextAndIcon(tab, mCustomTextView, mCustomIconView); - } - } - } - - private void updateTextAndIcon(Tab tab, TextView textView, ImageView iconView) { - final Drawable icon = tab.getIcon(); - final CharSequence text = tab.getText(); - - if (iconView != null) { - if (icon != null) { - iconView.setImageDrawable(icon); - iconView.setVisibility(VISIBLE); - setVisibility(VISIBLE); - } else { - iconView.setVisibility(GONE); - iconView.setImageDrawable(null); - } - iconView.setContentDescription(tab.getContentDescription()); - } - - final boolean hasText = !TextUtils.isEmpty(text); - if (textView != null) { - if (hasText) { - textView.setText(text); - textView.setContentDescription(tab.getContentDescription()); - textView.setVisibility(VISIBLE); - setVisibility(VISIBLE); - } else { - textView.setVisibility(GONE); - textView.setText(null); - } - } - - if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) { - setOnLongClickListener(this); - } else { - setOnLongClickListener(null); - setLongClickable(false); - } - } - - @Override - public boolean onLongClick(View v) { - final int[] screenPos = new int[2]; - getLocationOnScreen(screenPos); - - final Context context = getContext(); - final int width = getWidth(); - final int height = getHeight(); - final int screenWidth = context.getResources().getDisplayMetrics().widthPixels; - - Toast cheatSheet = Toast.makeText(context, mTab.getContentDescription(), - Toast.LENGTH_SHORT); - // Show under the tab - cheatSheet.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, - (screenPos[0] + width / 2) - screenWidth / 2, height); - - cheatSheet.show(); - return true; - } - - public Tab getTab() { - return mTab; - } - } - - private class SlidingTabStrip extends LinearLayout { - private int mSelectedIndicatorHeight; - private final Paint mSelectedIndicatorPaint; - - private int mSelectedPosition = -1; - private float mSelectionOffset; - - private int mIndicatorLeft = -1; - private int mIndicatorRight = -1; - - SlidingTabStrip(Context context) { - super(context); - setWillNotDraw(false); - mSelectedIndicatorPaint = new Paint(); - } - - void setSelectedIndicatorColor(int color) { - mSelectedIndicatorPaint.setColor(color); - ViewCompat.postInvalidateOnAnimation(this); - } - - void setSelectedIndicatorHeight(int height) { - mSelectedIndicatorHeight = height; - ViewCompat.postInvalidateOnAnimation(this); - } - - boolean childrenNeedLayout() { - for (int i = 0, z = getChildCount(); i < z; i++) { - final View child = getChildAt(i); - if (child.getWidth() <= 0) { - return true; - } - } - return false; - } - - void setIndicatorPositionFromTabPosition(int position, float positionOffset) { - if (mIndicatorAnimator != null && mIndicatorAnimator.isRunning()) { - return; - } - mSelectedPosition = position; - mSelectionOffset = positionOffset; - updateIndicatorPosition(); - } - - @Override - protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) { - // HorizontalScrollView will first measure use with UNSPECIFIED, and then with - // EXACTLY. Ignore the first call since anything we do will be overwritten anyway - return; - } - - if (mMode == MODE_FIXED && mTabGravity == GRAVITY_CENTER) { - final int count = getChildCount(); - - final int unspecifiedSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - - // First we'll find the largest tab - int largestTabWidth = 0; - for (int i = 0, z = count; i < z; i++) { - final View child = getChildAt(i); - child.measure(unspecifiedSpec, heightMeasureSpec); - largestTabWidth = Math.max(largestTabWidth, child.getMeasuredWidth()); - } - - if (largestTabWidth <= 0) { - // If we don't have a largest child yet, skip until the next measure pass - return; - } - - final int gutter = dpToPx(FIXED_WRAP_GUTTER_MIN); - if (largestTabWidth * count <= getMeasuredWidth() - gutter * 2) { - // If the tabs fit within our width minus gutters, we will set all tabs to have - // the same width - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - final LinearLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); - lp.width = largestTabWidth; - lp.weight = 0; - } - } else { - // If the tabs will wrap to be larger than the width minus gutters, we need - // to switch to GRAVITY_FILL - mTabGravity = GRAVITY_FILL; - updateTabViewsLayoutParams(); - } - - // Now re-measure after our changes - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - // If we've been layed out, update the indicator position - updateIndicatorPosition(); - } - - private void updateIndicatorPosition() { - final View selectedTitle = getChildAt(mSelectedPosition); - int left, right; - - if (selectedTitle != null && selectedTitle.getWidth() > 0) { - left = selectedTitle.getLeft(); - right = selectedTitle.getRight(); - - if (mSelectionOffset > 0f && mSelectedPosition < getChildCount() - 1) { - // Draw the selection partway between the tabs - View nextTitle = getChildAt(mSelectedPosition + 1); - left = (int) (mSelectionOffset * nextTitle.getLeft() + - (1.0f - mSelectionOffset) * left); - right = (int) (mSelectionOffset * nextTitle.getRight() + - (1.0f - mSelectionOffset) * right); - } - } else { - left = right = -1; - } - - setIndicatorPosition(left, right); - } - - private void setIndicatorPosition(int left, int right) { - if (left != mIndicatorLeft || right != mIndicatorRight) { - // If the indicator's left/right has changed, invalidate - mIndicatorLeft = left; - mIndicatorRight = right; - ViewCompat.postInvalidateOnAnimation(this); - } - } - - void animateIndicatorToPosition(final int position, int duration) { - final boolean isRtl = ViewCompat.getLayoutDirection(this) - == ViewCompat.LAYOUT_DIRECTION_RTL; - - final View targetView = getChildAt(position); - final int targetLeft = targetView.getLeft(); - final int targetRight = targetView.getRight(); - final int startLeft; - final int startRight; - - if (Math.abs(position - mSelectedPosition) <= 1) { - // If the views are adjacent, we'll animate from edge-to-edge - startLeft = mIndicatorLeft; - startRight = mIndicatorRight; - } else { - // Else, we'll just grow from the nearest edge - final int offset = dpToPx(MOTION_NON_ADJACENT_OFFSET); - if (position < mSelectedPosition) { - // We're going end-to-start - if (isRtl) { - startLeft = startRight = targetLeft - offset; - } else { - startLeft = startRight = targetRight + offset; - } - } else { - // We're going start-to-end - if (isRtl) { - startLeft = startRight = targetRight + offset; - } else { - startLeft = startRight = targetLeft - offset; - } - } - } - - if (startLeft != targetLeft || startRight != targetRight) { - ValueAnimatorCompat animator = mIndicatorAnimator = ViewUtils.createAnimator(); - animator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR); - animator.setDuration(duration); - animator.setFloatValues(0, 1); - animator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimatorCompat animator) { - final float fraction = animator.getAnimatedFraction(); - setIndicatorPosition( - AnimationUtils.lerp(startLeft, targetLeft, fraction), - AnimationUtils.lerp(startRight, targetRight, fraction)); - } - }); - animator.setListener(new ValueAnimatorCompat.AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(ValueAnimatorCompat animator) { - mSelectedPosition = position; - mSelectionOffset = 0f; - } - - @Override - public void onAnimationCancel(ValueAnimatorCompat animator) { - mSelectedPosition = position; - mSelectionOffset = 0f; - } - }); - animator.start(); - } - } - - @Override - public void draw(Canvas canvas) { - super.draw(canvas); - - // Thick colored underline below the current selection - if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) { - canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight, - mIndicatorRight, getHeight(), mSelectedIndicatorPaint); - } - } - } - - private static ColorStateList createColorStateList(int defaultColor, int selectedColor) { - final int[][] states = new int[2][]; - final int[] colors = new int[2]; - int i = 0; - - states[i] = SELECTED_STATE_SET; - colors[i] = selectedColor; - i++; - - // Default enabled state - states[i] = EMPTY_STATE_SET; - colors[i] = defaultColor; - i++; - - return new ColorStateList(states, colors); - } - - private ColorStateList loadTextColorFromTextAppearance(int textAppearanceResId) { - TypedArray a = getContext().obtainStyledAttributes(textAppearanceResId, - R.styleable.TextAppearance); - try { - return a.getColorStateList(R.styleable.TextAppearance_android_textColor); - } finally { - a.recycle(); - } - } - - /** - * A {@link ViewPager.OnPageChangeListener} class which contains the - * necessary calls back to the provided {@link VerticalTabLayout} so that the tab position is - * kept in sync. - * - *This class stores the provided VerticalTabLayout weakly, meaning that you can use
- * {@link ViewPager#addOnPageChangeListener(ViewPager.OnPageChangeListener)
- * addOnPageChangeListener(OnPageChangeListener)} without removing the listener and
- * not cause a leak.
- */
- public static class VerticalTabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener {
- private final WeakReference