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

来源:互联网 发布:英雄无敌3死亡阴影mac 编辑:程序博客网 时间:2024/06/05 17:37

看过小说都知道小说阅读器翻页有好多种效果,比如仿真翻页,滑动翻页,等等。由于某种原因,突然想写一个简单点的滑动翻页效果。在这里写出来也没有什么意图,希望大家可以根据这个效果举一反三,写出其他的效果。图就不上了。

下面是代码:大家理解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);}@Overrideprotected 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);}}@Overrideprotected 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;@Overridepublic 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")@Overridepublic 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;}@Overridepublic 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;}};};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new ReadingThread().start();}@Overridepublic void onClick(View v) {}@Overridepublic 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;}@Overridepublic boolean whetherHasPreviousPage() {return currentShowEndIndex > COUNT;}@Overridepublic boolean whetherHasNextPage() {return currentShowEndIndex < textLenght;}@Overridepublic boolean currentIsFirstPage() {boolean should = currentTopEndIndex > COUNT;if (!should) {currentBottomEndIndex = currentShowEndIndex;currentShowEndIndex = currentTopEndIndex;currentTopEndIndex = currentTopEndIndex - COUNT;}return should;}@Overridepublic 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,但是只要速率满足都可以判定改滑动是一个翻页的动作。

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

附上demo下载地址点击下载demo

1 0
原创粉丝点击