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 17 package com.android.deskclock; 18 19 import android.content.Context; 20 import android.support.v4.view.ViewConfigurationCompat; 21 import android.support.v4.view.ViewPager; 22 import android.util.AttributeSet; 23 import android.view.MotionEvent; 24 import android.view.View; 25 import android.view.ViewConfiguration; 26 import android.view.ViewParent; 27 28 public class VerticalViewPager extends ViewPager { 29 // TODO Remove the hack of using a parent view pager 30 private ViewPager mParentViewPager; 31 private float mLastMotionX; 32 private float mLastMotionY; 33 private float mTouchSlop; 34 private boolean mVerticalDrag; 35 private boolean mHorizontalDrag; 36 37 // Vertical transit page transformer 38 private final ViewPager.PageTransformer mPageTransformer = new ViewPager.PageTransformer() { 39 @Override 40 public void transformPage(View view, float position) { 41 final int pageWidth = view.getWidth(); 42 final int pageHeight = view.getHeight(); 43 if (position < -1) { 44 // This page is way off-screen to the left. 45 view.setAlpha(0); 46 } else if (position <= 1) { 47 view.setAlpha(1); 48 // Counteract the default slide transition 49 view.setTranslationX(pageWidth * -position); 50 // set Y position to swipe in from top 51 float yPosition = position * pageHeight; 52 view.setTranslationY(yPosition); 53 } else { 54 // This page is way off-screen to the right. 55 view.setAlpha(0); 56 } 57 } 58 }; 59 60 public VerticalViewPager(Context context) { 61 super(context, null); 62 } 63 64 public VerticalViewPager(Context context, AttributeSet attrs) { 65 super(context, attrs); 66 final ViewConfiguration configuration = ViewConfiguration.get(context); 67 mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); 68 init(); 69 } 70 71 private void init() { 72 // Make page transit vertical 73 setPageTransformer(true, mPageTransformer); 74 // Get rid of the overscroll drawing that happens on the left and right (the ripple) 75 setOverScrollMode(View.OVER_SCROLL_NEVER); 76 } 77 78 @Override 79 public boolean onTouchEvent(MotionEvent ev) { 80 try { 81 initializeParent(); 82 final float x = ev.getX(); 83 final float y = ev.getY(); 84 switch (ev.getAction()) { 85 case MotionEvent.ACTION_DOWN: { 86 mLastMotionX = x; 87 mLastMotionY = y; 88 if (!mParentViewPager.onTouchEvent(ev)) 89 return false; 90 return verticalDrag(ev); 91 } 92 case MotionEvent.ACTION_MOVE: { 93 final float xDiff = Math.abs(x - mLastMotionX); 94 final float yDiff = Math.abs(y - mLastMotionY); 95 if (!mHorizontalDrag && !mVerticalDrag) { 96 if (xDiff > mTouchSlop && xDiff > yDiff) { // Swiping left and right 97 mHorizontalDrag = true; 98 } else if (yDiff > mTouchSlop && yDiff > xDiff) { //Swiping up and down 99 mVerticalDrag = true; 100 } 101 } 102 if (mHorizontalDrag) { 103 return mParentViewPager.onTouchEvent(ev); 104 } else if (mVerticalDrag) { 105 return verticalDrag(ev); 106 } 107 } 108 case MotionEvent.ACTION_UP: { 109 if (mHorizontalDrag) { 110 mHorizontalDrag = false; 111 return mParentViewPager.onTouchEvent(ev); 112 } 113 if (mVerticalDrag) { 114 mVerticalDrag = false; 115 return verticalDrag(ev); 116 } 117 } 118 } 119 // Set both flags to false in case user lifted finger in the parent view pager 120 mHorizontalDrag = false; 121 mVerticalDrag = false; 122 } catch (Exception e) { 123 // The mParentViewPager shouldn't be null, but just in case. If this happens, 124 // app should not crash, instead just ignore the user swipe input 125 // TODO: handle the exception gracefully 126 } 127 return false; 128 } 129 130 private void initializeParent() { 131 if (mParentViewPager == null) { 132 // This vertical view pager is nested in the frame layout inside the timer tab 133 // (fragment), which is nested inside the horizontal view pager. Therefore, 134 // it needs 3 layers to get all the way to the horizontal view pager. 135 final ViewParent parent = getParent().getParent().getParent(); 136 if (parent instanceof ViewPager) { 137 mParentViewPager = (ViewPager) parent; 138 } 139 } 140 } 141 142 private boolean verticalDrag(MotionEvent ev) { 143 final float x = ev.getX(); 144 final float y = ev.getY(); 145 ev.setLocation(y, x); 146 return super.onTouchEvent(ev); 147 } 148 } 149