• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SlidingTabLayout(Context context, AttributeSet attrs)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      */
setViewPager(RtlCompatibleViewPager viewPager)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
onMeasure(int widthMeasureSpec, int heightMeasureSpec)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
onLayout(boolean changed, int left, int top, int right, int bottom)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
onClick(View v)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 
onViewPagerPageChanged(int position, float positionOffset)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 
populateTabStrip()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 
getIndicatorLeft()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 
isRtlMode()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
onPageScrolled(int position, float positionOffset, int positionOffsetPixels)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
onPageScrollStateChanged(int state)170         public void onPageScrollStateChanged(int state) {
171             mScrollState = state;
172         }
173 
174         @Override
onPageSelected(int position)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