[go: nahoru, domu]

1/*
2 * Copyright (C) 2012 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
17package android.support.v4.view;
18
19import android.content.Context;
20import android.graphics.Canvas;
21import android.graphics.Paint;
22import android.graphics.Rect;
23import android.graphics.drawable.Drawable;
24import android.support.annotation.ColorInt;
25import android.support.annotation.ColorRes;
26import android.support.annotation.DrawableRes;
27import android.util.AttributeSet;
28import android.view.MotionEvent;
29import android.view.View;
30import android.view.ViewConfiguration;
31
32/**
33 * PagerTabStrip is an interactive indicator of the current, next,
34 * and previous pages of a {@link ViewPager}. It is intended to be used as a
35 * child view of a ViewPager widget in your XML layout.
36 * Add it as a child of a ViewPager in your layout file and set its
37 * android:layout_gravity to TOP or BOTTOM to pin it to the top or bottom
38 * of the ViewPager. The title from each page is supplied by the method
39 * {@link PagerAdapter#getPageTitle(int)} in the adapter supplied to
40 * the ViewPager.
41 *
42 * <p>For a non-interactive indicator, see {@link PagerTitleStrip}.</p>
43 */
44public class PagerTabStrip extends PagerTitleStrip {
45    private static final String TAG = "PagerTabStrip";
46
47    private static final int INDICATOR_HEIGHT = 3; // dp
48    private static final int MIN_PADDING_BOTTOM = INDICATOR_HEIGHT + 3; // dp
49    private static final int TAB_PADDING = 16; // dp
50    private static final int TAB_SPACING = 32; // dp
51    private static final int MIN_TEXT_SPACING = TAB_SPACING + TAB_PADDING * 2; // dp
52    private static final int FULL_UNDERLINE_HEIGHT = 1; // dp
53    private static final int MIN_STRIP_HEIGHT = 32; // dp
54
55    private int mIndicatorColor;
56    private int mIndicatorHeight;
57
58    private int mMinPaddingBottom;
59    private int mMinTextSpacing;
60    private int mMinStripHeight;
61
62    private int mTabPadding;
63
64    private final Paint mTabPaint = new Paint();
65    private final Rect mTempRect = new Rect();
66
67    private int mTabAlpha = 0xFF;
68
69    private boolean mDrawFullUnderline = false;
70    private boolean mDrawFullUnderlineSet = false;
71    private int mFullUnderlineHeight;
72
73    private boolean mIgnoreTap;
74    private float mInitialMotionX;
75    private float mInitialMotionY;
76    private int mTouchSlop;
77
78    public PagerTabStrip(Context context) {
79        this(context, null);
80    }
81
82    public PagerTabStrip(Context context, AttributeSet attrs) {
83        super(context, attrs);
84
85        mIndicatorColor = mTextColor;
86        mTabPaint.setColor(mIndicatorColor);
87
88        // Note: this follows the rules for Resources#getDimensionPixelOffset/Size:
89        //       sizes round up, offsets round down.
90        final float density = context.getResources().getDisplayMetrics().density;
91        mIndicatorHeight = (int) (INDICATOR_HEIGHT * density + 0.5f);
92        mMinPaddingBottom = (int) (MIN_PADDING_BOTTOM * density + 0.5f);
93        mMinTextSpacing = (int) (MIN_TEXT_SPACING * density);
94        mTabPadding = (int) (TAB_PADDING * density + 0.5f);
95        mFullUnderlineHeight = (int) (FULL_UNDERLINE_HEIGHT * density + 0.5f);
96        mMinStripHeight = (int) (MIN_STRIP_HEIGHT * density + 0.5f);
97        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
98
99        // Enforce restrictions
100        setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
101        setTextSpacing(getTextSpacing());
102
103        setWillNotDraw(false);
104
105        mPrevText.setFocusable(true);
106        mPrevText.setOnClickListener(new OnClickListener() {
107            @Override
108            public void onClick(View v) {
109                mPager.setCurrentItem(mPager.getCurrentItem() - 1);
110            }
111        });
112
113        mNextText.setFocusable(true);
114        mNextText.setOnClickListener(new OnClickListener() {
115            @Override
116            public void onClick(View v) {
117                mPager.setCurrentItem(mPager.getCurrentItem() + 1);
118            }
119        });
120
121        if (getBackground() == null) {
122            mDrawFullUnderline = true;
123        }
124    }
125
126    /**
127     * Set the color of the tab indicator bar.
128     *
129     * @param color Color to set as an 0xRRGGBB value. The high byte (alpha) is ignored.
130     */
131    public void setTabIndicatorColor(@ColorInt int color) {
132        mIndicatorColor = color;
133        mTabPaint.setColor(mIndicatorColor);
134        invalidate();
135    }
136
137    /**
138     * Set the color of the tab indicator bar from a color resource.
139     *
140     * @param resId Resource ID of a color resource to load
141     */
142    public void setTabIndicatorColorResource(@ColorRes int resId) {
143        setTabIndicatorColor(getContext().getResources().getColor(resId));
144    }
145
146    /**
147     * @return The current tab indicator color as an 0xRRGGBB value.
148     */
149    @ColorInt
150    public int getTabIndicatorColor() {
151        return mIndicatorColor;
152    }
153
154    @Override
155    public void setPadding(int left, int top, int right, int bottom) {
156        if (bottom < mMinPaddingBottom) {
157            bottom = mMinPaddingBottom;
158        }
159        super.setPadding(left, top, right, bottom);
160    }
161
162    @Override
163    public void setTextSpacing(int textSpacing) {
164        if (textSpacing < mMinTextSpacing) {
165            textSpacing = mMinTextSpacing;
166        }
167        super.setTextSpacing(textSpacing);
168    }
169
170    @Override
171    public void setBackgroundDrawable(Drawable d) {
172        super.setBackgroundDrawable(d);
173        if (!mDrawFullUnderlineSet) {
174            mDrawFullUnderline = d == null;
175        }
176    }
177
178    @Override
179    public void setBackgroundColor(@ColorInt int color) {
180        super.setBackgroundColor(color);
181        if (!mDrawFullUnderlineSet) {
182            mDrawFullUnderline = (color & 0xFF000000) == 0;
183        }
184    }
185
186    @Override
187    public void setBackgroundResource(@DrawableRes int resId) {
188        super.setBackgroundResource(resId);
189        if (!mDrawFullUnderlineSet) {
190            mDrawFullUnderline = resId == 0;
191        }
192    }
193
194    /**
195     * Set whether this tab strip should draw a full-width underline in the
196     * current tab indicator color.
197     *
198     * @param drawFull true to draw a full-width underline, false otherwise
199     */
200    public void setDrawFullUnderline(boolean drawFull) {
201        mDrawFullUnderline = drawFull;
202        mDrawFullUnderlineSet = true;
203        invalidate();
204    }
205
206    /**
207     * Return whether or not this tab strip will draw a full-width underline.
208     * This defaults to true if no background is set.
209     *
210     * @return true if this tab strip will draw a full-width underline in the
211     * current tab indicator color.
212     */
213    public boolean getDrawFullUnderline() {
214        return mDrawFullUnderline;
215    }
216
217    @Override
218    int getMinHeight() {
219        return Math.max(super.getMinHeight(), mMinStripHeight);
220    }
221
222    @Override
223    public boolean onTouchEvent(MotionEvent ev) {
224        final int action = ev.getAction();
225        if (action != MotionEvent.ACTION_DOWN && mIgnoreTap) {
226            return false;
227        }
228
229        // Any tap within touch slop to either side of the current item
230        // will scroll to prev/next.
231        final float x = ev.getX();
232        final float y = ev.getY();
233        switch (action) {
234            case MotionEvent.ACTION_DOWN:
235                mInitialMotionX = x;
236                mInitialMotionY = y;
237                mIgnoreTap = false;
238                break;
239
240            case MotionEvent.ACTION_MOVE:
241                if (Math.abs(x - mInitialMotionX) > mTouchSlop ||
242                        Math.abs(y - mInitialMotionY) > mTouchSlop) {
243                    mIgnoreTap = true;
244                }
245                break;
246
247            case MotionEvent.ACTION_UP:
248                if (x < mCurrText.getLeft() - mTabPadding) {
249                    mPager.setCurrentItem(mPager.getCurrentItem() - 1);
250                } else if (x > mCurrText.getRight() + mTabPadding) {
251                    mPager.setCurrentItem(mPager.getCurrentItem() + 1);
252                }
253                break;
254        }
255
256        return true;
257    }
258
259    @Override
260    protected void onDraw(Canvas canvas) {
261        super.onDraw(canvas);
262
263        final int height = getHeight();
264        final int bottom = height;
265        final int left = mCurrText.getLeft() - mTabPadding;
266        final int right = mCurrText.getRight() + mTabPadding;
267        final int top = bottom - mIndicatorHeight;
268
269        mTabPaint.setColor(mTabAlpha << 24 | (mIndicatorColor & 0xFFFFFF));
270        canvas.drawRect(left, top, right, bottom, mTabPaint);
271
272        if (mDrawFullUnderline) {
273            mTabPaint.setColor(0xFF << 24 | (mIndicatorColor & 0xFFFFFF));
274            canvas.drawRect(getPaddingLeft(), height - mFullUnderlineHeight,
275                    getWidth() - getPaddingRight(), height, mTabPaint);
276        }
277    }
278
279    @Override
280    void updateTextPositions(int position, float positionOffset, boolean force) {
281        final Rect r = mTempRect;
282        int bottom = getHeight();
283        int left = mCurrText.getLeft() - mTabPadding;
284        int right = mCurrText.getRight() + mTabPadding;
285        int top = bottom - mIndicatorHeight;
286
287        r.set(left, top, right, bottom);
288
289        super.updateTextPositions(position, positionOffset, force);
290        mTabAlpha = (int) (Math.abs(positionOffset - 0.5f) * 2 * 0xFF);
291
292        left = mCurrText.getLeft() - mTabPadding;
293        right = mCurrText.getRight() + mTabPadding;
294        r.union(left, top, right, bottom);
295
296        invalidate(r);
297    }
298}
299