Android编程实现小说阅读器滑动效果的方法

作者:freesonhp 时间:2021-10-26 06:44:06 

本文实例讲述了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,滑动
0
投稿

猜你喜欢

  • java 使用idea将工程打成jar并创建成exe文件类型执行的方法详解

    2022-12-07 02:49:30
  • 详解在Spring中如何自动创建代理

    2023-11-15 15:28:52
  • 微服务通过Feign调用进行密码安全认证操作

    2023-07-30 02:43:38
  • Java反射机制详解

    2023-10-01 12:18:04
  • Java Validation方法入参校验实现过程解析

    2021-08-04 03:31:50
  • java实现LRU缓存淘汰算法的方法

    2022-10-09 00:09:05
  • SpringBoot整合java诊断工具Arthas解读

    2023-08-07 10:39:03
  • Java方法调用解析静态分派动态分派执行过程

    2023-05-03 04:32:40
  • 基于Transactional事务的使用以及注意说明

    2022-02-24 12:23:08
  • Java Swing中JTable渲染器与编辑器用法示例

    2022-11-02 09:23:13
  • c#实现多线程局域网聊天系统

    2022-12-01 23:34:25
  • 探讨:android项目开发 统筹兼顾 需要考虑的因素

    2023-08-05 11:21:32
  • Flutter 剪裁组件的使用

    2023-06-18 13:15:04
  • java GUI编程之paint绘制操作示例

    2023-11-24 17:58:39
  • Java和C++通过new创建的对象有何区别?

    2022-02-07 10:06:10
  • spring mvc中的@PathVariable获得请求url中的动态参数

    2023-08-22 22:08:40
  • SpringBoot中的Thymeleaf用法

    2023-10-30 17:30:39
  • C#常用数据结构和算法总结

    2021-10-31 22:05:29
  • IntelliJ IDEA中properties文件显示乱码问题的解决办法

    2021-09-08 04:59:03
  • java中匿名内部类详解

    2022-10-06 14:56:56
  • asp之家 软件编程 m.aspxhome.com