Android笔记:触摸事件的分析与总结----多点触控

简介:

一、多点触控

    当多点同时触摸屏幕时,系统将会产生如下的触摸事件:

    1.ACTION_DOWN:触摸屏幕的第一个点。此时手势开始。该点的数据通常在MotionEvent事件队列索引位置0处。

    2.ACTION_POINTER_DOWN:除了第一个点的其他触摸点数据。该点的数据的索引位置由getActionIndex()方法返回。

    3.ACTION_MOVE:在手势过程中发生的一次变化。

    4.ACTION_POINTER_UP:当不是第一个点的其他点UP后触发。

    5.ACTION_UP:当手势中的最后一个点离开屏幕。


简单测试范例


布局xml代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android"
     xmlns:tools= "http://schemas.android.com/tools"
     android:id= "@+id/layout1"
     android:layout_width= "match_parent"
     android:layout_height= "match_parent"
     android:tag= "true布局"
     tools:context= ".MainActivity"  >
 
     <TextView
         android:id= "@+id/message"
         android:layout_width= "wrap_content"
         android:layout_height= "wrap_content"
         android:tag= "true控件"
         android:text= "@string/hello_world"  />
 
</RelativeLayout>


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
package  com.lonshine.d_touchduodian;
 
import  android.os.Bundle;
import  android.app.Activity;
import  android.util.Log;
import  android.view.MotionEvent;
import  android.view.View;
import  android.view.View.OnTouchListener;
import  android.widget.RelativeLayout;
 
public  class  MainActivity  extends  Activity  implements  OnTouchListener  
{
     
     @Override
     protected  void  onCreate(Bundle savedInstanceState)
     {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         
         RelativeLayout layout = (RelativeLayout) findViewById(R.id.layout1);
         layout.setOnTouchListener( this );
         
     }
 
     @Override
     public  boolean  onTouch(View v, MotionEvent event)
     {
         String tag = v.getTag().toString();
         Log.e(tag,  "**********************************************" );
         Log.e(tag,  "**********************************************" );
         Log.e(tag,  "触摸的是:"  + tag);
         Log.e(tag, describeEvent(event));
         logAction(event);
         Log.e(tag,  "**********************************************" );
         Log.e(tag,  "**********************************************" );
         Log.e( "" "                        " );
         Log.e( "" "                        " );
         
         if ( "true" .equals(tag.substring( 0 4 )))
         {
             return  true ;
         }
         else
         {
             return  false ;
         }
     }
 
     
     protected  static  String describeEvent(MotionEvent event)
     {
         StringBuilder sb =  new  StringBuilder( 500 );
         
         sb.append( "Action: " ).append(event.getAction()).append( "\n" ); // 获取触控动作比如ACTION_DOWN
         int  count = event.getPointerCount();
         sb.append( "触点个数: " ).append(count).append( "\n" );
         int  i =  0 ;
         while  (i < count)
         {
             sb.append( "-------------------------" ).append( "\n" );
             int  pointerId = event.getPointerId(i);
             sb.append( "触点序号: " ).append(i).append( "\n" );
             sb.append( "触点ID : " ).append(pointerId).append( "\n" );
             sb.append( "相对坐标: " ).append(event.getX(i)).append( "  *  " ).append(event.getY(i)).append( "\n" );
             sb.append( "压力       : " ).append(event.getPressure(i)).append( "\n" ); // 压力值,0-1之间,看情况,很可能始终返回1
             sb.append( "范围大小: " ).append(event.getSize(i)).append( "\n" ); // 指压范围
             
             i++;
             sb.append( "-------------------------" ).append( "\n" );
         }
         
         
         sb.append( "按下时间: " ).append(event.getDownTime()).append( "ms   " );
         sb.append( "结束时间: " ).append(event.getEventTime()).append( "ms   " );
         sb.append( "运行时间: " ).append(event.getEventTime() - event.getDownTime()).append( "ms\n" );
         
         return  sb.toString();
     }
     
     
     
     private  void  logAction(MotionEvent event)
     {
         int  masked = event.getActionMasked();
         int  index = event.getActionIndex();
         int  pointerId = event.getPointerId(index);
         
         if (masked ==  5  || masked ==  6 )
         {
             masked = masked -  5 ;
         }
         
         Log.e( "Action" "触点序号: "  + index);
         Log.e( "Action" "触点ID :  "  + pointerId);
         Log.e( "Action" "ActionMasked :"  + masked);
     }
     
     
}


多点触控的日志打印出来太多,此处仅针对日志输出作简单分析:

A.按下第一根手指时,获得一个索引为0且指针ID为0的指针(ACTION_DOWN = 0);

B.接下去action输出为2(ACTION_MOVE),此时仍然仅有一个指针,并且索引和ID都为0;

C.然后按下第二根手指,action值输出为261。此值由两部分组成:一个是指针索引,一个是指针正在执行何种操作;

D.将十进制261转换为十六进制数为0x00000105。其中01代表指针索引,05代表操作值(即ACTION_POINTER_DOWN的值,用于多点触摸场景);

E.依此类推,当按下第三根手指时action输出值为0x00000205(十进制517),第四根则为0x000305(十进制773);

F.当第一根手指离开屏幕时,action值为0x00000006(十进制6),即索引为0,操作值为6(即ACTION_POINTER_UP);

G.如果此时第二根手指离开时,action值应该为0x00000106(十进制262),因为此时仍然拥有两根手指的信息;

H.而实际结果并没有得到262的action值,这是因为当第一根手指离开屏幕后,第二根手指的索引从1更改成了0,但是指针ID仍然为1.

I.对于每一次移动,action值将始终为2,因为ACTION_MOVE事件并没有包含哪根手指在移动的信息。


附:依次按下四根手指的日志

wKiom1RbP7iwz9OeAAZOMRV8Tik291.jpg





其他小结:

(1)getPointerCount() 获得触屏的点数,

   getActionIndex()获得触点的指针索引,

   getPointerId(index)获得指针索引对应的触点ID;


(2)getActionMasked()表示用于多点触控检测点。而在1.6和2.1中并没有event.getActionMasked()这个方法,其实他就是把event.getAction()& MotionEvent.ACTION_MASK封装了一下。






二、其他参考范例:

(一)单点触摸拖动图片与多点触摸缩放图片

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
package  com.lonshine.touchdemo;
 
 
 
import  android.app.Activity;
import  android.content.Context;
import  android.graphics.*;
import  android.graphics.drawable.BitmapDrawable;
import  android.os.Bundle;
import  android.view.MotionEvent;
import  android.widget.ImageView;
import  android.widget.ImageView.ScaleType;
 
/**
  * 多点触控来控制ImageView中图像的放大与缩小, 单点触控则用来拖动图片
 
  * @author zeng
 
  */
public  class  MainActivity  extends  Activity
{
     private  MyImageView imageView;
     private  Bitmap bitmap;
     
     // 两点触屏后之间的长度
     private  float  beforeLenght;
     private  float  afterLenght;
     
     // 单点移动的前后坐标值
     private  float  afterX, afterY;
     private  float  beforeX, beforeY;
     
     /** Called when the activity is first created. */
     @Override
     public  void  onCreate(Bundle savedInstanceState)
     {
         super .onCreate(savedInstanceState);
         findView();
         setContentView(imageView);
         config();
     }
     
     private  void  findView()
     {
         imageView =  new  MyImageView( this );
         // 获得图片
         bitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.xing)).getBitmap();
     }
     
     private  void  config()
     {
         // 设置imageView的显示图片
         imageView.setImageBitmap(bitmap);
         // 设置图片填充ImageView
         imageView.setScaleType(ScaleType.FIT_XY);
     }
     
     // 创建一个自己的ImageView类
     class  MyImageView  extends  ImageView
     {
         
         private  float  scale =  0 .1f;
         
         public  MyImageView(Context context)
         {
             super (context);
         }
         
         // 用来设置ImageView的位置
         private  void  setLocation( int  x,  int  y)
         {
             this .setFrame( this .getLeft() + x,  this .getTop() + y,  this .getRight() + x,  this .getBottom() + y);
         }
         
         /**
          * 用来放大缩小ImageView 因为图片是填充ImageView的,所以也就有放大缩小图片的效果 flag为0是放大图片,为1是缩小图片
          */
         private  void  setScale( float  temp,  int  flag)
         {
             
             if  (flag ==  0 )
             {
                 this .setFrame( this .getLeft() - ( int ) (temp *  this .getWidth()),  this .getTop() - ( int ) (temp *  this .getHeight()),  this .getRight()
                         + ( int ) (temp *  this .getWidth()),  this .getBottom() + ( int ) (temp *  this .getHeight()));
             }
             else
             {
                 this .setFrame( this .getLeft() + ( int ) (temp *  this .getWidth()),  this .getTop() + ( int ) (temp *  this .getHeight()),  this .getRight()
                         - ( int ) (temp *  this .getWidth()),  this .getBottom() - ( int ) (temp *  this .getHeight()));
             }
         }
         
         // 绘制边框
         @Override
         protected  void  onDraw(Canvas canvas)
         {
             super .onDraw(canvas);
             Rect rec = canvas.getClipBounds();
             rec.bottom--;
             rec.right--;
             Paint paint =  new  Paint();
             paint.setColor(Color.RED);
             paint.setStyle(Paint.Style.STROKE);
             canvas.drawRect(rec, paint);
         }
         
         /**
          * 让图片跟随手指触屏的位置移动 beforeX、Y是用来保存前一位置的坐标 afterX、Y是用来保存当前位置的坐标
          * 它们的差值就是ImageView各坐标的增加或减少值
          */
         public  void  moveWithFinger(MotionEvent event)
         {
             
             switch  (event.getAction())
             {
             
                 case  MotionEvent.ACTION_DOWN:
                     beforeX = event.getX();
                     beforeY = event.getY();
                     break ;
                 case  MotionEvent.ACTION_MOVE:
                     afterX = event.getX();
                     afterY = event.getY();
                     
                     this .setLocation(( int ) (afterX - beforeX), ( int ) (afterY - beforeY));
                     
                     beforeX = afterX;
                     beforeY = afterY;
                     break ;
                 
                 case  MotionEvent.ACTION_UP:
                     break ;
             }
         }
         
         /**
          * 通过多点触屏放大或缩小图像 beforeLenght用来保存前一时间两点之间的距离 afterLenght用来保存当前时间两点之间的距离
          */
         public  void  scaleWithFinger(MotionEvent event)
         {
             float  moveX = event.getX( 1 ) - event.getX( 0 );
             float  moveY = event.getY( 1 ) - event.getY( 0 );
             
             switch  (event.getAction())
             {
                 case  MotionEvent.ACTION_DOWN:
                     beforeLenght = ( float ) Math.sqrt((moveX * moveX) + (moveY * moveY));
                     break ;
                 case  MotionEvent.ACTION_MOVE:
                     // 得到两个点之间的长度
                     afterLenght = ( float ) Math.sqrt((moveX * moveX) + (moveY * moveY));
                     
                     float  gapLenght = afterLenght - beforeLenght;
                     
                     if  (gapLenght ==  0 )
                     {
                         break ;
                     }
                     
                     // 如果当前时间两点距离大于前一时间两点距离,则传0,否则传1
                     if  (gapLenght >  0 )
                     {
                         this .setScale(scale,  0 );
                     }
                     else
                     {
                         this .setScale(scale,  1 );
                     }
                     
                     beforeLenght = afterLenght;
                     break ;
             }
         }
         
     }
     
     // 这里来监听屏幕触控时间
     @Override
     public  boolean  onTouchEvent(MotionEvent event)
     {
         
         /**
          * 判定用户是否触摸到了图片 如果是单点触摸则调用控制图片移动的方法 如果是2点触控则调用控制图片大小的方法
          */
         if  (event.getY() > imageView.getTop() && event.getY() < imageView.getBottom() && event.getX() > imageView.getLeft()
                 && event.getX() < imageView.getRight())
         {
             if  (event.getPointerCount() ==  2 )
             {
                 imageView.scaleWithFinger(event);
             }
             else  if  (event.getPointerCount() ==  1 )
             {
                 imageView.moveWithFinger(event);
             }
         }
         return  true ;
     }
     
}


范例参考自:http://blog.csdn.net/ldj299/article/details/6422547




(二)实现多点消息拦截,用于多个物体拖动等效果

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
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
package  com.lonshine.d_touchdemo;
 
import  java.util.ArrayList;
import  java.util.List;
import  android.content.Context;
import  android.os.Handler;
import  android.os.Message;
import  android.view.MotionEvent;
import  android.view.VelocityTracker;
import  android.view.ViewConfiguration;
 
 
/**
  * 实现多点消息拦截,用于多个物体拖动等效果
  * @author zeng
  */
public  class  MultiTouchGestureDetector
{
     @SuppressWarnings ( "unused" )
     private  static  final  String MYTAG =  "Alan" ;
     public  static  final  String CLASS_NAME =  "MultiTouchGestureDetector" ;
     
     /**
      * 事件信息类 <br/>
      * 用来记录一个手势
      */
     private  class  EventInfo
     {
         private  MultiMotionEvent mCurrentDownEvent;  // 当前的down事件
         private  MultiMotionEvent mPreviousUpEvent;  // 上一次up事件
         private  boolean  mStillDown;  // 当前手指是否还在屏幕上
         private  boolean  mInLongPress;  // 当前事件是否属于长按手势
         private  boolean  mAlwaysInTapRegion;  // 是否当前手指仅在小范围内移动,当手指仅在小范围内移动时,视为手指未曾移动过,不会触发onScroll手势
         private  boolean  mAlwaysInBiggerTapRegion;  // 是否当前手指在较大范围内移动,仅当此值为true时,双击手势才能成立
         private  boolean  mIsDoubleTapping;  // 当前手势,是否为双击手势
         private  float  mLastMotionY;  // 最后一次事件的X坐标
         private  float  mLastMotionX;  // 最后一次事件的Y坐标
         
         private  EventInfo(MotionEvent e)
         {
             this ( new  MultiMotionEvent(e));
         }
         
         private  EventInfo(MultiMotionEvent me)
         {
             mCurrentDownEvent = me;
             mStillDown =  true ;
             mInLongPress =  false ;
             mAlwaysInTapRegion =  true ;
             mAlwaysInBiggerTapRegion =  true ;
             mIsDoubleTapping =  false ;
         }
         
         // 释放MotionEven对象,使系统能够继续使用它们
         public  void  recycle()
         {
             if  (mCurrentDownEvent !=  null )
             {
                 mCurrentDownEvent.recycle();
                 mCurrentDownEvent =  null ;
             }
             if  (mPreviousUpEvent !=  null )
             {
                 mPreviousUpEvent.recycle();
                 mPreviousUpEvent =  null ;
             }
         }
         
         @Override
         public  void  finalize()
         {
             this .recycle();
         }
     }
     
     /**
      * 多点事件类 <br/>
      * 将一个多点事件拆分为多个单点事件,并方便获得事件的绝对坐标 <br/>
      * 绝对坐标用以在界面中找到触点所在的控件
     
      * @author ray-ni
      */
     public  class  MultiMotionEvent
     {
         private  MotionEvent mEvent;
         private  int  mIndex;
         
         private  MultiMotionEvent(MotionEvent e)
         {
             mEvent = e;
             mIndex = (e.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;  // 等效于
                                                                                                                   // mEvent.getActionIndex();
         }
         
         private  MultiMotionEvent(MotionEvent e,  int  idx)
         {
             mEvent = e;
             mIndex = idx;
         }
         
         // 行为
         public  int  getAction()
         {
             int  action = mEvent.getAction() & MotionEvent.ACTION_MASK;  // 等效于
                                                                        // mEvent.getActionMasked();
             switch  (action)
             {
                 case  MotionEvent.ACTION_POINTER_DOWN:
                     action = MotionEvent.ACTION_DOWN;
                     break ;
                 case  MotionEvent.ACTION_POINTER_UP:
                     action = MotionEvent.ACTION_UP;
                     break ;
             }
             return  action;
         }
         
         // 返回X的绝对坐标
         public  float  getX()
         {
             return  mEvent.getX(mIndex) + mEvent.getRawX() - mEvent.getX();
         }
         
         // 返回Y的绝对坐标
         public  float  getY()
         {
             return  mEvent.getY(mIndex) + mEvent.getRawY() - mEvent.getY();
         }
         
         // 事件发生的时间
         public  long  getEventTime()
         {
             return  mEvent.getEventTime();
         }
         
         // 事件序号
         public  int  getIndex()
         {
             return  mIndex;
         }
         
         // 事件ID
         public  int  getId()
         {
             return  mEvent.getPointerId(mIndex);
         }
         
         // 释放事件对象,使系统能够继续使用
         public  void  recycle()
         {
             if  (mEvent !=  null )
             {
                 mEvent.recycle();
                 mEvent =  null ;
             }
         }
     }
     
     // 多点手势监听器
     public  interface  MultiTouchGestureListener
     {
         // 手指触碰到屏幕,由一个 ACTION_DOWN触发
         boolean  onDown(MultiMotionEvent e);
         
         // 确定一个press事件,强调手指按下的一段时间(TAP_TIMEOUT)内,手指未曾移动或抬起
         void  onShowPress(MultiMotionEvent e);
         
         // 手指点击屏幕后离开,由 ACTION_UP引发,可以简单的理解为单击事件,即手指点击时间不长(未构成长按事件),也不曾移动过
         boolean  onSingleTapUp(MultiMotionEvent e);
         
         // 长按,手指点下后一段时间(DOUBLE_TAP_TIMEOUT)内,不曾抬起或移动
         void  onLongPress(MultiMotionEvent e);
         
         // 拖动,由ACTION_MOVE触发,手指地按下后,在屏幕上移动
         boolean  onScroll(MultiMotionEvent e1, MultiMotionEvent e2,  float  distanceX,  float  distanceY);
         
         // 滑动,由ACTION_UP触发,手指按下并移动一段距离后,抬起时触发
         boolean  onFling(MultiMotionEvent e1, MultiMotionEvent e2,  float  velocityX,  float  velocityY);
     }
     
     // 多点双击监听器
     public  interface  MultiTouchDoubleTapListener
     {
         // 单击事件确认,强调第一个单击事件发生后,一段时间内,未发生第二次单击事件,即确定不会触发双击事件
         boolean  onSingleTapConfirmed(MultiMotionEvent e);
         
         // 双击事件,
         // 由ACTION_DOWN触发,从第一次单击事件的DOWN事件开始的一段时间(DOUBLE_TAP_TIMEOUT)内结束(即手指),
         // 并且在第一次单击事件的UP时间开始后的一段时间内(DOUBLE_TAP_TIMEOUT)发生第二次单击事件,
         // 除此之外两者坐标间距小于定值(DOUBLE_TAP_SLAP)时,则触发双击事件
         boolean  onDoubleTap(MultiMotionEvent e);
         
         // 双击事件,与onDoubleTap事件不同之处在于,构成双击的第二次点击的ACTION_DOWN,ACTION_MOVE和ACTION_UP都会触发该事件
         boolean  onDoubleTapEvent(MultiMotionEvent e);
     }
     
     // 事件信息队列,队列的下标与MotionEvent的pointId对应
     private  static  List<EventInfo> sEventInfos =  new  ArrayList<EventInfo>( 10 );
     // 双击判断队列,这个队列中的元素等待双击超时的判断结果
     private  static  List<EventInfo> sEventForDoubleTap =  new  ArrayList<EventInfo>( 5 );
     // 指定大点击区域的大小(这个比较拗口),这个值主要用于帮助判断双击是否成立
     private  int  mBiggerTouchSlopSquare =  20  20 ;
     // 判断是否构成onScroll手势,当手指在这个范围内移动时,不触发onScroll手势
     private  int  mTouchSlopSquare;
     // 判断是否构成双击,只有两次点击的距离小于该值,才能构成双击手势
     private  int  mDoubleTapSlopSquare;
     // 最小滑动速度
     private  int  mMinimumFlingVelocity;
     // 最大滑动速度
     private  int  mMaximumFlingVelocity;
     
     // 长按阀值,当手指按下后,在该阀值的时间内,未移动超过mTouchSlopSquare的距离并未抬起,则长按手势触发
     private  static  final  int  LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
     // showPress手势的触发阀值,当手指按下后,在该阀值的时间内,未移动超过mTouchSlopSquare的距离并未抬起,则showPress手势触发
     private  static  final  int  TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
     // 双击超时阀值,仅在两次双击事件的间隔(第一次单击的UP事件和第二次单击的DOWN事件)小于此阀值,双击事件才能成立
     private  static  final  int  DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
     // 双击区域阀值,仅在两次双击事件的距离小于此阀值,双击事件才能成立
     private  static  final  int  DOUBLE_TAP_SLAP =  64 ;
     
     // GestureHandler所处理的Message的what属性可能为以下 常量:
     // showPress手势
     private  static  final  int  SHOW_PRESS =  1 ;
     // 长按手势
     private  static  final  int  LONG_PRESS =  2 ;
     // SingleTapConfirmed手势
     private  static  final  int  TAP_SINGLE =  3 ;
     // 双击手势
     private  static  final  int  TAP_DOUBLE =  4 ;
     
     // 手势处理器
     private  final  GestureHandler mHandler;
     // 手势监听器
     private  final  MultiTouchGestureListener mListener;
     // 双击监听器
     private  MultiTouchDoubleTapListener mDoubleTapListener;
     
     // 长按允许阀值
     private  boolean  mIsLongpressEnabled;
     // 速度追踪器
     private  VelocityTracker mVelocityTracker;
     
     private  class  GestureHandler  extends  Handler
     {
         GestureHandler()
         {
             super ();
         }
         
         GestureHandler(Handler handler)
         {
             super (handler.getLooper());
         }
         
         @Override
         public  void  handleMessage(Message msg)
         {
             int  idx = (Integer) msg.obj;
             switch  (msg.what)
             {
                 case  SHOW_PRESS:
                 {
                     if  (idx >= sEventInfos.size())
                     {
                         // Log.w(MYTAG, CLASS_NAME +
                         // ":handleMessage, msg.what = SHOW_PRESS, idx=" + idx +
                         // ", while sEventInfos.size()="
                         // + sEventInfos.size());
                         break ;
                     }
                     EventInfo info = sEventInfos.get(idx);
                     if  (info ==  null )
                     {
                         // Log.e(MYTAG, CLASS_NAME +
                         // ":handleMessage, msg.what = SHOW_PRESS, idx=" + idx +
                         // ", Info = null");
                         break ;
                     }
                     // 触发手势监听器的onShowPress事件
                     mListener.onShowPress(info.mCurrentDownEvent);
                     break ;
                 }
                 case  LONG_PRESS:
                 {
                     // Log.d(MYTAG, CLASS_NAME + ":trigger LONG_PRESS");
                     
                     if  (idx >= sEventInfos.size())
                     {
                         // Log.w(MYTAG, CLASS_NAME +
                         // ":handleMessage, msg.what = LONG_PRESS, idx=" + idx +
                         // ", while sEventInfos.size()="
                         // + sEventInfos.size());
                         break ;
                     }
                     EventInfo info = sEventInfos.get(idx);
                     if  (info ==  null )
                     {
                         // Log.e(MYTAG, CLASS_NAME +
                         // ":handleMessage, msg.what = LONG_PRESS, idx=" + idx +
                         // ", Info = null");
                         break ;
                     }
                     dispatchLongPress(info, idx);
                     break ;
                 }
                 case  TAP_SINGLE:
                 {
                     // Log.d(MYTAG, CLASS_NAME + ":trriger TAP_SINGLE");
                     // If the user's finger is still down, do not count it as a
                     // tap
                     if  (idx >= sEventInfos.size())
                     {
                         // Log.e(MYTAG, CLASS_NAME +
                         // ":handleMessage, msg.what = TAP_SINGLE, idx=" + idx +
                         // ", while sEventInfos.size()="
                         // + sEventInfos.size());
                         break ;
                     }
                     EventInfo info = sEventInfos.get(idx);
                     if  (info ==  null )
                     {
                         // Log.e(MYTAG, CLASS_NAME +
                         // ":handleMessage, msg.what = TAP_SINGLE, idx=" + idx +
                         // ", Info = null");
                         break ;
                     }
                     if  (mDoubleTapListener !=  null  && !info.mStillDown)
                     // 手指在双击超时的阀值内未离开屏幕进行第二次单击事件,则确定单击事件成立(不再触发双击事件)
                         mDoubleTapListener.onSingleTapConfirmed(info.mCurrentDownEvent);
                     }
                     break ;
                 }
                 case  TAP_DOUBLE:
                 {
                     if  (idx >= sEventForDoubleTap.size())
                     {
                         // Log.w(MYTAG, CLASS_NAME +
                         // ":handleMessage, msg.what = TAP_DOUBLE, idx=" + idx +
                         // ", while sEventForDoubleTap.size()="
                         // + sEventForDoubleTap.size());
                         break ;
                     }
                     EventInfo info = sEventForDoubleTap.get(idx);
                     if  (info ==  null )
                     {
                         // Log.w(MYTAG, CLASS_NAME +
                         // ":handleMessage, msg.what = TAP_DOUBLE, idx=" + idx +
                         // ", Info = null");
                         break ;
                     }
                     sEventForDoubleTap.set(idx,  null ); // 这个没什么好做的,就是把队列中对应的元素清除而已
                     break ;
                 }
                 default :
                     throw  new  RuntimeException( "Unknown message "  + msg);  // never
             }
         }
     }
     
     /**
      * 触发长按事件
     
      * @param info
      * @param idx
      */
     private  void  dispatchLongPress(EventInfo info,  int  idx)
     {
         mHandler.removeMessages(TAP_SINGLE, idx); // 移除单击事件确认
         info.mInLongPress =  true ;
         mListener.onLongPress(info.mCurrentDownEvent);
     }
     
     /**
      * 构造器1
     
      * @param context
      * @param listener
      */
     public  MultiTouchGestureDetector(Context context, MultiTouchGestureListener listener)
     {
         this (context, listener,  null );
     }
     
     /**
      * 构造器2
     
      * @param context
      * @param listener
      * @param handler
      */
     public  MultiTouchGestureDetector(Context context, MultiTouchGestureListener listener, Handler handler)
     {
         if  (handler !=  null )
         {
             mHandler =  new  GestureHandler(handler);
         }
         else
         {
             mHandler =  new  GestureHandler();
         }
         mListener = listener;
         if  (listener  instanceof  MultiTouchDoubleTapListener)
         {
             setOnDoubleTapListener((MultiTouchDoubleTapListener) listener);
         }
         init(context);
     }
     
     /**
      * 初始化识别器
     
      * @param context
      */
     private  void  init(Context context)
     {
         if  (mListener ==  null )
         {
             throw  new  NullPointerException( "OnGestureListener must not be null" );
         }
         mIsLongpressEnabled =  true ;
         int  touchSlop, doubleTapSlop;
         if  (context ==  null )
         {
             touchSlop = ViewConfiguration.getTouchSlop();
             doubleTapSlop = DOUBLE_TAP_SLAP;
             mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
             mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
         }
         else
         { // 允许识别器在App中,使用偏好的设定
             final  ViewConfiguration configuration = ViewConfiguration.get(context);
             touchSlop = configuration.getScaledTouchSlop();
             doubleTapSlop = configuration.getScaledDoubleTapSlop();
             mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
             mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
         }
         mTouchSlopSquare = touchSlop * touchSlop /  16 ;
         mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
     }
     
     /**
      * 设置双击监听器
     
      * @param onDoubleTapListener
      */
     public  void  setOnDoubleTapListener(MultiTouchDoubleTapListener onDoubleTapListener)
     {
         mDoubleTapListener = onDoubleTapListener;
     }
     
     /**
      * 设置是否允许长按
     
      * @param isLongpressEnabled
      */
     public  void  setIsLongpressEnabled( boolean  isLongpressEnabled)
     {
         mIsLongpressEnabled = isLongpressEnabled;
     }
     
     /**
      * 判断是否允许长按
     
      * @return
      */
     public  boolean  isLongpressEnabled()
     {
         return  mIsLongpressEnabled;
     }
     
     /**
      * 判断当前事件是否为双击事件 <br/>
      * 通过遍历sEventForDoubleTap来匹配是否存在能够构成双击事件的单击事件
     
      * @param e
      * @return
      */
     private  EventInfo checkForDoubleTap(MultiMotionEvent e)
     {
         if  (sEventForDoubleTap.isEmpty())
         {
             // Log.e(MYTAG, CLASS_NAME +
             // ":checkForDoubleTap(), sEventForDoubleTap is empty !");
             return  null ;
         }
         for  ( int  i =  0 ; i < sEventForDoubleTap.size(); i++)
         {
             EventInfo info = sEventForDoubleTap.get(i);
             if  (info !=  null  && isConsideredDoubleTap(info, e))
             {
                 sEventForDoubleTap.set(i,  null ); // 这个单击事件已经被消耗了,所以置为null
                 mHandler.removeMessages(TAP_DOUBLE, i); // 移除Handler内的为处理消息
                 return  info;
             }
         }
         return  null ;
     }
     
     /**
      * 判断当前按下事件是否能和指定的单击事件构成双击事件
     
      * @param info
      * @param secondDown
      * @return
      */
     private  boolean  isConsideredDoubleTap(EventInfo info, MultiMotionEvent secondDown)
     {
         if  (!info.mAlwaysInBiggerTapRegion)
         // 如多第一次单击事件有过较大距离的移动,则无法构成双击事件
             return  false ;
         }
         if  (secondDown.getEventTime() - info.mPreviousUpEvent.getEventTime() > DOUBLE_TAP_TIMEOUT)
         {
             // 如果第一次单击的UP时间和第二次单击的down时间时间间隔大于DOUBLE_TAP_TIMEOUT,也无法构成双击事件
             return  false ;
         }
         int  deltaX = ( int ) info.mCurrentDownEvent.getX() - ( int ) secondDown.getX();
         int  deltaY = ( int ) info.mCurrentDownEvent.getY() - ( int ) secondDown.getY();
         return  (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare); // 最后判断两次单击事件的距离
     }
     
     /**
      * 将事件信息放入双击判断队列,并返回序号
     
      * @param info
      * @return
      */
     private  int  addIntoTheMinIndex(EventInfo info)
     {
         for  ( int  i =  0 ; i < sEventForDoubleTap.size(); i++)
         {
             if  (sEventForDoubleTap.get(i) ==  null )
             {
                 sEventForDoubleTap.set(i, info);
                 return  i;
             }
         }
         sEventForDoubleTap.add(info);
         return  sEventForDoubleTap.size() -  1 ;
     }
     
     /**
      * 从事件信息队列中移除指定序号的事件
     
      * @param idx
      */
     private  void  removeEventFromList( int  id)
     {
         if  (id > sEventInfos.size() || id <  0 )
         {
             // Log.e(MYTAG, CLASS_NAME + ".removeEventFromList(), id=" + id +
             // ", while sEventInfos.size() =" +
             // sEventInfos.size());
             return ;
         }
         sEventInfos.set(id,  null );
     }
     
     /**
      * 向事件队列中添加新信息
     
      * @param e
      */
     private  void  addEventIntoList(EventInfo info)
     {
         int  id = info.mCurrentDownEvent.getId();
         if  (id < sEventInfos.size())
         {
             // if (sEventInfos.get(id) != null)
             // Log.e(MYTAG, CLASS_NAME + ".addEventIntoList, info(" + id +
             // ") has not set to null !");
             sEventInfos.set(info.mCurrentDownEvent.getId(), info);
         }
         else  if  (id == sEventInfos.size())
         {
             sEventInfos.add(info);
         }
         else
         {
             // Log.e(MYTAG, CLASS_NAME + ".addEventIntoList, invalidata id !");
         }
     }
     
     public  boolean  onTouchEvent(MotionEvent ev)
     {
         if  (mVelocityTracker ==  null )
         {
             mVelocityTracker = VelocityTracker.obtain();
         }
         mVelocityTracker.addMovement(ev); // 把所有事件都添加到速度追踪器,为计算速度做准备
         boolean  handled =  false ;
         final  int  action = ev.getAction();  // 获取Action
         // int idx = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
         // MotionEvent.ACTION_POINTER_INDEX_SHIFT;//获取触摸事件的序号
         int  idx = ev.getPointerId(ev.getActionIndex()); // 获取触摸事件的id
         switch  (action & MotionEvent.ACTION_MASK)
         {
             case  MotionEvent.ACTION_DOWN:
             case  MotionEvent.ACTION_POINTER_DOWN:
             {
                 EventInfo info =  new  EventInfo(MotionEvent.obtain(ev));
                 this .addEventIntoList(info); // 将手势信息保存到队列中
                 if  (mDoubleTapListener !=  null )
                 { // 如果双击监听器不为null
                     if  (mHandler.hasMessages(TAP_DOUBLE))
                     {
                         MultiMotionEvent e =  new  MultiMotionEvent(ev);
                         EventInfo origInfo = checkForDoubleTap(e); // 检查是否构成双击事件
                         if  (origInfo !=  null )
                         {
                             info.mIsDoubleTapping =  true ;
                             handled |= mDoubleTapListener.onDoubleTap(origInfo.mCurrentDownEvent);
                             handled |= mDoubleTapListener.onDoubleTapEvent(e);
                         }
                     }
                     if  (!info.mIsDoubleTapping)
                     { // 当前事件不构成双击事件,那么发送延迟消息以判断onSingleTapConfirmed事件
                         mHandler.sendMessageDelayed(mHandler.obtainMessage(TAP_SINGLE, idx), DOUBLE_TAP_TIMEOUT);
                         // Log.d(MYTAG, CLASS_NAME + ": add TAP_SINGLE");
                     }
                 }
                 // 记录X坐标和Y坐标
                 info.mLastMotionX = info.mCurrentDownEvent.getX();
                 info.mLastMotionY = info.mCurrentDownEvent.getY();
                 
                 if  (mIsLongpressEnabled)
                 { // 允许长按
                     mHandler.removeMessages(LONG_PRESS, idx);
                     mHandler.sendMessageAtTime(mHandler.obtainMessage(LONG_PRESS, idx), info.mCurrentDownEvent.getEventTime() + TAP_TIMEOUT + LONGPRESS_TIMEOUT); // 延时消息以触发长按手势
                     // Log.d(MYTAG, CLASS_NAME +
                     // ":add LONG_PRESS to handler  for idx " + idx);
                 }
                 mHandler.sendMessageAtTime(mHandler.obtainMessage(SHOW_PRESS, idx), info.mCurrentDownEvent.getEventTime() + TAP_TIMEOUT); // 延时消息,触发showPress手势
                 handled |= mListener.onDown(info.mCurrentDownEvent); // 触发onDown()
                 break ;
             }
             case  MotionEvent.ACTION_UP:
             case  MotionEvent.ACTION_POINTER_UP:
             {
                 MultiMotionEvent currentUpEvent =  new  MultiMotionEvent(ev);
                 if  (idx >= sEventInfos.size())
                 {
                     // Log.e(MYTAG, CLASS_NAME + ":ACTION_POINTER_UP, idx=" +
                     // idx + ", while sEventInfos.size()=" +
                     // sEventInfos.size());
                     break ;
                 }
                 EventInfo info = sEventInfos.get(currentUpEvent.getId());
                 if  (info ==  null )
                 {
                     // Log.e(MYTAG, CLASS_NAME + ":ACTION_POINTER_UP, idx=" +
                     // idx + ", Info = null");
                     break ;
                 }
                 info.mStillDown =  false ;
                 if  (info.mIsDoubleTapping)
                 // 处于双击状态,则触发onDoubleTapEvent事件
                     handled |= mDoubleTapListener.onDoubleTapEvent(currentUpEvent);
                 }
                 else  if  (info.mInLongPress)
                 { // 处于长按状态
                     mHandler.removeMessages(TAP_SINGLE, idx); // 可以无视这行代码
                     info.mInLongPress =  false ;
                 }
                 else  if  (info.mAlwaysInTapRegion)
                 { // 尚未移动过
                     if  (mHandler.hasMessages(TAP_SINGLE, idx))
                     { // 还在双击的时间阀值内,所以要为双击判断做额外处理
                         mHandler.removeMessages(TAP_SINGLE, idx);
                         info.mPreviousUpEvent =  new  MultiMotionEvent(MotionEvent.obtain(ev));
                         int  index =  this .addIntoTheMinIndex(info); // 把当前事件放入队列,等待双击的判断
                         mHandler.sendMessageAtTime(mHandler.obtainMessage(TAP_DOUBLE, index), info.mCurrentDownEvent.getEventTime() + DOUBLE_TAP_TIMEOUT);  // 将双击超时判断添加到Handler
                         // Log.d(MYTAG, CLASS_NAME + ": add TAP_DOUBLE");
                     }
                     handled = mListener.onSingleTapUp(currentUpEvent); // 触发onSingleTapUp事件
                 }
                 else
                 {
                     // A fling must travel the minimum tap distance
                     final  VelocityTracker velocityTracker = mVelocityTracker;
                     velocityTracker.computeCurrentVelocity( 1000 , mMaximumFlingVelocity); // 计算1秒钟内的滑动速度
                     // 获取X和Y方向的速度
                     final  float  velocityX = velocityTracker.getXVelocity(idx);
                     final  float  velocityY = velocityTracker.getYVelocity(idx);
                     // Log.i(MYTAG, CLASS_NAME + ":ACTION_POINTER_UP, idx=" +
                     // idx +
                     // ", vx=" + velocityX + ", vy=" + velocityY);
                     // 触发滑动事件
                     if  ((Math.abs(velocityY) > mMinimumFlingVelocity) || (Math.abs(velocityX) > mMinimumFlingVelocity))
                     {
                         handled = mListener.onFling(info.mCurrentDownEvent, currentUpEvent, velocityX, velocityY);
                     }
                 }
                 // Hold the event we obtained above - listeners may have changed
                 // the
                 // original.
                 if  (action == MotionEvent.ACTION_UP)
                 // 释放速度追踪器
                     mVelocityTracker.recycle();
                     mVelocityTracker =  null ;
                     // Log.w(MYTAG, CLASS_NAME +
                     // ":ACTION_POINTER_UP, mVelocityTracker.recycle()");
                 }
                 
                 info.mIsDoubleTapping =  false ;
                 // Log.d(MYTAG, CLASS_NAME + "remove LONG_PRESS");
                 // 移除showPress和长按消息
                 mHandler.removeMessages(SHOW_PRESS, idx);
                 mHandler.removeMessages(LONG_PRESS, idx);
                 removeEventFromList(currentUpEvent.getId()); // 手指离开,则从队列中删除手势信息
                 break ;
             }
             case  MotionEvent.ACTION_MOVE:
                 for  ( int  rIdx =  0 ; rIdx < ev.getPointerCount(); rIdx++)
                 { // 因为无法确定当前发生移动的是哪个手指,所以遍历处理所有手指
                     MultiMotionEvent e =  new  MultiMotionEvent(ev, rIdx);
                     if  (e.getId() >= sEventInfos.size())
                     {
                         // Log.e(MYTAG, CLASS_NAME + ":ACTION_MOVE, idx=" + rIdx
                         // + ", while sEventInfos.size()=" +
                         // sEventInfos.size());
                         break ;
                     }
                     EventInfo info = sEventInfos.get(e.getId());
                     if  (info ==  null )
                     {
                         // Log.e(MYTAG, CLASS_NAME + ":ACTION_MOVE, idx=" + rIdx
                         // + ", Info = null");
                         break ;
                     }
                     if  (info.mInLongPress)
                     // 长按,则不处理move事件
                         break ;
                     }
                     // 当前坐标
                     float  x = e.getX();
                     float  y = e.getY();
                     // 距离上次事件移动的位置
                     final  float  scrollX = x - info.mLastMotionX;
                     final  float  scrollY = y - info.mLastMotionY;
                     if  (info.mIsDoubleTapping)
                     { // 双击事件
                         handled |= mDoubleTapListener.onDoubleTapEvent(e);
                     }
                     else  if  (info.mAlwaysInTapRegion)
                     { // 该手势尚未移动过(移动的距离小于mTouchSlopSquare,视为未移动过)
                         // 计算从落下到当前事件,移动的距离
                         final  int  deltaX = ( int ) (x - info.mCurrentDownEvent.getX());
                         final  int  deltaY = ( int ) (y - info.mCurrentDownEvent.getY());
                         // Log.d(MYTAG, CLASS_NAME + "deltaX="+deltaX+";deltaY="
                         // +
                         // deltaX +"mTouchSlopSquare=" + mTouchSlopSquare);
                         int  distance = (deltaX * deltaX) + (deltaY * deltaY);
                         if  (distance > mTouchSlopSquare)
                         // 移动距离超过mTouchSlopSquare
                             handled = mListener.onScroll(info.mCurrentDownEvent, e, scrollX, scrollY);
                             info.mLastMotionX = e.getX();
                             info.mLastMotionY = e.getY();
                             info.mAlwaysInTapRegion =  false ;
                             // Log.d(MYTAG, CLASS_NAME +
                             // ":remove LONG_PRESS for idx" + rIdx +
                             // ",mTouchSlopSquare("+mTouchSlopSquare+"), distance("+distance+")");
                             // 清除onSingleTapConform,showPress,longPress三种消息
                             int  id = e.getId();
                             mHandler.removeMessages(TAP_SINGLE, id);
                             mHandler.removeMessages(SHOW_PRESS, id);
                             mHandler.removeMessages(LONG_PRESS, id);
                         }
                         if  (distance > mBiggerTouchSlopSquare)
                         { // 移动距离大于mBiggerTouchSlopSquare,则无法构成双击事件
                             info.mAlwaysInBiggerTapRegion =  false ;
                         }
                     }
                     else  if  ((Math.abs(scrollX) >=  1 ) || (Math.abs(scrollY) >=  1 ))
                     { // 之前已经移动过了
                         handled = mListener.onScroll(info.mCurrentDownEvent, e, scrollX, scrollY);
                         info.mLastMotionX = x;
                         info.mLastMotionY = y;
                     }
                 }
                 break ;
             case  MotionEvent.ACTION_CANCEL:
                 cancel(); // 清理
         }
         return  handled;
     }
     
     // 清理所有队列
     private  void  cancel()
     {
         mHandler.removeMessages(SHOW_PRESS);
         mHandler.removeMessages(LONG_PRESS);
         mHandler.removeMessages(TAP_SINGLE);
         mVelocityTracker.recycle();
         mVelocityTracker =  null ;
         sEventInfos.clear();
         sEventForDoubleTap.clear();
     }
}




本文转自 glblong 51CTO博客,原文链接:http://blog.51cto.com/glblong/1569058,如需转载请自行联系原作者

目录
相关文章
|
20天前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
28天前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
53 15
Android 系统缓存扫描与清理方法分析
|
1月前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
75 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
1月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
90 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
1月前
|
存储 Linux Android开发
Android底层:通熟易懂分析binder:1.binder准备工作
本文详细介绍了Android Binder机制的准备工作,包括打开Binder驱动、内存映射(mmap)、启动Binder主线程等内容。通过分析系统调用和进程与驱动层的通信,解释了Binder如何实现进程间通信。文章还探讨了Binder主线程的启动流程及其在进程通信中的作用,最后总结了Binder准备工作的调用时机和重要性。
Android底层:通熟易懂分析binder:1.binder准备工作
|
2月前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
141 3
|
1月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境的差异性分析
【10月更文挑战第8天】 本文旨在探讨Android和iOS两大移动操作系统在开发环境上的不同,包括开发语言、工具、平台特性等方面。通过对这些差异性的分析,帮助开发者更好地理解两大平台,以便在项目开发中做出更合适的技术选择。
|
6天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
11天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
13天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。