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

时间:2022-05-31 06:54:42

本文实例讲述了Android编程实现小说阅读器滑动效果的方法。分享给大家供大家参考,具体如下:

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

下面是代码:大家理解onTouch事件即可

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
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测试文件:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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布局文件:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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布局文件:

?
1
2
3
4
5
6
<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程序设计有所帮助。