1 package com.wzq.verticalViewPager; 2 3 import android.content.Context; 4 import android.util.AttributeSet; 5 import android.util.DisplayMetrics; 6 import android.util.Log; 7 import android.view.MotionEvent; 8 import android.view.VelocityTracker; 9 import android.view.View; 10 import android.view.ViewGroup; 11 import android.view.WindowManager; 12 import android.widget.Scroller; 13 14 /** 15 * 垂直方向上的ViewPager 16 */ 17 public class VerticalViewPager extends ViewGroup { 18 19 /** 20 * 屏幕的高度 21 */ 22 private int mScreenHeight; 23 /** 24 * 手指按下时的getScrollY 25 */ 26 private int mScrollStart; 27 /** 28 * 手指抬起时的getScrollY 29 */ 30 private int mScrollEnd; 31 /** 32 * 记录移动时的Y 33 */ 34 private int mLastY; 35 /** 36 * 滚动的辅助类 37 */ 38 private Scroller mScroller; 39 /** 40 * 是否正在滚动 41 */ 42 private boolean isScrolling; 43 /** 44 * 加速度检测 45 */ 46 private VelocityTracker mVelocityTracker; 47 /** 48 * 记录当前页 49 */ 50 private int currentPage = 0; 51 52 private OnPageChangeListener mOnPageChangeListener; 53 54 /** 55 * 回调接口 56 */ 57 public interface OnPageChangeListener { 58 void onPageChange(int currentPage); 59 } 60 61 /** 62 * 设置回调接口 63 * 64 * @param listener 65 */ 66 public void setOnPageChangeListener(OnPageChangeListener listener) { 67 mOnPageChangeListener = listener; 68 } 69 70 public VerticalViewPager(Context context, AttributeSet attrs) { 71 super(context, attrs); 72 /** 73 * 获取屏幕高度 74 */ 75 DisplayMetrics dm = new DisplayMetrics(); 76 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 77 wm.getDefaultDisplay().getMetrics(dm); 78 mScreenHeight = dm.heightPixels; 79 // 初始化滚动辅助类 80 mScroller = new Scroller(context); 81 } 82 83 @Override 84 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 85 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 86 int count = getChildCount(); 87 for (int i = 0; i < count; i++) { 88 View childView = getChildAt(i); 89 measureChild(childView, widthMeasureSpec, heightMeasureSpec); 90 } 91 } 92 93 @Override 94 protected void onLayout(boolean changed, int l, int t, int r, int b) { 95 if (changed) { 96 childCount = getChildCount(); 97 98 // 1, 设置主布局的高度(孩子个数 * 屏幕高度) 99 MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); 100 lp.height = mScreenHeight * childCount; 101 setLayoutParams(lp); 102 // 2,调整每个孩子的高度 103 for (int i = 0; i < childCount; i++) { 104 View child = getChildAt(i); 105 if (child.getVisibility() != View.GONE) { 106 child.layout(l, i * mScreenHeight, r, (i + 1) * mScreenHeight); 107 } 108 } 109 } 110 } 111 112 private String TAG = "VerticalViewPager"; 113 private int childCount; 114 115 @Override 116 public boolean onTouchEvent(MotionEvent event) { 117 // 如果当前正在滚动,调用父类的onTouchEvent 118 if (isScrolling) 119 return super.onTouchEvent(event); 120 int action = event.getAction(); 121 int y = (int) event.getY(); 122 obtainVelocity(event); 123 switch (action) { 124 case MotionEvent.ACTION_DOWN: 125 mScrollStart = getScrollY(); 126 mLastY = y; 127 Log.i(TAG, "111111---ACTION_DOWN getScrollY = " + mScrollStart + " ; mLastY = " 128 + mLastY); 129 break; 130 case MotionEvent.ACTION_MOVE: 131 if (!mScroller.isFinished()) { 132 mScroller.abortAnimation(); 133 } 134 int dy = mLastY - y; 135 int scrollY = getScrollY(); 136 // 边界值检查 137 Log.i(TAG, "2---ACTION_MOVE ; dy = " + dy + " scrollY = " + scrollY); 138 // 已经到达控件最顶端,此时下拉,拉拽速度减半 139 if (dy < 0 && scrollY <= 0) { 140 dy = dy / 2;// 控件移动速度减半 141 } 142 // 已经到达控件最底部,此时上拉,拉拽速度减半 143 // if (dy > 0 && scrollY + dy > getHeight() - mScreenHeight) { 144 if (dy > 0 && scrollY > (childCount - 1) * mScreenHeight) { 145 dy = dy / 2;// 控件移动速度减半 146 } 147 scrollBy(0, dy); 148 mLastY = y; 149 break; 150 case MotionEvent.ACTION_UP: 151 mScrollEnd = getScrollY(); 152 int dScrollY = mScrollEnd - mScrollStart; 153 Log.i(TAG, "333333---ACTION_UP_UP getScrollY = " + mScrollEnd + " ; dScrollY = " 154 + dScrollY); 155 if (wantScrollToNext()) {// 往上滑动 156 if (mScrollStart == (childCount - 1) * mScreenHeight) {// 已经在整个控件的最底部了 157 mScroller.startScroll(0, getScrollY(), 0, -dScrollY);// 回弹(用户不想换页) 158 } else { 159 if (shouldScrollToNext()) { 160 mScroller.startScroll(0, getScrollY(), 0, mScreenHeight - dScrollY);// 换页 161 } else { 162 mScroller.startScroll(0, getScrollY(), 0, -dScrollY);// 回弹 163 } 164 } 165 } 166 if (wantScrollToPre()) {// 往下滑动 167 if (mScrollStart == 0) {// 已经在整个控件的最顶部了 168 mScroller.startScroll(0, getScrollY(), 0, -dScrollY);// 回弹 169 } else { 170 if (shouldScrollToPre()) { 171 mScroller.startScroll(0, getScrollY(), 0, -mScreenHeight - dScrollY);// 换页 172 } else { 173 mScroller.startScroll(0, getScrollY(), 0, -dScrollY);// 回弹 174 } 175 } 176 } 177 isScrolling = true; 178 postInvalidate(); 179 recycleVelocity(); 180 break; 181 } 182 return true; 183 } 184 185 @Override 186 public void computeScroll() { 187 super.computeScroll(); 188 // the animation is not yet finished 189 if (mScroller.computeScrollOffset()) { 190 scrollTo(0, mScroller.getCurrY()); 191 postInvalidate(); 192 Log.i(TAG, "44---computeScroll animation is not not not not not yet finished"); 193 } else {// animation is finished 194 Log.i(TAG, "44---computeScroll animation finished"); 195 int position = getScrollY() / mScreenHeight; 196 Log.i(TAG, "~~~~~~~~~computeScroll position = " + position + " , currentPage = " 197 + currentPage); 198 if (position != currentPage) { 199 if (mOnPageChangeListener != null) { 200 currentPage = position; 201 mOnPageChangeListener.onPageChange(currentPage); 202 } 203 } 204 isScrolling = false; 205 } 206 } 207 208 /** 209 * 根据滚动距离判断是否能够滚到下一页 210 * 211 * @return 212 */ 213 private boolean shouldScrollToNext() { 214 return mScrollEnd - mScrollStart > mScreenHeight / 2 || Math.abs(getVelocity()) > 600; 215 } 216 217 /** 218 * 根据滚动距离判断是否能够滚动到上一页 219 * 220 * @return 221 */ 222 private boolean shouldScrollToPre() { 223 return -mScrollEnd + mScrollStart > mScreenHeight / 2 || Math.abs(getVelocity()) > 600; 224 } 225 226 /** 227 * 根据用户滑动,判断用户的意图是否是滚动到上一页,即手向下滑动。 228 * 229 * @return 230 */ 231 private boolean wantScrollToPre() { 232 return mScrollEnd < mScrollStart; 233 } 234 235 /** 236 * 根据用户滑动,判断用户的意图是否是滚到到下一页,即手向上滑动。 237 * 238 * @return 239 */ 240 private boolean wantScrollToNext() { 241 return mScrollEnd > mScrollStart; 242 } 243 244 /** 245 * 获取Y方向的加速度 246 * 247 * @return 248 */ 249 private int getVelocity() { 250 mVelocityTracker.computeCurrentVelocity(1000); 251 return (int) mVelocityTracker.getYVelocity(); 252 } 253 254 /** 255 * 初始化加速度检测器 256 * 257 * @param event 258 */ 259 private void obtainVelocity(MotionEvent event) { 260 if (mVelocityTracker == null) { 261 mVelocityTracker = VelocityTracker.obtain(); 262 } 263 mVelocityTracker.addMovement(event); 264 } 265 266 /** 267 * 是否资源 268 */ 269 private void recycleVelocity() { 270 if (mVelocityTracker != null) { 271 mVelocityTracker.recycle(); 272 mVelocityTracker = null; 273 } 274 } 275 276 }
xml文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <com.wzq.verticalViewPager.VerticalViewPager xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:id="@+id/id_main_ly" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:orientation="vertical" > 8 9 <RelativeLayout 10 android:layout_width="match_parent" 11 android:layout_height="match_parent" 12 android:background="@drawable/introduce" > 13 14 <Button 15 android:layout_width="wrap_content" 16 android:layout_height="wrap_content" 17 android:text="position = 0" /> 18 </RelativeLayout> 19 20 <RelativeLayout 21 android:layout_width="match_parent" 22 android:layout_height="match_parent" 23 android:background="@drawable/introduce" > 24 25 <Button 26 android:layout_width="wrap_content" 27 android:layout_height="wrap_content" 28 android:text="position = 1" /> 29 </RelativeLayout> 30 31 <RelativeLayout 32 android:layout_width="match_parent" 33 android:layout_height="match_parent" 34 android:background="@drawable/introduce" > 35 36 <Button 37 android:layout_width="wrap_content" 38 android:layout_height="wrap_content" 39 android:text="position = 2" /> 40 </RelativeLayout> 41 42 <RelativeLayout 43 android:layout_width="match_parent" 44 android:layout_height="match_parent" 45 android:background="@drawable/introduce" > 46 47 <Button 48 android:layout_width="wrap_content" 49 android:layout_height="wrap_content" 50 android:text="position = 3" /> 51 </RelativeLayout> 52 53 </com.wzq.verticalViewPager.VerticalViewPager>
引用
1 public class VerticalViewPagerActivity extends Activity { 2 private VerticalViewPager mMianLayout; 3 4 @Override 5 protected void onCreate(Bundle savedInstanceState) { 6 super.onCreate(savedInstanceState); 7 setContentView(R.layout.a_vertical_viewpager); 8 9 mMianLayout = (VerticalViewPager) findViewById(R.id.id_main_ly); 10 mMianLayout.setOnPageChangeListener(new OnPageChangeListener() { 11 @Override 12 public void onPageChange(int currentPage) { 13 // TODO 14 } 15 }); 16 } 17 18 }
参考:http://blog.csdn.net/lmj623565791/article/details/23692439