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

<?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程序设计有所帮助。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索android
, 滑动效果
小说阅读器
阅读器滑动翻页实现、android实现pdf阅读器、android 滑动解锁实现、android实现左右滑动、android实现上下滑动,以便于您获取更多的相关知识。

时间: 2025-01-02 05:00:17

Android编程实现小说阅读器滑动效果的方法_Android的相关文章

Android编程中ViewPage判断左右滑动方向的方法_Android

本文实例讲述了Android编程中ViewPage判断左右滑动方向的方法.分享给大家供大家参考,具体如下: package com.meityitian.app.views; import android.content.Context; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.util.Log; import com.meityitian.app.uti

Android编程设定activity进入和退出效果的方法

本文实例讲述了Android编程设定activity进入和退出效果的方法.分享给大家供大家参考,具体如下: 看了android的源代码和资源文件,终于明白如何去修改设置Dialog和Activity的进入和退出效果了.设置Dialog首先通过getWindow()方法获取它的窗口,然后通过getAttributes()方法获得window的WindowManager.LayoutParams lp, lp有个公共属性windowAnimations, 只要把要实现的animation的id赋值给

Android编程实现上方通知栏里闪动效果的方法

本文实例讲述了Android编程实现上方通知栏里闪动效果的方法.分享给大家供大家参考,具体如下: 显示通知代码: private void showNotification(Context ctx, String url) { Notification n = new Notification(); n.flags |= Notification.FLAG_SHOW_LIGHTS; n.flags |= Notification.FLAG_AUTO_CANCEL; n.defaults = No

Android编程ProgressBar自定义样式之动画模式实现方法_Android

本文实例讲述了Android编程ProgressBar自定义样式之动画模式实现方法.分享给大家供大家参考,具体如下: 忘记在哪里看到的那位仁兄写的,就是通过用动画效果来实现的,现在顺便也把他写出来,希望那位仁兄不要见怪. 效果: 和之前的一样,在布局文件中: <ProgressBar android:id="@+id/progressBar3" android:layout_width="wrap_content" android:layout_height=

Android编程之控件可拖动的实现方法_Android

本文实例讲述了Android编程之控件可拖动的实现方法.分享给大家供大家参考,具体如下: 点击和触摸的区别是什么? 点击: 一组动作的集合 手指按下着按钮 手指要在按钮停留一段时间 手指离开按钮 private static final String TAG = "DragViewActivity"; private ImageView iv_dv_view; private TextView tv_drag_view; private int startx; private int

Android编程实现识别与挂载U盘的方法_Android

本文实例讲述了Android编程实现识别与挂载U盘的方法.分享给大家供大家参考,具体如下: 关于U盘的挂载可以先看一下这篇文章<Android2.3实现SD卡与U盘自动挂载的方法>,作者是将U盘直接挂载在SD卡下.我的机器是MTK6575的平台,支持sdcard和sdcard2,亲测将U盘挂载到sdcard2下可以实现U盘识别,但这只是一个临时解决办法,因为在同时插sdcard2和usb的情况下可能会有冲突,比如两个设备相同目录下存在同名文件的情况下只能识别一个. 要实现真正的U盘挂载需要修改

Android编程学习之异步加载图片的方法_Android

本文实例讲述了Android编程学习之异步加载图片的方法.分享给大家供大家参考,具体如下: 最近在android开发中碰到比较棘手的问题,就是加载图片内存溢出.我开发的是一个新闻应用,应用中用到大量的图片,一个界面中可能会有上百张图片.开发android应用的朋友可能或多或少碰到加载图片内存溢出问题,一般情况下,加载一张大图就会导致内存溢出,同样,加载多张图片内存溢出的概率也很高. 列一下网络上查到的一般做法: 1.使用BitmapFactory.Options对图片进行压缩 2.优化加载图片的

Android编程判断应用程序是否已安装的方法_Android

本文实例讲述了Android编程判断应用程序是否已安装的方法.分享给大家供大家参考,具体如下: 这里以twitter程序为例,假设twitter程序包名为"com.twitter.android".这时候可用以下代码来判断是否被安装. PackageInfo packageInfo; try { packageInfo = this.getPackageManager().getPackageInfo( "com.twitter.android", 0); } ca

Android编程实现向SD卡写入数据的方法_Android

本文实例讲述了Android编程实现向SD卡写入数据的方法.分享给大家供大家参考,具体如下: 1.代码: /** * 向sdcard中写入文件 * @param filename 文件名 * @param content 文件内容 */ public void saveToSDCard(String filename,String content) throws Exception{ File file=new File("/mnt/sdcard", filename); Output