Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

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