Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 基於Android小說閱讀器滑動效果的一種實現

基於Android小說閱讀器滑動效果的一種實現

編輯:關於Android編程

看過小說都知道小說閱讀器翻頁有好多種效果,比如仿真翻頁,滑動翻頁,等等。由於某種原因,突然想寫一個簡單點的滑動翻頁效果。在這裡寫出來也沒有什麼意圖,希望大家可以根據這個效果舉一反三,寫出其他的效果。圖就不上了。

下面是代碼:大家理解onTouch事件即可

package com.example.testscroll.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

public class FlipperLayout extends ViewGroup {

	private Scroller mScroller;
	private VelocityTracker mVelocityTracker;

	private int mVelocityValue = 0;

	/** 商定這個滑動是否有效的距離 */
	private int limitDistance = 0;

	private int screenWidth = 0;

	/** 手指移動的方向 */
	private static final int MOVE_TO_LEFT = 0;
	private static final int MOVE_TO_RIGHT = 1;
	private static final int MOVE_NO_RESULT = 2;

	/** 最後觸摸的結果方向 */
	private int mTouchResult = MOVE_NO_RESULT;
	/** 一開始的方向 */
	private int mDirection = MOVE_NO_RESULT;

	/** 觸摸的模式 */
	private static final int MODE_NONE = 0;
	private static final int MODE_MOVE = 1;

	private int mMode = MODE_NONE;

	/** 滑動的view */
	private View mScrollerView = null;

	/** 最上層的view(處於邊緣的,看不到的) */
	private View currentTopView = null;

	/** 顯示的view,顯示在屏幕 */
	private View currentShowView = null;

	/** 最底層的view(看不到的) */
	private View currentBottomView = null;

	public FlipperLayout(Context context) {
		super(context);
		init(context);
	}

	public FlipperLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}

	public FlipperLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	private void init(Context context) {
		mScroller = new Scroller(context);
		screenWidth = context.getResources().getDisplayMetrics().widthPixels;
		limitDistance = screenWidth / 3;
	}

	/***
	 * 
	 * @param listener
	 * @param currentBottomView
	 * 		最底層的view,初始狀態看不到
	 * @param currentShowView
	 * 		正在顯示的View
	 * @param currentTopView
	 * 		最上層的View,初始化時滑出屏幕
	 */
	public void initFlipperViews(TouchListener listener, View currentBottomView, View currentShowView, View currentTopView) {
		this.currentBottomView = currentBottomView;
		this.currentShowView = currentShowView;
		this.currentTopView = currentTopView;
		setTouchResultListener(listener);
		addView(currentBottomView);
		addView(currentShowView);
		addView(currentTopView);
		/** 默認將最上層的view滑動的邊緣(用於查看上一頁) */
		currentTopView.scrollTo(-screenWidth, 0);
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		for (int i = 0; i < getChildCount(); i++) {
			View child = getChildAt(i);
			int height = child.getMeasuredHeight();
			int width = child.getMeasuredWidth();
			child.layout(0, 0, width, height);
		}
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int width = MeasureSpec.getSize(widthMeasureSpec);
		int height = MeasureSpec.getSize(heightMeasureSpec);
		setMeasuredDimension(width, height);
		for (int i = 0; i < getChildCount(); i++) {
			getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
		}
	}

	private int startX = 0;

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (!mScroller.isFinished()) {
				break;
			}
			startX = (int) ev.getX();
			break;
		}
		return super.dispatchTouchEvent(ev);
	}

	@SuppressWarnings("deprecation")
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		obtainVelocityTracker(event);
		switch (event.getAction()) {
		case MotionEvent.ACTION_MOVE:
			if (!mScroller.isFinished()) {
				return super.onTouchEvent(event);
			}
			if (startX == 0) {
				startX = (int) event.getX();
			}
			final int distance = startX - (int) event.getX();
			if (mDirection == MOVE_NO_RESULT) {
				if (mListener.whetherHasNextPage() && distance > 0) {
					mDirection = MOVE_TO_LEFT;
				} else if (mListener.whetherHasPreviousPage() && distance < 0) {
					mDirection = MOVE_TO_RIGHT;
				}
			}
			if (mMode == MODE_NONE
					&& ((mDirection == MOVE_TO_LEFT && mListener.whetherHasNextPage()) || (mDirection == MOVE_TO_RIGHT && mListener
							.whetherHasPreviousPage()))) {
				mMode = MODE_MOVE;
			}

			if (mMode == MODE_MOVE) {
				if ((mDirection == MOVE_TO_LEFT && distance <= 0) || (mDirection == MOVE_TO_RIGHT && distance >= 0)) {
					mMode = MODE_NONE;
				}
			}

			if (mDirection != MOVE_NO_RESULT) {
				if (mDirection == MOVE_TO_LEFT) {
					if (mScrollerView != currentShowView) {
						mScrollerView = currentShowView;
					}
				} else {
					if (mScrollerView != currentTopView) {
						mScrollerView = currentTopView;
					}
				}
				if (mMode == MODE_MOVE) {
					mVelocityTracker.computeCurrentVelocity(1000, ViewConfiguration.getMaximumFlingVelocity());
					if (mDirection == MOVE_TO_LEFT) {
						mScrollerView.scrollTo(distance, 0);
					} else {
						mScrollerView.scrollTo(screenWidth + distance, 0);
					}
				} else {
					final int scrollX = mScrollerView.getScrollX();
					if (mDirection == MOVE_TO_LEFT && scrollX != 0 && mListener.whetherHasNextPage()) {
						mScrollerView.scrollTo(0, 0);
					} else if (mDirection == MOVE_TO_RIGHT && mListener.whetherHasPreviousPage() && screenWidth != Math.abs(scrollX)) {
						mScrollerView.scrollTo(-screenWidth, 0);
					}

				}
			}

			break;

		case MotionEvent.ACTION_UP:
			if (mScrollerView == null) {
				return super.onTouchEvent(event);
			}
			final int scrollX = mScrollerView.getScrollX();
			mVelocityValue = (int) mVelocityTracker.getXVelocity();
			// scroll左正,右負(),(startX + dx)的值如果為0,即復位
			/*
			 * android.widget.Scroller.startScroll( int startX, int startY, int
			 * dx, int dy, int duration )
			 */

			int time = 500;

			if (mMode == MODE_MOVE && mDirection == MOVE_TO_LEFT) {
				if (scrollX > limitDistance || mVelocityValue < -time) {
					// 手指向左移動,可以翻屏幕
					mTouchResult = MOVE_TO_LEFT;
					if (mVelocityValue < -time) {
						time = 200;
					}
					mScroller.startScroll(scrollX, 0, screenWidth - scrollX, 0, time);
				} else {
					mTouchResult = MOVE_NO_RESULT;
					mScroller.startScroll(scrollX, 0, -scrollX, 0, time);
				}
			} else if (mMode == MODE_MOVE && mDirection == MOVE_TO_RIGHT) {
				if ((screenWidth - scrollX) > limitDistance || mVelocityValue > time) {
					// 手指向右移動,可以翻屏幕
					mTouchResult = MOVE_TO_RIGHT;
					if (mVelocityValue > time) {
						time = 250;
					}
					mScroller.startScroll(scrollX, 0, -scrollX, 0, time);
				} else {
					mTouchResult = MOVE_NO_RESULT;
					mScroller.startScroll(scrollX, 0, screenWidth - scrollX, 0, time);
				}
			}
			resetVariables();
			postInvalidate();
			break;
		}
		return true;
	}

	private void resetVariables() {
		mDirection = MOVE_NO_RESULT;
		mMode = MODE_NONE;
		startX = 0;
		releaseVelocityTracker();
	}

	private TouchListener mListener;

	private void setTouchResultListener(TouchListener listener) {
		this.mListener = listener;
	}

	@Override
	public void computeScroll() {
		super.computeScroll();
		if (mScroller.computeScrollOffset()) {
			mScrollerView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
			postInvalidate();
		} else if (mScroller.isFinished() && mListener != null && mTouchResult != MOVE_NO_RESULT) {
			if (mTouchResult == MOVE_TO_LEFT) {
				if (currentTopView != null) {
					removeView(currentTopView);
				}
				currentTopView = mScrollerView;
				currentShowView = currentBottomView;
				if (mListener.currentIsLastPage()) {
					final View newView = mListener.createView(mTouchResult);
					currentBottomView = newView;
					addView(newView, 0);
				} else {
					currentBottomView = new View(getContext());
					currentBottomView.setVisibility(View.GONE);
					addView(currentBottomView, 0);
				}
			} else {
				if (currentBottomView != null) {
					removeView(currentBottomView);
				}
				currentBottomView = currentShowView;
				currentShowView = mScrollerView;
				if (mListener.currentIsFirstPage()) {
					final View newView = mListener.createView(mTouchResult);
					currentTopView = newView;
					currentTopView.scrollTo(-screenWidth, 0);
					addView(currentTopView);
				} else {
					currentTopView = new View(getContext());
					currentTopView.scrollTo(-screenWidth, 0);
					currentTopView.setVisibility(View.GONE);
					addView(currentTopView);
				}
			}
			mTouchResult = MOVE_NO_RESULT;
		}
	}

	private void obtainVelocityTracker(MotionEvent event) {
		if (mVelocityTracker == null) {
			mVelocityTracker = VelocityTracker.obtain();
		}
		mVelocityTracker.addMovement(event);
	}

	private void releaseVelocityTracker() {
		if (mVelocityTracker != null) {
			mVelocityTracker.recycle();
			mVelocityTracker = null;
		}
	}

	/***
	 * 用來實時回調觸摸事件回調
	 * 
	 * @author freeson
	 */
	public interface TouchListener {

		/** 手指向左滑動,即查看下一章節 */
		final int MOVE_TO_LEFT = 0;
		/** 手指向右滑動,即查看上一章節 */
		final int MOVE_TO_RIGHT = 1;

		/**
		 * 創建一個承載Text的View
		 * 
		 * @param direction
		 *            {@link MOVE_TO_LEFT,MOVE_TO_RIGHT}
		 * @return
		 */
		public View createView(final int direction);

		/***
		 * 當前頁是否是第一頁
		 * 
		 * @return
		 */
		public boolean currentIsFirstPage();

		/***
		 * 當前頁是否是最後一頁
		 * 
		 * @return
		 */
		public boolean currentIsLastPage();

		/**
		 * 當前頁是否有上一頁(用來判斷可滑動性)
		 * 
		 * @return
		 */
		public boolean whetherHasPreviousPage();

		/***
		 * 當前頁是否有下一頁(用來判斷可滑動性)
		 * 
		 * @return
		 */
		public boolean whetherHasNextPage();
	}

}

Activity測試文件:

package com.example.testscroll;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import android.app.Activity;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;

import com.example.testscroll.view.FlipperLayout;
import com.example.testscroll.view.FlipperLayout.TouchListener;
import com.example.testscrollactivity.R;

public class MainActivity extends Activity implements OnClickListener, TouchListener {

	private String text = "";
	private int textLenght = 0;

	private static final int COUNT = 400;

	private int currentTopEndIndex = 0;

	private int currentShowEndIndex = 0;

	private int currentBottomEndIndex = 0;

	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			FlipperLayout rootLayout = (FlipperLayout) findViewById(R.id.container);
			View recoverView = LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new, null);
			View view1 = LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new, null);
			View view2 = LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new, null);
			rootLayout.initFlipperViews(MainActivity.this, view2, view1, recoverView);

			textLenght = text.length();

			System.out.println("----textLenght----->" + textLenght);

			TextView textView = (TextView) view1.findViewById(R.id.textview);
			if (textLenght > COUNT) {
				textView.setText(text.subSequence(0, COUNT));
				textView = (TextView) view2.findViewById(R.id.textview);
				if (textLenght > (COUNT << 1)) {
					textView.setText(text.subSequence(COUNT, COUNT * 2));
					currentShowEndIndex = COUNT;
					currentBottomEndIndex = COUNT << 1;
				} else {
					textView.setText(text.subSequence(COUNT, textLenght));
					currentShowEndIndex = textLenght;
					currentBottomEndIndex = textLenght;
				}
			} else {
				textView.setText(text.subSequence(0, textLenght));
				currentShowEndIndex = textLenght;
				currentBottomEndIndex = textLenght;
			}
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		new ReadingThread().start();
	}

	@Override
	public void onClick(View v) {

	}

	@Override
	public View createView(final int direction) {
		String txt = "";
		if (direction == TouchListener.MOVE_TO_LEFT) {
			currentTopEndIndex = currentShowEndIndex;
			final int nextIndex = currentBottomEndIndex + COUNT;
			currentShowEndIndex = currentBottomEndIndex;
			if (textLenght > nextIndex) {
				txt = text.substring(currentBottomEndIndex, nextIndex);
				currentBottomEndIndex = nextIndex;
			} else {
				txt = text.substring(currentBottomEndIndex, textLenght);
				currentBottomEndIndex = textLenght;
			}
		} else {
			currentBottomEndIndex = currentShowEndIndex;
			currentShowEndIndex = currentTopEndIndex;
			currentTopEndIndex = currentTopEndIndex - COUNT;
			txt = text.substring(currentTopEndIndex - COUNT, currentTopEndIndex);
		}

		View view = LayoutInflater.from(this).inflate(R.layout.view_new, null);
		TextView textView = (TextView) view.findViewById(R.id.textview);
		textView.setText(txt);

		System.out.println("-top->" + currentTopEndIndex + "-show->" + currentShowEndIndex + "--bottom-->" + currentBottomEndIndex);
		return view;
	}

	@Override
	public boolean whetherHasPreviousPage() {
		return currentShowEndIndex > COUNT;
	}

	@Override
	public boolean whetherHasNextPage() {
		return currentShowEndIndex < textLenght;
	}

	@Override
	public boolean currentIsFirstPage() {
		boolean should = currentTopEndIndex > COUNT;
		if (!should) {
			currentBottomEndIndex = currentShowEndIndex;
			currentShowEndIndex = currentTopEndIndex;
			currentTopEndIndex = currentTopEndIndex - COUNT;
		}
		return should;
	}

	@Override
	public boolean currentIsLastPage() {
		boolean should = currentBottomEndIndex < textLenght;
		if (!should) {
			currentTopEndIndex = currentShowEndIndex;
			final int nextIndex = currentBottomEndIndex + COUNT;
			currentShowEndIndex = currentBottomEndIndex;
			if (textLenght > nextIndex) {
				currentBottomEndIndex = nextIndex;
			} else {
				currentBottomEndIndex = textLenght;
			}
		}
		return should;
	}

	private class ReadingThread extends Thread {
		public void run() {
			AssetManager am = getAssets();
			InputStream response;
			try {
				response = am.open("text.txt");
				if (response != null) {
					ByteArrayOutputStream baos = new ByteArrayOutputStream();
					int i = -1;
					while ((i = response.read()) != -1) {
						baos.write(i);
					}
					text = new String(baos.toByteArray(), "UTF-8");
					baos.close();
					response.close();
					handler.sendEmptyMessage(0);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
	}

}

xml布局文件:




    

    


activity布局文件:




備注:上面為什麼加一個速率計算器呢,其實只是為了識別這個動作是不是快速滑動的動作,就算滑動的距離不到屏幕的1/3,但是只要速率滿足都可以判定改滑動是一個翻頁的動作。

注意哦:這只是其中一個滑動的效果而已啊,不包括小說分章節的邏輯哦。雖然有些粗糙,但是還是有可以值得學習的地方,大家如果還有什麼好的解決方案,可以一起討論。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved