本文要解決在側滑菜單右邊加個文本框,并能實現文本的上下滑動和菜單的左右滾動。這里推薦可以好好看看android的觸摸事件的分發機制,這里我就不詳細講了,我只講講這個應用。要實現的功能就像uc瀏覽器(或其它手機瀏覽器)的左右滾動,切換網頁,上下滾動,拖動內容。
本文的效果:
一、功能要求與實現
1、功能要求:
(1)手指一開始按著屏幕左右移動時,只能左右滾動菜單,如果這時手指一直按著,而且上下移動了,那么菜單顯示部分保持不變,但文本框也不上下移動!
(2)手指一開始按著屏幕上下移動時,只能上下滾動文本框,如果這時手指一直按著,而且左右移動了,那么文本框顯示部分保持不變,但菜單也不左右移動!
2、初步實現:
左邊的菜單項增加一個listview,為右邊的內容項添加一個textview,并且為了能讓它實現上下滾動的功能,給textview加了個scrollview。
這種效果肯定是不對的,你看,我們手指上下禾移動文本時,如果還左右移動了,菜單也顯示出來了。
3、修改實現
這時我就想從觸摸事件的分發入手,這里因為我是把scrollview的觸摸事件注冊到linearlayout。(linearlayout中包含了scrollview,不懂看下面的布局)中去,所以觸摸事件會先傳遞給linearlayout。
分以下兩種情況:
(1)如果是手指左右移動,則把觸摸事件傳給linearlayout。函數ontouch返回true,表示觸摸事件不再傳遞下去,那么scrollview就動不了了
(2)如果是手指上下移動,觸摸事件先傳給linearlayout,但linearlayout不做任何處理,直接傳遞給scrollview,scrollview來處理觸摸事件。
這是修改后的效果:
二、布局與代碼
1、布局
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
|
<linearlayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http://schemas.android.com/tools" android:id= "@+id/layout" android:layout_width= "match_parent" android:layout_height= "match_parent" android:orientation= "horizontal" tools:context= ".mainactivity" > <linearlayout android:id= "@+id/menu" android:layout_width= "match_parent" android:layout_height= "match_parent" android:orientation= "vertical" android:background= "@drawable/menu" > <!-- 添加一個listview控件 --> <listview android:id= "@+id/menulist" android:layout_width= "fill_parent" android:layout_height= "fill_parent" /> </linearlayout> <linearlayout android:id= "@+id/content" android:layout_width= "match_parent" android:layout_height= "match_parent" android:orientation= "vertical" > <scrollview android:id= "@+id/scrollview" android:layout_width= "fill_parent" android:layout_height= "wrap_content" > <textview android:id= "@+id/content_text" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:text= "@string/text1" android:textsize= "22px" /> </scrollview> </linearlayout> </linearlayout> |
2、代碼
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
|
package com.example.learningjava; import java.util.arraylist; import java.util.hashmap; import java.util.map; import com.example.learningjava.r.string; import android.r.integer; import android.r.menu; import android.os.asynctask; import android.os.build; import android.os.bundle; import android.annotation.suppresslint; import android.annotation.targetapi; import android.widget.adapterview; import android.widget.adapterview.onitemclicklistener; import android.widget.arrayadapter; import android.widget.linearlayout.layoutparams; import android.widget.listview; import android.widget.scrollview; import android.widget.toast; import android.app.activity; import android.content.context; import android.util.attributeset; import android.util.displaymetrics; import android.util.log; import android.view.gesturedetector; import android.view.menu; import android.view.motionevent; import android.view.velocitytracker; import android.view.view; import android.view.view.ontouchlistener; import android.view.window; import android.widget.linearlayout; public class mainactivity extends activity implements ontouchlistener{ private linearlayout menulayout; //菜單項 private linearlayout contentlayout; //內容項 private layoutparams menuparams; //菜單項目的參數 private layoutparams contentparams; //內容項目的參數contentlayout的寬度值 private int displaywidth; //手機屏幕分辨率 private float xdown; //手指點下去的橫坐標 private float xmove; //手指移動的橫坐標 private float xup; //記錄手指上抬后的橫坐標 private float ydown; //手指點下去的縱坐標 private float ymove; //手指移動的縱坐標 private velocitytracker mvelocitytracker; // 用于計算手指滑動的速度。 private float velocityx; //手指左右移動的速度 public static final int snap_velocity = 400 ; //滾動顯示和隱藏menu時,手指滑動需要達到的速度。 private boolean menuisshow = false ; //初始化菜單項不可翙 private static final int menupadding= 160 ; //menu完成顯示,留給content的寬度 private listview menulistview; //菜單列表的內容 private scrollview scrollview; // 文本框的滾動條 private boolean wanttoscrolltext= false ; //想要下下滾動文本內容 private boolean wanttoscrolltextmenu= false ; private boolean onefucction= false ; //確保函數只被調用一次 protected void oncreate(bundle savedinstancestate) { super .oncreate(savedinstancestate); requestwindowfeature(window.feature_no_title); setcontentview(r.layout.activity_main); initlayoutparams(); initmenulist(); initscrollview(); } /** *初始化layout并設置其相應的參數 */ private void initlayoutparams() { //得到屏幕的大小 displaymetrics dm = new displaymetrics(); getwindowmanager().getdefaultdisplay().getmetrics(dm); displaywidth =dm.widthpixels; //獲得控件 menulayout = (linearlayout) findviewbyid(r.id.menu); contentlayout = (linearlayout) findviewbyid(r.id.content); findviewbyid(r.id.layout).setontouchlistener( this ); //獲得控件參數 menuparams=(linearlayout.layoutparams)menulayout.getlayoutparams(); contentparams = (linearlayout.layoutparams) contentlayout.getlayoutparams(); //初始化菜單和內容的寬和邊距 menuparams.width = displaywidth - menupadding; menuparams.leftmargin = 0 - menuparams.width; contentparams.width = displaywidth; contentparams.leftmargin= 0 ; //設置參數 menulayout.setlayoutparams(menuparams); contentlayout.setlayoutparams(contentparams); } /** * 初始化菜單列表內容 */ private void initmenulist() { final string[] strs = new string[] { "第1章 java概述 " , "第2章 理解面向對象" , "第3章 數據類型和運算符" , "第4章 流程控制和數組" , "第5章 面向對象(上)" }; menulistview = (listview) findviewbyid(r.id.menulist); menulistview.setadapter( new arrayadapter<string>( this ,android.r.layout.simple_list_item_1, strs)); //為listview綁定適配器 //啟動列表點擊監聽事件 menulistview.setonitemclicklistener( new onitemclicklistener() { @override public void onitemclick(adapterview<?> arg0, view arg1, int arg2, long arg3) { toast.maketext(getapplicationcontext(), "您選擇了" + strs[arg2], toast.length_short).show(); } }); } /** * 初始化scrollview */ public void initscrollview(){ scrollview = (scrollview) this .findviewbyid(r.id.scrollview); scrollview.setontouchlistener( this ); //綁定監聽側滑事件的view,即在綁定的view進行滑動才可以顯示和隱藏左側布局。 這句非常重要,不要設置它的觸摸事件 了,要不會吞掉布局的觸摸事件 } @override public boolean ontouch(view v, motionevent event) { acquirevelocitytracker(event); if (event.getaction()==motionevent.action_down) { xdown=event.getrawx(); ydown=event.getrawy(); return false ; } else if (event.getaction()==motionevent.action_move) { if (wanttoscrolltext) //當前想滾動顯示文本 return false ; xmove=event.getrawx(); ymove=event.getrawy(); if (menuisshow){ isscrolltoshowmenu(); return true ; } if (!onefucction) { onefucction= true ; //這個if只能被調用一次 if (math.abs(xdown-xmove)<math.abs(ydown-ymove)) { wanttoscrolltext= true ; return false ; } } isscrolltoshowmenu(); } else if (event.getaction()==motionevent.action_up) { onefucction= false ; if (wanttoscrolltext){ wanttoscrolltext= false ; return false ; } xup=event.getrawx(); isshowmenu(); releasevelocitytracker(); } else if (event.getaction()==motionevent.action_cancel) { releasevelocitytracker(); return false ; } return true ; //false時才能把觸摸事件再傳給scroll } /** * 根據手指按下的距離,判斷是否滾動顯示菜單 */ private void isscrolltoshowmenu() { int distancex = ( int ) (xmove - xdown); if (!menuisshow) { scrolltoshowmenu(distancex); } else { scrolltohidemenu(distancex); } } /** * 手指抬起之后判斷是否要顯示菜單 */ private void isshowmenu() { velocityx =getscrollvelocity(); if (wanttoshowmenu()){ if (shouldshowmenu()){ showmenu(); } else { hidemenu(); } } else if (wanttohidemenu()){ if (shouldhidemenu()){ hidemenu(); } else { showmenu(); } } } /** *想要顯示菜單,當向右移動距離大于0并且菜單不可見 */ private boolean wanttoshowmenu(){ return !menuisshow&&xup-xdown> 0 ; } /** *想要隱藏菜單,當向左移動距離大于0并且菜單可見 */ private boolean wanttohidemenu(){ return menuisshow&&xdown-xup> 0 ; } /** *判斷應該顯示菜單,當向右移動的距離超過菜單的一半或者速度超過給定值 */ private boolean shouldshowmenu(){ return xup-xdown>menuparams.width/ 2 ||velocityx>snap_velocity; } /** *判斷應該隱藏菜單,當向左移動的距離超過菜單的一半或者速度超過給定值 */ private boolean shouldhidemenu(){ return xdown-xup>menuparams.width/ 2 ||velocityx>snap_velocity; } /** * 顯示菜單欄 */ private void showmenu() { new showmenuasynctask().execute( 50 ); menuisshow= true ; } /** * 隱藏菜單欄 */ private void hidemenu() { new showmenuasynctask().execute(- 50 ); menuisshow= false ; } /** *指針按著時,滾動將菜單慢慢顯示出來 *@param scrollx 每次滾動移動的距離 */ private void scrolltoshowmenu( int scrollx) { if (scrollx> 0 &&scrollx<= menuparams.width) menuparams.leftmargin =-menuparams.width+scrollx; menulayout.setlayoutparams(menuparams); } /** *指針按著時,滾動將菜單慢慢隱藏出來 *@param scrollx 每次滾動移動的距離 */ private void scrolltohidemenu( int scrollx) { if (scrollx>=-menuparams.width&&scrollx< 0 ) menuparams.leftmargin=scrollx; menulayout.setlayoutparams(menuparams); } /** * 創建velocitytracker對象,并將觸摸content界面的滑動事件加入到velocitytracker當中。 * @param event 向velocitytracker添加motionevent */ private void acquirevelocitytracker( final motionevent event) { if ( null == mvelocitytracker) { mvelocitytracker = velocitytracker.obtain(); } mvelocitytracker.addmovement(event); } /** * 獲取手指在content界面滑動的速度。 * @return 滑動速度,以每秒鐘移動了多少像素值為單位。 */ private int getscrollvelocity() { mvelocitytracker.computecurrentvelocity( 1000 ); int velocity = ( int ) mvelocitytracker.getxvelocity(); return math.abs(velocity); } /** * 釋放velocitytracker */ private void releasevelocitytracker() { if ( null != mvelocitytracker) { mvelocitytracker.clear(); mvelocitytracker.recycle(); mvelocitytracker = null ; } } /** * *:模擬動畫過程,讓肉眼能看到滾動的效果 * */ class showmenuasynctask extends asynctask<integer, integer, integer> { @override protected integer doinbackground(integer... params) { int leftmargin = menuparams.leftmargin; while ( true ) { // 根據傳入的速度來滾動界面,當滾動到達左邊界或右邊界時,跳出循環。 leftmargin += params[ 0 ]; if (params[ 0 ] > 0 && leftmargin > 0 ) { leftmargin= 0 ; break ; } else if (params[ 0 ] < 0 && leftmargin <-menuparams.width) { leftmargin=-menuparams.width; break ; } publishprogress(leftmargin); try { thread.sleep( 40 ); //休眠一下,肉眼才能看到滾動效果 } catch (interruptedexception e) { e.printstacktrace(); } } return leftmargin; } @override protected void onprogressupdate(integer... value) { menuparams.leftmargin = value[ 0 ]; menulayout.setlayoutparams(menuparams); } @override protected void onpostexecute(integer result) { menuparams.leftmargin = result; menulayout.setlayoutparams(menuparams); } } } |
三、原理與說明
原理 :
1、將scrollview的觸摸事件注冊到linearlayout中去。(linearlayout中包含了scrollview,不懂看布局)
2、首先判斷手勢是想要左右運動還是上下運動,如果是左右運動,那么linearlayout得到觸摸事件,即函數ontouch返回true;如果想上下運動,即函數ontouch返回false;
這里要注意的是,手勢判斷只一次,什么意思呢?就是說你第1次按下,到你一直按著,這中間只判斷一次你的手勢想要做的運動。
3、手指離開屏幕后,再來恢復所有的參數。
這是為大家分享的源碼,請下載:android仿uc瀏覽器左右上下滾動功能,希望本文所述對大家學習android軟件編程有所幫助。