1 /*
2  * Copyright (C) 2014 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 package com.android.contacts.common.list;
17 
18 import android.content.Context;
19 import android.content.res.ColorStateList;
20 import android.content.res.TypedArray;
21 import android.graphics.Outline;
22 import android.support.v4.view.PagerAdapter;
23 import android.support.v4.view.ViewPager;
24 import android.util.AttributeSet;
25 import android.util.TypedValue;
26 import android.view.Gravity;
27 import android.view.View;
28 import android.view.ViewOutlineProvider;
29 import android.widget.FrameLayout;
30 import android.widget.HorizontalScrollView;
31 import android.widget.LinearLayout;
32 import android.widget.TextView;
33 import android.widget.Toast;
34 
35 import com.android.contacts.common.R;
36 
37 /**
38  * Lightweight implementation of ViewPager tabs. This looks similar to traditional actionBar tabs,
39  * but allows for the view containing the tabs to be placed anywhere on screen. Text-related
40  * attributes can also be assigned in XML - these will get propogated to the child TextViews
41  * automatically.
42  */
43 public class ViewPagerTabs extends HorizontalScrollView implements ViewPager.OnPageChangeListener {
44 
45     ViewPager mPager;
46     private ViewPagerTabStrip mTabStrip;
47 
48     /**
49      * Linearlayout that will contain the TextViews serving as tabs. This is the only child
50      * of the parent HorizontalScrollView.
51      */
52     final int mTextStyle;
53     final ColorStateList mTextColor;
54     final int mTextSize;
55     final boolean mTextAllCaps;
56     int mPrevSelected = -1;
57     int mSidePadding;
58 
59     private static final ViewOutlineProvider VIEW_BOUNDS_OUTLINE_PROVIDER =
60             new ViewOutlineProvider() {
61         @Override
62         public void getOutline(View view, Outline outline) {
63             outline.setRect(0, 0, view.getWidth(), view.getHeight());
64         }
65     };
66 
67     private static final int TAB_SIDE_PADDING_IN_DPS = 10;
68 
69     // TODO: This should use <declare-styleable> in the future
70     private static final int[] ATTRS = new int[] {
71         android.R.attr.textSize,
72         android.R.attr.textStyle,
73         android.R.attr.textColor,
74         android.R.attr.textAllCaps
75     };
76 
77     /**
78      * Simulates actionbar tab behavior by showing a toast with the tab title when long clicked.
79      */
80     private class OnTabLongClickListener implements OnLongClickListener {
81         final int mPosition;
82 
OnTabLongClickListener(int position)83         public OnTabLongClickListener(int position) {
84             mPosition = position;
85         }
86 
87         @Override
onLongClick(View v)88         public boolean onLongClick(View v) {
89             final int[] screenPos = new int[2];
90             getLocationOnScreen(screenPos);
91 
92             final Context context = getContext();
93             final int width = getWidth();
94             final int height = getHeight();
95             final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
96 
97             Toast toast = Toast.makeText(context, mPager.getAdapter().getPageTitle(mPosition),
98                     Toast.LENGTH_SHORT);
99 
100             // Show the toast under the tab
101             toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL,
102                     (screenPos[0] + width / 2) - screenWidth / 2, screenPos[1] + height);
103 
104             toast.show();
105             return true;
106         }
107     }
108 
ViewPagerTabs(Context context)109     public ViewPagerTabs(Context context) {
110         this(context, null);
111     }
112 
ViewPagerTabs(Context context, AttributeSet attrs)113     public ViewPagerTabs(Context context, AttributeSet attrs) {
114         this(context, attrs, 0);
115     }
116 
ViewPagerTabs(Context context, AttributeSet attrs, int defStyle)117     public ViewPagerTabs(Context context, AttributeSet attrs, int defStyle) {
118         super(context, attrs, defStyle);
119         setFillViewport(true);
120 
121         mSidePadding = (int) (getResources().getDisplayMetrics().density * TAB_SIDE_PADDING_IN_DPS);
122 
123         final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
124         mTextSize = a.getDimensionPixelSize(0, 0);
125         mTextStyle = a.getInt(1, 0);
126         mTextColor = a.getColorStateList(2);
127         mTextAllCaps = a.getBoolean(3, false);
128 
129         mTabStrip = new ViewPagerTabStrip(context);
130         addView(mTabStrip,
131                 new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
132         a.recycle();
133 
134         // enable shadow casting from view bounds
135         setOutlineProvider(VIEW_BOUNDS_OUTLINE_PROVIDER);
136     }
137 
setViewPager(ViewPager viewPager)138     public void setViewPager(ViewPager viewPager) {
139         mPager = viewPager;
140         addTabs(mPager.getAdapter());
141     }
142 
addTabs(PagerAdapter adapter)143     private void addTabs(PagerAdapter adapter) {
144         mTabStrip.removeAllViews();
145 
146         final int count = adapter.getCount();
147         for (int i = 0; i < count; i++) {
148             addTab(adapter.getPageTitle(i), i);
149         }
150     }
151 
addTab(CharSequence tabTitle, final int position)152     private void addTab(CharSequence tabTitle, final int position) {
153         final TextView textView = new TextView(getContext());
154         textView.setText(tabTitle);
155         textView.setBackgroundResource(R.drawable.view_pager_tab_background);
156         textView.setGravity(Gravity.CENTER);
157         textView.setOnClickListener(new OnClickListener() {
158             @Override
159             public void onClick(View v) {
160                 mPager.setCurrentItem(getRtlPosition(position));
161             }
162         });
163 
164         textView.setOnLongClickListener(new OnTabLongClickListener(position));
165 
166         // Assign various text appearance related attributes to child views.
167         if (mTextStyle > 0) {
168             textView.setTypeface(textView.getTypeface(), mTextStyle);
169         }
170         if (mTextSize > 0) {
171             textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
172         }
173         if (mTextColor != null) {
174             textView.setTextColor(mTextColor);
175         }
176         textView.setAllCaps(mTextAllCaps);
177         textView.setPadding(mSidePadding, 0, mSidePadding, 0);
178         mTabStrip.addView(textView, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
179                 LayoutParams.MATCH_PARENT, 1));
180         // Default to the first child being selected
181         if (position == 0) {
182             mPrevSelected = 0;
183             textView.setSelected(true);
184         }
185     }
186 
187     @Override
onPageScrolled(int position, float positionOffset, int positionOffsetPixels)188     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
189         position = getRtlPosition(position);
190         int tabStripChildCount = mTabStrip.getChildCount();
191         if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
192             return;
193         }
194 
195         mTabStrip.onPageScrolled(position, positionOffset, positionOffsetPixels);
196     }
197 
198     @Override
onPageSelected(int position)199     public void onPageSelected(int position) {
200         position = getRtlPosition(position);
201         int tabStripChildCount = mTabStrip.getChildCount();
202         if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
203             return;
204         }
205 
206         if (mPrevSelected >= 0 && mPrevSelected < tabStripChildCount) {
207             mTabStrip.getChildAt(mPrevSelected).setSelected(false);
208         }
209         final View selectedChild = mTabStrip.getChildAt(position);
210         selectedChild.setSelected(true);
211 
212         // Update scroll position
213         final int scrollPos = selectedChild.getLeft() - (getWidth() - selectedChild.getWidth()) / 2;
214         smoothScrollTo(scrollPos, 0);
215         mPrevSelected = position;
216     }
217 
218     @Override
onPageScrollStateChanged(int state)219     public void onPageScrollStateChanged(int state) {
220     }
221 
getRtlPosition(int position)222     private int getRtlPosition(int position) {
223         if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
224             return mTabStrip.getChildCount() - 1 - position;
225         }
226         return position;
227     }
228 }
229 
230