OSDN Git Service

Update Eleven headers and namespace for open source
[android-x86/packages-apps-Eleven.git] / src / com / viewpagerindicator / UnderlinePageIndicator.java
1 /*
2  * Copyright (C) 2012 Jake Wharton
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.viewpagerindicator;
17
18 import android.content.Context;
19 import android.content.res.Resources;
20 import android.content.res.TypedArray;
21 import android.graphics.Canvas;
22 import android.graphics.Paint;
23 import android.graphics.drawable.Drawable;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.support.v4.view.MotionEventCompat;
27 import android.support.v4.view.ViewConfigurationCompat;
28 import android.support.v4.view.ViewPager;
29 import android.util.AttributeSet;
30 import android.view.MotionEvent;
31 import android.view.View;
32 import android.view.ViewConfiguration;
33
34 import com.cyanogenmod.eleven.R;
35
36 /**
37  * Draws a line for each page. The current page line is colored differently
38  * than the unselected page lines.
39  */
40 public class UnderlinePageIndicator extends View implements PageIndicator {
41     private static final int INVALID_POINTER = -1;
42     private static final int FADE_FRAME_MS = 30;
43
44     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
45
46     private boolean mFades;
47     private int mFadeDelay;
48     private int mFadeLength;
49     private int mFadeBy;
50
51     private ViewPager mViewPager;
52     private ViewPager.OnPageChangeListener mListener;
53     private int mScrollState;
54     private int mCurrentPage;
55     private float mPositionOffset;
56
57     private int mTouchSlop;
58     private float mLastMotionX = -1;
59     private int mActivePointerId = INVALID_POINTER;
60     private boolean mIsDragging;
61
62     private final Runnable mFadeRunnable = new Runnable() {
63       @Override public void run() {
64         if (!mFades) return;
65
66         final int alpha = Math.max(mPaint.getAlpha() - mFadeBy, 0);
67         mPaint.setAlpha(alpha);
68         invalidate();
69         if (alpha > 0) {
70           postDelayed(this, FADE_FRAME_MS);
71         }
72       }
73     };
74
75     public UnderlinePageIndicator(Context context) {
76         this(context, null);
77     }
78
79     public UnderlinePageIndicator(Context context, AttributeSet attrs) {
80         this(context, attrs, R.attr.vpiUnderlinePageIndicatorStyle);
81     }
82
83     public UnderlinePageIndicator(Context context, AttributeSet attrs, int defStyle) {
84         super(context, attrs, defStyle);
85         if (isInEditMode()) return;
86
87         final Resources res = getResources();
88
89         //Load defaults from resources
90         final boolean defaultFades = res.getBoolean(R.bool.default_underline_indicator_fades);
91         final int defaultFadeDelay = res.getInteger(R.integer.default_underline_indicator_fade_delay);
92         final int defaultFadeLength = res.getInteger(R.integer.default_underline_indicator_fade_length);
93         final int defaultSelectedColor = res.getColor(R.color.default_underline_indicator_selected_color);
94
95         //Retrieve styles attributes
96         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UnderlinePageIndicator, defStyle, 0);
97
98         setFades(a.getBoolean(R.styleable.UnderlinePageIndicator_fades, defaultFades));
99         setSelectedColor(a.getColor(R.styleable.UnderlinePageIndicator_selectedColor, defaultSelectedColor));
100         setFadeDelay(a.getInteger(R.styleable.UnderlinePageIndicator_fadeDelay, defaultFadeDelay));
101         setFadeLength(a.getInteger(R.styleable.UnderlinePageIndicator_fadeLength, defaultFadeLength));
102
103         Drawable background = a.getDrawable(R.styleable.UnderlinePageIndicator_android_background);
104         if (background != null) {
105           setBackgroundDrawable(background);
106         }
107
108         a.recycle();
109
110         final ViewConfiguration configuration = ViewConfiguration.get(context);
111         mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
112     }
113
114     public boolean getFades() {
115         return mFades;
116     }
117
118     public void setFades(boolean fades) {
119         if (fades != mFades) {
120             mFades = fades;
121             if (fades) {
122                 post(mFadeRunnable);
123             } else {
124                 removeCallbacks(mFadeRunnable);
125                 mPaint.setAlpha(0xFF);
126                 invalidate();
127             }
128         }
129     }
130
131     public int getFadeDelay() {
132         return mFadeDelay;
133     }
134
135     public void setFadeDelay(int fadeDelay) {
136         mFadeDelay = fadeDelay;
137     }
138
139     public int getFadeLength() {
140         return mFadeLength;
141     }
142
143     public void setFadeLength(int fadeLength) {
144         mFadeLength = fadeLength;
145         mFadeBy = 0xFF / (mFadeLength / FADE_FRAME_MS);
146     }
147
148     public int getSelectedColor() {
149         return mPaint.getColor();
150     }
151
152     public void setSelectedColor(int selectedColor) {
153         mPaint.setColor(selectedColor);
154         invalidate();
155     }
156
157     @Override
158     protected void onDraw(Canvas canvas) {
159         super.onDraw(canvas);
160
161         if (mViewPager == null) {
162             return;
163         }
164         final int count = mViewPager.getAdapter().getCount();
165         if (count == 0) {
166             return;
167         }
168
169         if (mCurrentPage >= count) {
170             setCurrentItem(count - 1);
171             return;
172         }
173
174         final int paddingLeft = getPaddingLeft();
175         final float pageWidth = (getWidth() - paddingLeft - getPaddingRight()) / (1f * count);
176         final float left = paddingLeft + pageWidth * (mCurrentPage + mPositionOffset);
177         final float right = left + pageWidth;
178         final float top = getPaddingTop();
179         final float bottom = getHeight() - getPaddingBottom();
180         canvas.drawRect(left, top, right, bottom, mPaint);
181     }
182
183     public boolean onTouchEvent(MotionEvent ev) {
184         if (super.onTouchEvent(ev)) {
185             return true;
186         }
187         if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {
188             return false;
189         }
190
191         final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
192         switch (action) {
193             case MotionEvent.ACTION_DOWN:
194                 mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
195                 mLastMotionX = ev.getX();
196                 break;
197
198             case MotionEvent.ACTION_MOVE: {
199                 final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
200                 final float x = MotionEventCompat.getX(ev, activePointerIndex);
201                 final float deltaX = x - mLastMotionX;
202
203                 if (!mIsDragging) {
204                     if (Math.abs(deltaX) > mTouchSlop) {
205                         mIsDragging = true;
206                     }
207                 }
208
209                 if (mIsDragging) {
210                     mLastMotionX = x;
211                     if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {
212                         mViewPager.fakeDragBy(deltaX);
213                     }
214                 }
215
216                 break;
217             }
218
219             case MotionEvent.ACTION_CANCEL:
220             case MotionEvent.ACTION_UP:
221                 if (!mIsDragging) {
222                     final int count = mViewPager.getAdapter().getCount();
223                     final int width = getWidth();
224                     final float halfWidth = width / 2f;
225                     final float sixthWidth = width / 6f;
226
227                     if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) {
228                         if (action != MotionEvent.ACTION_CANCEL) {
229                             mViewPager.setCurrentItem(mCurrentPage - 1);
230                         }
231                         return true;
232                     } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) {
233                         if (action != MotionEvent.ACTION_CANCEL) {
234                             mViewPager.setCurrentItem(mCurrentPage + 1);
235                         }
236                         return true;
237                     }
238                 }
239
240                 mIsDragging = false;
241                 mActivePointerId = INVALID_POINTER;
242                 if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();
243                 break;
244
245             case MotionEventCompat.ACTION_POINTER_DOWN: {
246                 final int index = MotionEventCompat.getActionIndex(ev);
247                 mLastMotionX = MotionEventCompat.getX(ev, index);
248                 mActivePointerId = MotionEventCompat.getPointerId(ev, index);
249                 break;
250             }
251
252             case MotionEventCompat.ACTION_POINTER_UP:
253                 final int pointerIndex = MotionEventCompat.getActionIndex(ev);
254                 final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
255                 if (pointerId == mActivePointerId) {
256                     final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
257                     mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
258                 }
259                 mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));
260                 break;
261         }
262
263         return true;
264     }
265
266     @Override
267     public void setViewPager(ViewPager viewPager) {
268         if (mViewPager == viewPager) {
269             return;
270         }
271         if (mViewPager != null) {
272             //Clear us from the old pager.
273             mViewPager.setOnPageChangeListener(null);
274         }
275         if (viewPager.getAdapter() == null) {
276             throw new IllegalStateException("ViewPager does not have adapter instance.");
277         }
278         mViewPager = viewPager;
279         mViewPager.setOnPageChangeListener(this);
280         invalidate();
281         post(new Runnable() {
282             @Override public void run() {
283                 if (mFades) {
284                     post(mFadeRunnable);
285                 }
286             }
287         });
288     }
289
290     @Override
291     public void setViewPager(ViewPager view, int initialPosition) {
292         setViewPager(view);
293         setCurrentItem(initialPosition);
294     }
295
296     @Override
297     public void setCurrentItem(int item) {
298         if (mViewPager == null) {
299             throw new IllegalStateException("ViewPager has not been bound.");
300         }
301         mViewPager.setCurrentItem(item);
302         mCurrentPage = item;
303         invalidate();
304     }
305
306     @Override
307     public void notifyDataSetChanged() {
308         invalidate();
309     }
310
311     @Override
312     public void onPageScrollStateChanged(int state) {
313         mScrollState = state;
314
315         if (mListener != null) {
316             mListener.onPageScrollStateChanged(state);
317         }
318     }
319
320     @Override
321     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
322         mCurrentPage = position;
323         mPositionOffset = positionOffset;
324         if (mFades) {
325             if (positionOffsetPixels > 0) {
326                 removeCallbacks(mFadeRunnable);
327                 mPaint.setAlpha(0xFF);
328             } else if (mScrollState != ViewPager.SCROLL_STATE_DRAGGING) {
329                 postDelayed(mFadeRunnable, mFadeDelay);
330             }
331         }
332         invalidate();
333
334         if (mListener != null) {
335             mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
336         }
337     }
338
339     @Override
340     public void onPageSelected(int position) {
341         if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
342             mCurrentPage = position;
343             mPositionOffset = 0;
344             invalidate();
345             mFadeRunnable.run();
346         }
347         if (mListener != null) {
348             mListener.onPageSelected(position);
349         }
350     }
351
352     @Override
353     public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
354         mListener = listener;
355     }
356
357     @Override
358     public void onRestoreInstanceState(Parcelable state) {
359         SavedState savedState = (SavedState)state;
360         super.onRestoreInstanceState(savedState.getSuperState());
361         mCurrentPage = savedState.currentPage;
362         requestLayout();
363     }
364
365     @Override
366     public Parcelable onSaveInstanceState() {
367         Parcelable superState = super.onSaveInstanceState();
368         SavedState savedState = new SavedState(superState);
369         savedState.currentPage = mCurrentPage;
370         return savedState;
371     }
372
373     static class SavedState extends BaseSavedState {
374         int currentPage;
375
376         public SavedState(Parcelable superState) {
377             super(superState);
378         }
379
380         private SavedState(Parcel in) {
381             super(in);
382             currentPage = in.readInt();
383         }
384
385         @Override
386         public void writeToParcel(Parcel dest, int flags) {
387             super.writeToParcel(dest, flags);
388             dest.writeInt(currentPage);
389         }
390
391         @SuppressWarnings("UnusedDeclaration")
392         public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
393             @Override
394             public SavedState createFromParcel(Parcel in) {
395                 return new SavedState(in);
396             }
397
398             @Override
399             public SavedState[] newArray(int size) {
400                 return new SavedState[size];
401             }
402         };
403     }
404 }