Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.widget;
     18 
     19 import android.content.Context;
     20 import android.support.v4.view.PagerAdapter;
     21 import android.util.AttributeSet;
     22 import android.view.Gravity;
     23 import android.view.LayoutInflater;
     24 import android.view.View;
     25 import android.widget.FrameLayout;
     26 import android.widget.LinearLayout;
     27 import android.widget.TextView;
     28 
     29 import com.android.settings.R;
     30 
     31 /**
     32  * To be used with ViewPager to provide a tab indicator component which give constant feedback as
     33  * to the user's scroll progress.
     34  */
     35 public final class SlidingTabLayout extends FrameLayout implements View.OnClickListener {
     36 
     37     private final LinearLayout mTitleView;
     38     private final View mIndicatorView;
     39     private final LayoutInflater mLayoutInflater;
     40 
     41     private RtlCompatibleViewPager mViewPager;
     42     private int mSelectedPosition;
     43     private float mSelectionOffset;
     44 
     45     public SlidingTabLayout(Context context, AttributeSet attrs) {
     46         super(context, attrs);
     47         mLayoutInflater = LayoutInflater.from(context);
     48         mTitleView = new LinearLayout(context);
     49         mTitleView.setGravity(Gravity.CENTER_HORIZONTAL);
     50         mIndicatorView = mLayoutInflater.inflate(R.layout.sliding_tab_indicator_view, this, false);
     51 
     52         addView(mTitleView, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
     53         addView(mIndicatorView, mIndicatorView.getLayoutParams());
     54     }
     55 
     56     /**
     57      * Sets the associated view pager. Note that the assumption here is that the pager content
     58      * (number of tabs and tab titles) does not change after this call has been made.
     59      */
     60     public void setViewPager(RtlCompatibleViewPager viewPager) {
     61         mTitleView.removeAllViews();
     62 
     63         mViewPager = viewPager;
     64         if (viewPager != null) {
     65             viewPager.addOnPageChangeListener(new InternalViewPagerListener());
     66             populateTabStrip();
     67         }
     68     }
     69 
     70     @Override
     71     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     72         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     73         final int titleCount = mTitleView.getChildCount();
     74         if (titleCount > 0) {
     75             final int width = MeasureSpec.makeMeasureSpec(
     76                     mTitleView.getMeasuredWidth() / titleCount, MeasureSpec.EXACTLY);
     77             final int height = MeasureSpec.makeMeasureSpec(
     78                     mIndicatorView.getMeasuredHeight(), MeasureSpec.EXACTLY);
     79             mIndicatorView.measure(width, height);
     80         }
     81     }
     82 
     83     @Override
     84     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
     85         if (mTitleView.getChildCount() > 0) {
     86             final int indicatorBottom = getMeasuredHeight();
     87             final int indicatorHeight = mIndicatorView.getMeasuredHeight();
     88             final int indicatorWidth = mIndicatorView.getMeasuredWidth();
     89             final int totalWidth = getMeasuredWidth();
     90             final int leftPadding = getPaddingLeft();
     91             final int rightPadding = getPaddingRight();
     92 
     93             mTitleView.layout(leftPadding, 0, mTitleView.getMeasuredWidth() + rightPadding,
     94                     mTitleView.getMeasuredHeight());
     95             // IndicatorView should start on the right when RTL mode is enabled
     96             if (isRtlMode()) {
     97                 mIndicatorView.layout(totalWidth - indicatorWidth,
     98                         indicatorBottom - indicatorHeight, totalWidth,
     99                         indicatorBottom);
    100             } else {
    101                 mIndicatorView.layout(0, indicatorBottom - indicatorHeight,
    102                         indicatorWidth, indicatorBottom);
    103             }
    104         }
    105     }
    106 
    107     @Override
    108     public void onClick(View v) {
    109         final int titleCount = mTitleView.getChildCount();
    110         for (int i = 0; i < titleCount; i++) {
    111             if (v == mTitleView.getChildAt(i)) {
    112                 mViewPager.setCurrentItem(i);
    113                 return;
    114             }
    115         }
    116     }
    117 
    118     private void onViewPagerPageChanged(int position, float positionOffset) {
    119         mSelectedPosition = position;
    120         mSelectionOffset = positionOffset;
    121         // Translation should be reversed in RTL mode
    122         final int leftIndicator = isRtlMode() ? -getIndicatorLeft() : getIndicatorLeft();
    123         mIndicatorView.setTranslationX(leftIndicator);
    124     }
    125 
    126     private void populateTabStrip() {
    127         final PagerAdapter adapter = mViewPager.getAdapter();
    128 
    129         for (int i = 0; i < adapter.getCount(); i++) {
    130             final TextView tabTitleView = (TextView) mLayoutInflater.inflate(
    131                     R.layout.sliding_tab_title_view, mTitleView, false);
    132 
    133             tabTitleView.setText(adapter.getPageTitle(i));
    134             tabTitleView.setOnClickListener(this);
    135 
    136             mTitleView.addView(tabTitleView);
    137             tabTitleView.setSelected(i == mViewPager.getCurrentItem());
    138         }
    139     }
    140 
    141     private int getIndicatorLeft() {
    142         View selectedTitle = mTitleView.getChildAt(mSelectedPosition);
    143         int left = selectedTitle.getLeft();
    144         if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
    145             View nextTitle = mTitleView.getChildAt(mSelectedPosition + 1);
    146             left = (int) (mSelectionOffset * nextTitle.getLeft()
    147                     + (1.0f - mSelectionOffset) * left);
    148         }
    149         return left;
    150     }
    151 
    152     private boolean isRtlMode() {
    153         return getLayoutDirection() == LAYOUT_DIRECTION_RTL;
    154     }
    155 
    156     private final class InternalViewPagerListener implements
    157             RtlCompatibleViewPager.OnPageChangeListener {
    158         private int mScrollState;
    159 
    160         @Override
    161         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    162             final int titleCount = mTitleView.getChildCount();
    163             if ((titleCount == 0) || (position < 0) || (position >= titleCount)) {
    164                 return;
    165             }
    166             onViewPagerPageChanged(position, positionOffset);
    167         }
    168 
    169         @Override
    170         public void onPageScrollStateChanged(int state) {
    171             mScrollState = state;
    172         }
    173 
    174         @Override
    175         public void onPageSelected(int position) {
    176             position = mViewPager.getRtlAwareIndex(position);
    177             if (mScrollState == RtlCompatibleViewPager.SCROLL_STATE_IDLE) {
    178                 onViewPagerPageChanged(position, 0f);
    179             }
    180             final int titleCount = mTitleView.getChildCount();
    181             for (int i = 0; i < titleCount; i++) {
    182                 mTitleView.getChildAt(i).setSelected(position == i);
    183             }
    184         }
    185     }
    186 }
    187