Android墨跡3.0特性介紹效果實現——做一個垂直滾動的Layout

墨跡天氣新版的開機介紹很漂亮,上下滾動翻頁,翻頁結束後元素會有動畫效果,分析一下動畫元素都是基本的Animation,沒有用到最新的屬性動畫;上下翻頁滾動的控件android沒有提供,只有橫向的Viewpager,這裏有一種實現->點擊打開鏈接,用到了開源的控件ViewPager-Android,我們這裏試着手動實現一個上下滾動的翻頁控件。

前期準備

首先我們用apktool把墨跡天氣的安裝包解壓出來,取出其中的圖片資源和佈局文件,一共4個佈局


翻頁控件實現

要實現自定義佈局,需要繼承ViewGroup,然後實現onMeasure、onLayout方法

@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		int count = getChildCount();
		int height = getMeasuredHeight();
		int top = 0;
		for (int i = 0; i < count; ++i) {
			View childView = getChildAt(i);
			if (childView.getVisibility() != View.GONE) {
				childView.layout(l, top, r, top + height);
				top += height;
			}
		}
		mTotalHeight = height * (count - 1);
		mTolerance = height / 2;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int count = getChildCount();
		for (int i = 0; i < count; ++i) {
			View childView = getChildAt(i);
			measureChild(childView, widthMeasureSpec, heightMeasureSpec);
		}
	}

這裏在onLayout方法中將頁面排下來,每頁內容都充滿控件,垂直排列。

這時候在如果在activity中將View inflate出來再通過addview添加到控件中就會看到第一頁的內容,但此時還不能滑動。下面我們就來實現上下滑動翻頁。

要響應控件上的手勢操作需要實現onTouchEvent方法:

	@Override
	public boolean onTouchEvent(MotionEvent event) {

		if (mVelocityTracker == null) {
			mVelocityTracker = VelocityTracker.obtain();
		}
		mVelocityTracker.addMovement(event);

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (!mScroller.isFinished()) {
				mScroller.abortAnimation();
			}
			mLastY = (int) event.getY();
			mStartYPosition = getScrollY();
			break;
		case MotionEvent.ACTION_MOVE:
			int y = (int) event.getY();
			int distance = mLastY - y;
			int scrollY = getScrollY();
			// 邊界檢查
			if (distance < 0 && scrollY + distance < 0) {
				distance = 0 - scrollY;
			} else if (distance > 0 && scrollY + distance > mTotalHeight) {
				distance = mTotalHeight - scrollY;
			}
			scrollBy(0, distance);
			mLastY = y;
			break;
		case MotionEvent.ACTION_UP:

			mEndYPosition = getScrollY();
			int posDiff = mEndYPosition - mStartYPosition;

			mVelocityTracker.computeCurrentVelocity(1000);
			int velocityY = (int) mVelocityTracker.getYVelocity();
			mVelocityTracker.recycle();
			mVelocityTracker = null;

			if (Math.abs(velocityY) >= 600 || Math.abs(posDiff) > mTolerance) {
				int dis = 0;
				if (posDiff > 0) {
					dis = getMeasuredHeight() - posDiff;
				} else if (posDiff < 0) {
					dis = -(getMeasuredHeight() + posDiff);
				}
				mScroller.startScroll(0, 0, 0, dis);
			} else {
				mScroller.startScroll(0, 0, 0, -posDiff);
			}

			postInvalidate();
			break;
		default:
			break;
		}

		return true;
	}

上面的這些操作包括了滾動、邊界檢查(避免滑出邊界)和完成翻頁的功能,最開始其實是這樣的

@Override
	public boolean onTouchEvent(MotionEvent event) {

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			mLastY = (int) event.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			int y = (int) event.getY();
			int distance = mLastY - y;
			scrollBy(0, distance);
			mLastY = y;
			break;
		case MotionEvent.ACTION_UP:
			break;
		default:
			break;
		}

		return true;
	}

頁面可以隨着手指上下滾動,但是會超出邊界,再添加上邊界檢查,就是修改一下ACTION_MOVE,

		case MotionEvent.ACTION_MOVE:
			int y = (int) event.getY();
			int distance = mLastY - y;
			int scrollY = getScrollY();
			// 邊界檢查
			if (distance < 0 && scrollY + distance < 0) {
				distance = 0 - scrollY;
			} else if (distance > 0 && scrollY + distance > mTotalHeight) {
				distance = mTotalHeight - scrollY;
			}
			scrollBy(0, distance);
			mLastY = y;
			break;

這時候再滾動的時候就會發現到達邊界的時候就無法再滑動了,下面再添加滑動半屏後自動完成翻頁的功能,就是最上面的那個完整的代碼,裏面用到了scroller,在擡起手指的時候計算滾動剩餘距離,然後開始滾動,scroller只負責完成滾動過程位置的計算,真正控制頁面的是在computeScroll()方法裏:

	@Override
	public void computeScroll() {

		if (mScroller.computeScrollOffset()) {
			int scroll = mScroller.getCurrY();
			if (scroll > 0 && mEndYPosition + scroll > mTotalHeight) {
				scroll = mTotalHeight - mEndYPosition;
			} else if (scroll < 0 && mEndYPosition + scroll < 0) {
				scroll = -mEndYPosition;
			}
			scrollTo(0, mEndYPosition + scroll);
			mIsScrolling = true;
			postInvalidate();
		} else if (mIsScrolling) {
			if (mPageScrollListener != null) {
				int position = getScrollY() / getMeasuredHeight();
				if (position != mCurrentPage) {
					mCurrentPage = position;
					mPageScrollListener.onPageChanged(mCurrentPage);
				}
			}
			mIsScrolling = false;
		}
	}

每次頁面重繪都會調用computeScroll方法,然後通過scroller得到此時的滾動值,再次重繪,直到滾動結束,這裏也做了下邊界檢測,防止滾過頭了。

滾動結束後要通知控件的使用者翻頁已完成,所以定義一個翻頁完成的接口

public void setOnPageScrollListener(OnPageScrollListener listener) {
		mPageScrollListener = listener;
	}

	public interface OnPageScrollListener {
		public void onPageChanged(int position);
	}

在computeScroll()中發現翻頁完成了就調用這個接口。

剩下的就是在activity中加載動畫了,每當翻頁結束就播放相應頁面的動畫並清除上一頁的動畫效果

class MyPageScrollListener implements OnPageScrollListener {

		@Override
		public void onPageChanged(int position) {
			switch (position) {
			case 0:
				layout1AnimStart();
				break;
			case 1:
				layout2AnimStart();
				break;
			case 2:
				layout3AnimStart();
				break;
			case 3:
				layout4AnimStart();
				break;
			}
		}

	}

演示效果


代碼在這裏->http://download.csdn.net/detail/xu_fu/7185403

發佈了54 篇原創文章 · 獲贊 12 · 訪問量 72萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章