• 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             mTitleView.layout(0, 0, mTitleView.getMeasuredWidth(), mTitleView.getMeasuredHeight());
87             final int indicatorBottom = getMeasuredHeight();
88             final int indicatorHeight = mIndicatorView.getMeasuredHeight();
89             final int indicatorWidth = mIndicatorView.getMeasuredWidth();
90             final int totalWidth = getMeasuredWidth();
91 
92             // IndicatorView should start on the right when RTL mode is enabled
93             if (isRtlMode()) {
94                 mIndicatorView.layout(totalWidth - indicatorWidth,
95                         indicatorBottom - indicatorHeight, totalWidth,
96                         indicatorBottom);
97             } else {
98 
99                 mIndicatorView.layout(0, indicatorBottom - indicatorHeight,
100                         indicatorWidth, indicatorBottom);
101             }
102         }
103     }
104 
105     @Override
onClick(View v)106     public void onClick(View v) {
107         final int titleCount = mTitleView.getChildCount();
108         for (int i = 0; i < titleCount; i++) {
109             if (v == mTitleView.getChildAt(i)) {
110                 mViewPager.setCurrentItem(i);
111                 return;
112             }
113         }
114     }
115 
onViewPagerPageChanged(int position, float positionOffset)116     private void onViewPagerPageChanged(int position, float positionOffset) {
117         mSelectedPosition = position;
118         mSelectionOffset = positionOffset;
119         // Translation should be reversed in RTL mode
120         final int leftIndicator = isRtlMode() ? -getIndicatorLeft() : getIndicatorLeft();
121         mIndicatorView.setTranslationX(leftIndicator);
122     }
123 
populateTabStrip()124     private void populateTabStrip() {
125         final PagerAdapter adapter = mViewPager.getAdapter();
126 
127         for (int i = 0; i < adapter.getCount(); i++) {
128             final TextView tabTitleView = (TextView) mLayoutInflater.inflate(
129                     R.layout.sliding_tab_title_view, mTitleView, false);
130 
131             tabTitleView.setText(adapter.getPageTitle(i));
132             tabTitleView.setOnClickListener(this);
133 
134             mTitleView.addView(tabTitleView);
135             tabTitleView.setSelected(i == mViewPager.getCurrentItem());
136         }
137     }
138 
getIndicatorLeft()139     private int getIndicatorLeft() {
140         View selectedTitle = mTitleView.getChildAt(mSelectedPosition);
141         int left = selectedTitle.getLeft();
142         if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
143             View nextTitle = mTitleView.getChildAt(mSelectedPosition + 1);
144             left = (int) (mSelectionOffset * nextTitle.getLeft()
145                     + (1.0f - mSelectionOffset) * left);
146         }
147         return left;
148     }
149 
isRtlMode()150     private boolean isRtlMode() {
151         return getLayoutDirection() == LAYOUT_DIRECTION_RTL;
152     }
153 
154     private final class InternalViewPagerListener implements
155             RtlCompatibleViewPager.OnPageChangeListener {
156         private int mScrollState;
157 
158         @Override
onPageScrolled(int position, float positionOffset, int positionOffsetPixels)159         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
160             final int titleCount = mTitleView.getChildCount();
161             if ((titleCount == 0) || (position < 0) || (position >= titleCount)) {
162                 return;
163             }
164             onViewPagerPageChanged(position, positionOffset);
165         }
166 
167         @Override
onPageScrollStateChanged(int state)168         public void onPageScrollStateChanged(int state) {
169             mScrollState = state;
170         }
171 
172         @Override
onPageSelected(int position)173         public void onPageSelected(int position) {
174             position = mViewPager.getRtlAwareIndex(position);
175             if (mScrollState == RtlCompatibleViewPager.SCROLL_STATE_IDLE) {
176                 onViewPagerPageChanged(position, 0f);
177             }
178             final int titleCount = mTitleView.getChildCount();
179             for (int i = 0; i < titleCount; i++) {
180                 mTitleView.getChildAt(i).setSelected(position == i);
181             }
182         }
183     }
184 }
185