服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - Android实现阅读APP平移翻页效果

Android实现阅读APP平移翻页效果

2021-06-26 22:41陈靖_ Android

这篇文章主要介绍了Android实现阅读APP平移翻页效果的具体方法,模仿多看阅读平移翻页,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

自己做的一个app需要用到翻页阅读,网上看过立体翻页效果,不过bug太多了还不兼容。看了一下多看阅读翻页是采用平移翻页的,于是就仿写了一个平移翻页的控件。效果如下:

Android实现阅读APP平移翻页效果

在翻页时页面右边缘绘制了阴影,效果还不错。要实现这种平移翻页控件并不难,只需要定义一个布局管理页面就可以了。具体实现上有以下难点:

    1、循环翻页,页面的重复利用。

    2、在翻页时过滤掉多点触碰。

    3、采用setadapter的方式设置页面布局和数据。

下面就来一一解决这几个难点。首先看循环翻页问题,怎么样能采用较少的页面实现这种翻页呢?由于屏幕上每次只能显示一张完整的页面,翻过去的页面也看不到,所以可以把翻过去的页面拿来重复利用,不必每次都new一个页面,所以,我只用了三张页面实现循环翻页。要想重复利用页面,首先要知道页面在布局中序号和对应的层次关系,比如一个父控件的子view的序号越大就位于越上层。循环利用页面的原理图如下:

向右翻页时状态图是这样的,只用了0、1、2三张页面,页面序号为2的位于最上层,我把它隐藏在左边,所以看到的只有页面1,页面0在1下面挡着也看不到,向右翻页时,页面2被滑到屏幕中,这时候把页面0的内容替换成页面2的前一页内容,把它放到之前页面2的位置,这时,状态又回到了初始状态,又可以继续向右翻页了!

Android实现阅读APP平移翻页效果

向左翻页时是这样的,初始状态还是一样,当页面1被往左翻过时,看到的是页面0,这时候页面0下面已经没有页面了,而页面2已经用不到了,这时候把页面2放到页面0下面,这时候状态又回到了初始状态,就可以继续往左翻页了。

Android实现阅读APP平移翻页效果

类似于这种循环效果的实现我一直用的解决方案都是将选中的置于最中间,比如原理图中的页面1,每次翻页完成后可见的都是页面1。在滚动选择器pickerview中也是同样的方案。这就解决了页面的重复利用问题了。

解决难点2 翻页时过滤多点触碰这个问题在中已经解决过了,就是用一个控制变量mevents过滤掉pointer down或up后到来的第一个move事件。

解决难点3 采用adapter方式设置页面的布局和数据。这个在android的adapterview里用到的,但是我没有看它的adapter机制,太复杂了,我就搞了个简单的adapter,如下:

pageadapter.java:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.jingchen.pagerdemo;
 
import android.view.view;
 
public abstract class pageadapter
{
 /**
  * @return 页面view
  */
 public abstract view getview();
 
 public abstract int getcount();
 
 /**
  * 将内容添加到view中
  *
  * @param view
  *   包含内容的view
  * @param position
  *   第position页
  */
 public abstract void addcontent(view view, int position);
}

这是一个抽象类,getview()用于返回页面的布局,getcount()返回数据总共需要多少页,addcontent(view view, int position)这个是每翻过一页后将会被调用来请求页面数据的,参数view就是页面,position是表明第几页。待会儿会在自定义布局中定义setadapter方法设置设配器。
ok,难点都解决了,自定义一个布局叫scanview继承自relativelayout:

scanview.java:

?
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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
package com.jingchen.pagerdemo;
 
import java.util.timer;
import java.util.timertask;
 
import android.content.context;
import android.graphics.canvas;
import android.graphics.lineargradient;
import android.graphics.paint;
import android.graphics.paint.style;
import android.graphics.rectf;
import android.graphics.shader.tilemode;
import android.os.handler;
import android.os.message;
import android.util.attributeset;
import android.util.log;
import android.view.motionevent;
import android.view.velocitytracker;
import android.view.view;
import android.widget.relativelayout;
 
/**
 * @author chenjing
 *
 */
public class scanview extends relativelayout
{
 public static final string tag = "scanview";
 private boolean isinit = true;
 // 滑动的时候存在两页可滑动,要判断是哪一页在滑动
 private boolean ispremoving = true, iscurrmoving = true;
 // 当前是第几页
 private int index;
 private float lastx;
 // 前一页,当前页,下一页的左边位置
 private int prepageleft = 0, currpageleft = 0, nextpageleft = 0;
 // 三张页面
 private view prepage, currpage, nextpage;
 // 页面状态
 private static final int state_move = 0;
 private static final int state_stop = 1;
 // 滑动的页面,只有前一页和当前页可滑
 private static final int pre = 2;
 private static final int curr = 3;
 private int state = state_stop;
 // 正在滑动的页面右边位置,用于绘制阴影
 private float right;
 // 手指滑动的距离
 private float movelenght;
 // 页面宽高
 private int mwidth, mheight;
 // 获取滑动速度
 private velocitytracker vt;
 // 防止抖动
 private float speed_shake = 20;
 // 当前滑动速度
 private float speed;
 private timer timer;
 private mytimertask mtask;
 // 滑动动画的移动速度
 public static final int move_speed = 10;
 // 页面适配器
 private pageadapter adapter;
 /**
 * 过滤多点触碰的控制变量
 */
 private int mevents;
 
 public void setadapter(scanviewadapter adapter)
 {
 removeallviews();
 this.adapter = adapter;
 prepage = adapter.getview();
 addview(prepage, 0, new layoutparams(layoutparams.match_parent,
 layoutparams.match_parent));
 adapter.addcontent(prepage, index - 1);
 
 currpage = adapter.getview();
 addview(currpage, 0, new layoutparams(layoutparams.match_parent,
 layoutparams.match_parent));
 adapter.addcontent(currpage, index);
 
 nextpage = adapter.getview();
 addview(nextpage, 0, new layoutparams(layoutparams.match_parent,
 layoutparams.match_parent));
 adapter.addcontent(nextpage, index + 1);
 
 }
 
 /**
 * 向左滑。注意可以滑动的页面只有当前页和前一页
 *
 * @param which
 */
 private void moveleft(int which)
 {
 switch (which)
 {
 case pre:
 prepageleft -= move_speed;
 if (prepageleft < -mwidth)
 prepageleft = -mwidth;
 right = mwidth + prepageleft;
 break;
 case curr:
 currpageleft -= move_speed;
 if (currpageleft < -mwidth)
 currpageleft = -mwidth;
 right = mwidth + currpageleft;
 break;
 }
 }
 
 /**
 * 向右滑。注意可以滑动的页面只有当前页和前一页
 *
 * @param which
 */
 private void moveright(int which)
 {
 switch (which)
 {
 case pre:
 prepageleft += move_speed;
 if (prepageleft > 0)
 prepageleft = 0;
 right = mwidth + prepageleft;
 break;
 case curr:
 currpageleft += move_speed;
 if (currpageleft > 0)
 currpageleft = 0;
 right = mwidth + currpageleft;
 break;
 }
 }
 
 /**
 * 当往回翻过一页时添加前一页在最左边
 */
 private void addprepage()
 {
 removeview(nextpage);
 addview(nextpage, -1, new layoutparams(layoutparams.match_parent,
 layoutparams.match_parent));
 // 从适配器获取前一页内容
 adapter.addcontent(nextpage, index - 1);
 // 交换顺序
 view temp = nextpage;
 nextpage = currpage;
 currpage = prepage;
 prepage = temp;
 prepageleft = -mwidth;
 }
 
 /**
 * 当往前翻过一页时,添加一页在最底下
 */
 private void addnextpage()
 {
 removeview(prepage);
 addview(prepage, 0, new layoutparams(layoutparams.match_parent,
 layoutparams.match_parent));
 // 从适配器获取后一页内容
 adapter.addcontent(prepage, index + 1);
 // 交换顺序
 view temp = currpage;
 currpage = nextpage;
 nextpage = prepage;
 prepage = temp;
 currpageleft = 0;
 }
 
 handler updatehandler = new handler()
 {
 
 @override
 public void handlemessage(message msg)
 {
 if (state != state_move)
 return;
 // 移动页面
 // 翻回,先判断当前哪一页处于未返回状态
 if (prepageleft > -mwidth && speed <= 0)
 {
 // 前一页处于未返回状态
 moveleft(pre);
 } else if (currpageleft < 0 && speed >= 0)
 {
 // 当前页处于未返回状态
 moveright(curr);
 } else if (speed < 0 && index < adapter.getcount())
 {
 // 向左翻,翻动的是当前页
 moveleft(curr);
 if (currpageleft == (-mwidth))
 {
  index++;
  // 翻过一页,在底下添加一页,把最上层页面移除
  addnextpage();
 }
 } else if (speed > 0 && index > 1)
 {
 // 向右翻,翻动的是前一页
 moveright(pre);
 if (prepageleft == 0)
 {
  index--;
  // 翻回一页,添加一页在最上层,隐藏在最左边
  addprepage();
 }
 }
 if (right == 0 || right == mwidth)
 {
 releasemoving();
 state = state_stop;
 quitmove();
 }
 scanview.this.requestlayout();
 }
 
 };
 
 public scanview(context context, attributeset attrs, int defstyle)
 {
 super(context, attrs, defstyle);
 init();
 }
 
 public scanview(context context)
 {
 super(context);
 init();
 }
 
 public scanview(context context, attributeset attrs)
 {
 super(context, attrs);
 init();
 }
 
 /**
 * 退出动画翻页
 */
 public void quitmove()
 {
 if (mtask != null)
 {
 mtask.cancel();
 mtask = null;
 }
 }
 
 private void init()
 {
 index = 1;
 timer = new timer();
 mtask = new mytimertask(updatehandler);
 }
 
 /**
 * 释放动作,不限制手滑动方向
 */
 private void releasemoving()
 {
 ispremoving = true;
 iscurrmoving = true;
 }
 
 @override
 public boolean dispatchtouchevent(motionevent event)
 {
 if (adapter != null)
 switch (event.getactionmasked())
 {
 case motionevent.action_down:
 lastx = event.getx();
 try
 {
  if (vt == null)
  {
  vt = velocitytracker.obtain();
  } else
  {
  vt.clear();
  }
 } catch (exception e)
 {
  e.printstacktrace();
 }
 vt.addmovement(event);
 mevents = 0;
 break;
 case motionevent.action_pointer_down:
 case motionevent.action_pointer_up:
 mevents = -1;
 break;
 case motionevent.action_move:
 // 取消动画
 quitmove();
 log.d("index", "mevents = " + mevents + ", ispremoving = "
  + ispremoving + ", iscurrmoving = " + iscurrmoving);
 vt.addmovement(event);
 vt.computecurrentvelocity(500);
 speed = vt.getxvelocity();
 movelenght = event.getx() - lastx;
 if ((movelenght > 0 || !iscurrmoving) && ispremoving
  && mevents == 0)
 {
  ispremoving = true;
  iscurrmoving = false;
  if (index == 1)
  {
  // 第一页不能再往右翻,跳转到前一个activity
  state = state_move;
  releasemoving();
  } else
  {
  // 非第一页
  prepageleft += (int) movelenght;
  // 防止滑过边界
  if (prepageleft > 0)
  prepageleft = 0;
  else if (prepageleft < -mwidth)
  {
  // 边界判断,释放动作,防止来回滑动导致滑动前一页时当前页无法滑动
  prepageleft = -mwidth;
  releasemoving();
  }
  right = mwidth + prepageleft;
  state = state_move;
  }
 } else if ((movelenght < 0 || !ispremoving) && iscurrmoving
  && mevents == 0)
 {
  ispremoving = false;
  iscurrmoving = true;
  if (index == adapter.getcount())
  {
  // 最后一页不能再往左翻
  state = state_stop;
  releasemoving();
  } else
  {
  currpageleft += (int) movelenght;
  // 防止滑过边界
  if (currpageleft < -mwidth)
  currpageleft = -mwidth;
  else if (currpageleft > 0)
  {
  // 边界判断,释放动作,防止来回滑动导致滑动当前页是前一页无法滑动
  currpageleft = 0;
  releasemoving();
  }
  right = mwidth + currpageleft;
  state = state_move;
  }
 
 } else
  mevents = 0;
 lastx = event.getx();
 requestlayout();
 break;
 case motionevent.action_up:
 if (math.abs(speed) < speed_shake)
  speed = 0;
 quitmove();
 mtask = new mytimertask(updatehandler);
 timer.schedule(mtask, 0, 5);
 try
 {
  vt.clear();
  vt.recycle();
 } catch (exception e)
 {
  e.printstacktrace();
 }
 break;
 default:
 break;
 }
 super.dispatchtouchevent(event);
 return true;
 }
 
 /*
 * (非 javadoc) 在这里绘制翻页阴影效果
 *
 * @see android.view.viewgroup#dispatchdraw(android.graphics.canvas)
 */
 @override
 protected void dispatchdraw(canvas canvas)
 {
 super.dispatchdraw(canvas);
 if (right == 0 || right == mwidth)
 return;
 rectf rectf = new rectf(right, 0, mwidth, mheight);
 paint paint = new paint();
 paint.setantialias(true);
 lineargradient lineargradient = new lineargradient(right, 0,
 right + 36, 0, 0xffbbbbbb, 0x00bbbbbb, tilemode.clamp);
 paint.setshader(lineargradient);
 paint.setstyle(style.fill);
 canvas.drawrect(rectf, paint);
 }
 
 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec)
 {
 super.onmeasure(widthmeasurespec, heightmeasurespec);
 mwidth = getmeasuredwidth();
 mheight = getmeasuredheight();
 if (isinit)
 {
 // 初始状态,一页放在左边隐藏起来,两页叠在一块
 prepageleft = -mwidth;
 currpageleft = 0;
 nextpageleft = 0;
 isinit = false;
 }
 }
 
 @override
 protected void onlayout(boolean changed, int l, int t, int r, int b)
 {
 if (adapter == null)
 return;
 prepage.layout(prepageleft, 0,
 prepageleft + prepage.getmeasuredwidth(),
 prepage.getmeasuredheight());
 currpage.layout(currpageleft, 0,
 currpageleft + currpage.getmeasuredwidth(),
 currpage.getmeasuredheight());
 nextpage.layout(nextpageleft, 0,
 nextpageleft + nextpage.getmeasuredwidth(),
 nextpage.getmeasuredheight());
 invalidate();
 }
 
 class mytimertask extends timertask
 {
 handler handler;
 
 public mytimertask(handler handler)
 {
 this.handler = handler;
 }
 
 @override
 public void run()
 {
 handler.sendmessage(handler.obtainmessage());
 }
 
 }
}

代码中的注释写的非常多,原理理解了看代码就容易看懂了。写完这个布局后再写一个scanviewadapter继承pageadapter:

?
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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
package com.jingchen.pagerdemo;
 
import java.util.timer;
import java.util.timertask;
 
import android.content.context;
import android.graphics.canvas;
import android.graphics.lineargradient;
import android.graphics.paint;
import android.graphics.paint.style;
import android.graphics.rectf;
import android.graphics.shader.tilemode;
import android.os.handler;
import android.os.message;
import android.util.attributeset;
import android.util.log;
import android.view.motionevent;
import android.view.velocitytracker;
import android.view.view;
import android.widget.relativelayout;
 
/**
 * @author chenjing
 *
 */
public class scanview extends relativelayout
{
 public static final string tag = "scanview";
 private boolean isinit = true;
 // 滑动的时候存在两页可滑动,要判断是哪一页在滑动
 private boolean ispremoving = true, iscurrmoving = true;
 // 当前是第几页
 private int index;
 private float lastx;
 // 前一页,当前页,下一页的左边位置
 private int prepageleft = 0, currpageleft = 0, nextpageleft = 0;
 // 三张页面
 private view prepage, currpage, nextpage;
 // 页面状态
 private static final int state_move = 0;
 private static final int state_stop = 1;
 // 滑动的页面,只有前一页和当前页可滑
 private static final int pre = 2;
 private static final int curr = 3;
 private int state = state_stop;
 // 正在滑动的页面右边位置,用于绘制阴影
 private float right;
 // 手指滑动的距离
 private float movelenght;
 // 页面宽高
 private int mwidth, mheight;
 // 获取滑动速度
 private velocitytracker vt;
 // 防止抖动
 private float speed_shake = 20;
 // 当前滑动速度
 private float speed;
 private timer timer;
 private mytimertask mtask;
 // 滑动动画的移动速度
 public static final int move_speed = 10;
 // 页面适配器
 private pageadapter adapter;
 /**
 * 过滤多点触碰的控制变量
 */
 private int mevents;
 
 public void setadapter(scanviewadapter adapter)
 {
 removeallviews();
 this.adapter = adapter;
 prepage = adapter.getview();
 addview(prepage, 0, new layoutparams(layoutparams.match_parent,
 layoutparams.match_parent));
 adapter.addcontent(prepage, index - 1);
 
 currpage = adapter.getview();
 addview(currpage, 0, new layoutparams(layoutparams.match_parent,
 layoutparams.match_parent));
 adapter.addcontent(currpage, index);
 
 nextpage = adapter.getview();
 addview(nextpage, 0, new layoutparams(layoutparams.match_parent,
 layoutparams.match_parent));
 adapter.addcontent(nextpage, index + 1);
 
 }
 
 /**
 * 向左滑。注意可以滑动的页面只有当前页和前一页
 *
 * @param which
 */
 private void moveleft(int which)
 {
 switch (which)
 {
 case pre:
 prepageleft -= move_speed;
 if (prepageleft < -mwidth)
 prepageleft = -mwidth;
 right = mwidth + prepageleft;
 break;
 case curr:
 currpageleft -= move_speed;
 if (currpageleft < -mwidth)
 currpageleft = -mwidth;
 right = mwidth + currpageleft;
 break;
 }
 }
 
 /**
 * 向右滑。注意可以滑动的页面只有当前页和前一页
 *
 * @param which
 */
 private void moveright(int which)
 {
 switch (which)
 {
 case pre:
 prepageleft += move_speed;
 if (prepageleft > 0)
 prepageleft = 0;
 right = mwidth + prepageleft;
 break;
 case curr:
 currpageleft += move_speed;
 if (currpageleft > 0)
 currpageleft = 0;
 right = mwidth + currpageleft;
 break;
 }
 }
 
 /**
 * 当往回翻过一页时添加前一页在最左边
 */
 private void addprepage()
 {
 removeview(nextpage);
 addview(nextpage, -1, new layoutparams(layoutparams.match_parent,
 layoutparams.match_parent));
 // 从适配器获取前一页内容
 adapter.addcontent(nextpage, index - 1);
 // 交换顺序
 view temp = nextpage;
 nextpage = currpage;
 currpage = prepage;
 prepage = temp;
 prepageleft = -mwidth;
 }
 
 /**
 * 当往前翻过一页时,添加一页在最底下
 */
 private void addnextpage()
 {
 removeview(prepage);
 addview(prepage, 0, new layoutparams(layoutparams.match_parent,
 layoutparams.match_parent));
 // 从适配器获取后一页内容
 adapter.addcontent(prepage, index + 1);
 // 交换顺序
 view temp = currpage;
 currpage = nextpage;
 nextpage = prepage;
 prepage = temp;
 currpageleft = 0;
 }
 
 handler updatehandler = new handler()
 {
 
 @override
 public void handlemessage(message msg)
 {
 if (state != state_move)
 return;
 // 移动页面
 // 翻回,先判断当前哪一页处于未返回状态
 if (prepageleft > -mwidth && speed <= 0)
 {
 // 前一页处于未返回状态
 moveleft(pre);
 } else if (currpageleft < 0 && speed >= 0)
 {
 // 当前页处于未返回状态
 moveright(curr);
 } else if (speed < 0 && index < adapter.getcount())
 {
 // 向左翻,翻动的是当前页
 moveleft(curr);
 if (currpageleft == (-mwidth))
 {
  index++;
  // 翻过一页,在底下添加一页,把最上层页面移除
  addnextpage();
 }
 } else if (speed > 0 && index > 1)
 {
 // 向右翻,翻动的是前一页
 moveright(pre);
 if (prepageleft == 0)
 {
  index--;
  // 翻回一页,添加一页在最上层,隐藏在最左边
  addprepage();
 }
 }
 if (right == 0 || right == mwidth)
 {
 releasemoving();
 state = state_stop;
 quitmove();
 }
 scanview.this.requestlayout();
 }
 
 };
 
 public scanview(context context, attributeset attrs, int defstyle)
 {
 super(context, attrs, defstyle);
 init();
 }
 
 public scanview(context context)
 {
 super(context);
 init();
 }
 
 public scanview(context context, attributeset attrs)
 {
 super(context, attrs);
 init();
 }
 
 /**
 * 退出动画翻页
 */
 public void quitmove()
 {
 if (mtask != null)
 {
 mtask.cancel();
 mtask = null;
 }
 }
 
 private void init()
 {
 index = 1;
 timer = new timer();
 mtask = new mytimertask(updatehandler);
 }
 
 /**
 * 释放动作,不限制手滑动方向
 */
 private void releasemoving()
 {
 ispremoving = true;
 iscurrmoving = true;
 }
 
 @override
 public boolean dispatchtouchevent(motionevent event)
 {
 if (adapter != null)
 switch (event.getactionmasked())
 {
 case motionevent.action_down:
 lastx = event.getx();
 try
 {
  if (vt == null)
  {
  vt = velocitytracker.obtain();
  } else
  {
  vt.clear();
  }
 } catch (exception e)
 {
  e.printstacktrace();
 }
 vt.addmovement(event);
 mevents = 0;
 break;
 case motionevent.action_pointer_down:
 case motionevent.action_pointer_up:
 mevents = -1;
 break;
 case motionevent.action_move:
 // 取消动画
 quitmove();
 log.d("index", "mevents = " + mevents + ", ispremoving = "
  + ispremoving + ", iscurrmoving = " + iscurrmoving);
 vt.addmovement(event);
 vt.computecurrentvelocity(500);
 speed = vt.getxvelocity();
 movelenght = event.getx() - lastx;
 if ((movelenght > 0 || !iscurrmoving) && ispremoving
  && mevents == 0)
 {
  ispremoving = true;
  iscurrmoving = false;
  if (index == 1)
  {
  // 第一页不能再往右翻,跳转到前一个activity
  state = state_move;
  releasemoving();
  } else
  {
  // 非第一页
  prepageleft += (int) movelenght;
  // 防止滑过边界
  if (prepageleft > 0)
  prepageleft = 0;
  else if (prepageleft < -mwidth)
  {
  // 边界判断,释放动作,防止来回滑动导致滑动前一页时当前页无法滑动
  prepageleft = -mwidth;
  releasemoving();
  }
  right = mwidth + prepageleft;
  state = state_move;
  }
 } else if ((movelenght < 0 || !ispremoving) && iscurrmoving
  && mevents == 0)
 {
  ispremoving = false;
  iscurrmoving = true;
  if (index == adapter.getcount())
  {
  // 最后一页不能再往左翻
  state = state_stop;
  releasemoving();
  } else
  {
  currpageleft += (int) movelenght;
  // 防止滑过边界
  if (currpageleft < -mwidth)
  currpageleft = -mwidth;
  else if (currpageleft > 0)
  {
  // 边界判断,释放动作,防止来回滑动导致滑动当前页是前一页无法滑动
  currpageleft = 0;
  releasemoving();
  }
  right = mwidth + currpageleft;
  state = state_move;
  }
 
 } else
  mevents = 0;
 lastx = event.getx();
 requestlayout();
 break;
 case motionevent.action_up:
 if (math.abs(speed) < speed_shake)
  speed = 0;
 quitmove();
 mtask = new mytimertask(updatehandler);
 timer.schedule(mtask, 0, 5);
 try
 {
  vt.clear();
  vt.recycle();
 } catch (exception e)
 {
  e.printstacktrace();
 }
 break;
 default:
 break;
 }
 super.dispatchtouchevent(event);
 return true;
 }
 
 /*
 * (非 javadoc) 在这里绘制翻页阴影效果
 *
 * @see android.view.viewgroup#dispatchdraw(android.graphics.canvas)
 */
 @override
 protected void dispatchdraw(canvas canvas)
 {
 super.dispatchdraw(canvas);
 if (right == 0 || right == mwidth)
 return;
 rectf rectf = new rectf(right, 0, mwidth, mheight);
 paint paint = new paint();
 paint.setantialias(true);
 lineargradient lineargradient = new lineargradient(right, 0,
 right + 36, 0, 0xffbbbbbb, 0x00bbbbbb, tilemode.clamp);
 paint.setshader(lineargradient);
 paint.setstyle(style.fill);
 canvas.drawrect(rectf, paint);
 }
 
 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec)
 {
 super.onmeasure(widthmeasurespec, heightmeasurespec);
 mwidth = getmeasuredwidth();
 mheight = getmeasuredheight();
 if (isinit)
 {
 // 初始状态,一页放在左边隐藏起来,两页叠在一块
 prepageleft = -mwidth;
 currpageleft = 0;
 nextpageleft = 0;
 isinit = false;
 }
 }
 
 @override
 protected void onlayout(boolean changed, int l, int t, int r, int b)
 {
 if (adapter == null)
 return;
 prepage.layout(prepageleft, 0,
 prepageleft + prepage.getmeasuredwidth(),
 prepage.getmeasuredheight());
 currpage.layout(currpageleft, 0,
 currpageleft + currpage.getmeasuredwidth(),
 currpage.getmeasuredheight());
 nextpage.layout(nextpageleft, 0,
 nextpageleft + nextpage.getmeasuredwidth(),
 nextpage.getmeasuredheight());
 invalidate();
 }
 
 class mytimertask extends timertask
 {
 handler handler;
 
 public mytimertask(handler handler)
 {
 this.handler = handler;
 }
 
 @override
 public void run()
 {
 handler.sendmessage(handler.obtainmessage());
 }
 
 }
}

这里只是我的demo里写的adapter,也可以写成带更多内容的adapter。addcontent里带的参数view就是getview里面返回的view,这样就可以根据inflate的布局设置内容了,getview返回的布局page_layout.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
25
26
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@drawable/cover" >
 
 <textview
 android:id="@+id/content"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centerhorizontal="true"
 android:layout_margintop="60dp"
 android:padding="10dp"
 android:textcolor="#000000"
 android:textsize="22sp" />
 
 <textview
 android:id="@+id/index"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignparentbottom="true"
 android:layout_centerhorizontal="true"
 android:layout_marginbottom="60dp"
 android:textcolor="#000000"
 android:textsize="30sp" />
 
</relativelayout>

只包含了两个textview,所以在adapter中可以根据id查找到这两个textview再给它设置内容。
ok了,mainactivity的布局如下:

?
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
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@drawable/cover" >
 
 <textview
 android:id="@+id/content"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centerhorizontal="true"
 android:layout_margintop="60dp"
 android:padding="10dp"
 android:textcolor="#000000"
 android:textsize="22sp" />
 
 <textview
 android:id="@+id/index"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignparentbottom="true"
 android:layout_centerhorizontal="true"
 android:layout_marginbottom="60dp"
 android:textcolor="#000000"
 android:textsize="30sp" />
 
</relativelayout>

很简单,只包含了scanview。
mainactivity的代码:

?
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
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@drawable/cover" >
 
 <textview
 android:id="@+id/content"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centerhorizontal="true"
 android:layout_margintop="60dp"
 android:padding="10dp"
 android:textcolor="#000000"
 android:textsize="22sp" />
 
 <textview
 android:id="@+id/index"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignparentbottom="true"
 android:layout_centerhorizontal="true"
 android:layout_marginbottom="60dp"
 android:textcolor="#000000"
 android:textsize="30sp" />
 
</relativelayout>

给scanview设置adapter就可以了。
好啦,仿多看的平移翻页就完成了。

希望本文对大家学习android软件编程有所帮助。

延伸 · 阅读

精彩推荐