Android仿UC浏览器左右上下滚动功能

时间:2021-09-16 08:42:17

本文要解决在侧滑菜单右边加个文本框,并能实现文本的上下滑动和菜单的左右滚动。这里推荐可以好好看看android的触摸事件的分发机制,这里我就不详细讲了,我只讲讲这个应用。要实现的功能就像uc浏览器(或其它手机浏览器)的左右滚动,切换网页,上下滚动,拖动内容。
本文的效果:

Android仿UC浏览器左右上下滚动功能

Android仿UC浏览器左右上下滚动功能

一、功能要求与实现
1、功能要求:
(1)手指一开始按着屏幕左右移动时,只能左右滚动菜单,如果这时手指一直按着,而且上下移动了,那么菜单显示部分保持不变,但文本框也不上下移动!                      
(2)手指一开始按着屏幕上下移动时,只能上下滚动文本框,如果这时手指一直按着,而且左右移动了,那么文本框显示部分保持不变,但菜单也不左右移动!
2、初步实现:
      左边的菜单项增加一个listview,为右边的内容项添加一个textview,并且为了能让它实现上下滚动的功能,给textview加了个scrollview。
       这种效果肯定是不对的,你看,我们手指上下禾移动文本时,如果还左右移动了,菜单也显示出来了。

 Android仿UC浏览器左右上下滚动功能

Android仿UC浏览器左右上下滚动功能

3、修改实现   
     这时我就想从触摸事件的分发入手,这里因为我是把scrollview的触摸事件注册到linearlayout。(linearlayout中包含了scrollview,不懂看下面的布局)中去,所以触摸事件会先传递给linearlayout。
分以下两种情况:
(1)如果是手指左右移动,则把触摸事件传给linearlayout。函数ontouch返回true,表示触摸事件不再传递下去,那么scrollview就动不了了
(2)如果是手指上下移动,触摸事件先传给linearlayout,但linearlayout不做任何处理,直接传递给scrollview,scrollview来处理触摸事件。
这是修改后的效果:

Android仿UC浏览器左右上下滚动功能                                             

二、布局与代码
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仿uc浏览器左右上下滚动功能,希望本文所述对大家学习android软件编程有所帮助。