Welcome

首页 / 移动开发 / 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);}@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。
希望本文所述对大家Android程序设计有所帮助。