基于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布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <TextView
        android:id="@+id/textview"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1.0"
        android:background="#666666"
        android:gravity="center"
        android:text="新建的View"
        android:textColor="@android:color/white"
        android:textSize="16sp"
        android:visibility="visible" />

    <View
        android:layout_width="5dp"
        android:layout_height="match_parent"
        android:background="#FFFF00"
        android:gravity="center"
        android:textSize="25sp"
        android:visibility="visible" />

</LinearLayout>

activity布局文件:

<com.example.testscroll.view.FlipperLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

</com.example.testscroll.view.FlipperLayout>

备注:上面为什么加一个速率计算器呢,其实只是为了识别这个动作是不是快速滑动的动作,就算滑动的距离不到屏幕的1/3,但是只要速率满足都可以判定改滑动是一个翻页的动作。

注意哦:这只是其中一个滑动的效果而已啊,不包括小说分章节的逻辑哦。虽然有些粗糙,但是还是有可以值得学习的地方,大家如果还有什么好的解决方案,可以一起讨论。

基于Android小说阅读器滑动效果的一种实现,布布扣,bubuko.com

基于Android小说阅读器滑动效果的一种实现

上一篇:android 发送短信 怎样做到一条一条的发送,仅仅有在上一条发送成功之后才发送下一条短信


下一篇:详解 Android 的 Activity 组件