`
从此醉
  • 浏览: 1047194 次
  • 性别: Icon_minigender_1
  • 来自: US
社区版块
存档分类
最新评论

Android双向滑动菜单完全解析,教你如何一分钟实现双向滑动特效

 
阅读更多

记得在很早之前,我写了一篇关于Android滑动菜单的文章,其中有一个朋友在评论中留言,希望我可以帮他将这个滑动菜单改成双向滑动的方式。当时也没想花太多时间,简单修改了一下就发给了他,结果没想到后来却有一大批的朋友都来问我要这份双向滑动菜单的代码。由于这份代码写得很不用心,我发了部分朋友之后实在不忍心继续发下去了,于是决定专门写一篇文章来介绍更好的Android双向滑动菜单的实现方法。

在开始动手之前先来讲一下实现原理,在一个Activity的布局中需要有三部分,一个是左侧菜单的布局,一个是右侧菜单的布局,一个是内容布局。左侧菜单居屏幕左边缘对齐,右侧菜单居屏幕右边缘对齐,然后内容布局占满整个屏幕,并压在了左侧菜单和右侧菜单的上面。当用户手指向右滑动时,将右侧菜单隐藏,左侧菜单显示,然后通过偏移内容布局的位置,就可以让左侧菜单展现出来。同样的道理,当用户手指向左滑动时,将左侧菜单隐藏,右侧菜单显示,也是通过偏移内容布局的位置,就可以让右侧菜单展现出来。原理示意图所下所示:


介绍完了原理,我们就开始动手实现吧。新建一个Android项目,项目名就叫做BidirSlidingLayout。然后新建我们最主要的BidirSlidingLayout类,这个类就是实现双向滑动菜单功能的核心类,代码如下所示:

  1. publicclassBidirSlidingLayoutextendsRelativeLayoutimplementsOnTouchListener{
  2. /**
  3. *滚动显示和隐藏左侧布局时,手指滑动需要达到的速度。
  4. */
  5. publicstaticfinalintSNAP_VELOCITY=200;
  6. /**
  7. *滑动状态的一种,表示未进行任何滑动。
  8. */
  9. publicstaticfinalintDO_NOTHING=0;
  10. /**
  11. *滑动状态的一种,表示正在滑出左侧菜单。
  12. */
  13. publicstaticfinalintSHOW_LEFT_MENU=1;
  14. /**
  15. *滑动状态的一种,表示正在滑出右侧菜单。
  16. */
  17. publicstaticfinalintSHOW_RIGHT_MENU=2;
  18. /**
  19. *滑动状态的一种,表示正在隐藏左侧菜单。
  20. */
  21. publicstaticfinalintHIDE_LEFT_MENU=3;
  22. /**
  23. *滑动状态的一种,表示正在隐藏右侧菜单。
  24. */
  25. publicstaticfinalintHIDE_RIGHT_MENU=4;
  26. /**
  27. *记录当前的滑动状态
  28. */
  29. privateintslideState;
  30. /**
  31. *屏幕宽度值。
  32. */
  33. privateintscreenWidth;
  34. /**
  35. *在被判定为滚动之前用户手指可以移动的最大值。
  36. */
  37. privateinttouchSlop;
  38. /**
  39. *记录手指按下时的横坐标。
  40. */
  41. privatefloatxDown;
  42. /**
  43. *记录手指按下时的纵坐标。
  44. */
  45. privatefloatyDown;
  46. /**
  47. *记录手指移动时的横坐标。
  48. */
  49. privatefloatxMove;
  50. /**
  51. *记录手指移动时的纵坐标。
  52. */
  53. privatefloatyMove;
  54. /**
  55. *记录手机抬起时的横坐标。
  56. */
  57. privatefloatxUp;
  58. /**
  59. *左侧菜单当前是显示还是隐藏。只有完全显示或隐藏时才会更改此值,滑动过程中此值无效。
  60. */
  61. privatebooleanisLeftMenuVisible;
  62. /**
  63. *右侧菜单当前是显示还是隐藏。只有完全显示或隐藏时才会更改此值,滑动过程中此值无效。
  64. */
  65. privatebooleanisRightMenuVisible;
  66. /**
  67. *是否正在滑动。
  68. */
  69. privatebooleanisSliding;
  70. /**
  71. *左侧菜单布局对象。
  72. */
  73. privateViewleftMenuLayout;
  74. /**
  75. *右侧菜单布局对象。
  76. */
  77. privateViewrightMenuLayout;
  78. /**
  79. *内容布局对象。
  80. */
  81. privateViewcontentLayout;
  82. /**
  83. *用于监听滑动事件的View。
  84. */
  85. privateViewmBindView;
  86. /**
  87. *左侧菜单布局的参数。
  88. */
  89. privateMarginLayoutParamsleftMenuLayoutParams;
  90. /**
  91. *右侧菜单布局的参数。
  92. */
  93. privateMarginLayoutParamsrightMenuLayoutParams;
  94. /**
  95. *内容布局的参数。
  96. */
  97. privateRelativeLayout.LayoutParamscontentLayoutParams;
  98. /**
  99. *用于计算手指滑动的速度。
  100. */
  101. privateVelocityTrackermVelocityTracker;
  102. /**
  103. *重写BidirSlidingLayout的构造函数,其中获取了屏幕的宽度和touchSlop的值。
  104. *
  105. *@paramcontext
  106. *@paramattrs
  107. */
  108. publicBidirSlidingLayout(Contextcontext,AttributeSetattrs){
  109. super(context,attrs);
  110. WindowManagerwm=(WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
  111. screenWidth=wm.getDefaultDisplay().getWidth();
  112. touchSlop=ViewConfiguration.get(context).getScaledTouchSlop();
  113. }
  114. /**
  115. *绑定监听滑动事件的View。
  116. *
  117. *@parambindView
  118. *需要绑定的View对象。
  119. */
  120. publicvoidsetScrollEvent(ViewbindView){
  121. mBindView=bindView;
  122. mBindView.setOnTouchListener(this);
  123. }
  124. /**
  125. *将界面滚动到左侧菜单界面,滚动速度设定为-30.
  126. */
  127. publicvoidscrollToLeftMenu(){
  128. newLeftMenuScrollTask().execute(-30);
  129. }
  130. /**
  131. *将界面滚动到右侧菜单界面,滚动速度设定为-30.
  132. */
  133. publicvoidscrollToRightMenu(){
  134. newRightMenuScrollTask().execute(-30);
  135. }
  136. /**
  137. *将界面从左侧菜单滚动到内容界面,滚动速度设定为30.
  138. */
  139. publicvoidscrollToContentFromLeftMenu(){
  140. newLeftMenuScrollTask().execute(30);
  141. }
  142. /**
  143. *将界面从右侧菜单滚动到内容界面,滚动速度设定为30.
  144. */
  145. publicvoidscrollToContentFromRightMenu(){
  146. newRightMenuScrollTask().execute(30);
  147. }
  148. /**
  149. *左侧菜单是否完全显示出来,滑动过程中此值无效。
  150. *
  151. *@return左侧菜单完全显示返回true,否则返回false。
  152. */
  153. publicbooleanisLeftLayoutVisible(){
  154. returnisLeftMenuVisible;
  155. }
  156. /**
  157. *右侧菜单是否完全显示出来,滑动过程中此值无效。
  158. *
  159. *@return右侧菜单完全显示返回true,否则返回false。
  160. */
  161. publicbooleanisRightLayoutVisible(){
  162. returnisRightMenuVisible;
  163. }
  164. /**
  165. *在onLayout中重新设定左侧菜单、右侧菜单、以及内容布局的参数。
  166. */
  167. @Override
  168. protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){
  169. super.onLayout(changed,l,t,r,b);
  170. if(changed){
  171. //获取左侧菜单布局对象
  172. leftMenuLayout=getChildAt(0);
  173. leftMenuLayoutParams=(MarginLayoutParams)leftMenuLayout.getLayoutParams();
  174. //获取右侧菜单布局对象
  175. rightMenuLayout=getChildAt(1);
  176. rightMenuLayoutParams=(MarginLayoutParams)rightMenuLayout.getLayoutParams();
  177. //获取内容布局对象
  178. contentLayout=getChildAt(2);
  179. contentLayoutParams=(RelativeLayout.LayoutParams)contentLayout.getLayoutParams();
  180. contentLayoutParams.width=screenWidth;
  181. contentLayout.setLayoutParams(contentLayoutParams);
  182. }
  183. }
  184. @Override
  185. publicbooleanonTouch(Viewv,MotionEventevent){
  186. createVelocityTracker(event);
  187. switch(event.getAction()){
  188. caseMotionEvent.ACTION_DOWN:
  189. //手指按下时,记录按下时的坐标
  190. xDown=event.getRawX();
  191. yDown=event.getRawY();
  192. //将滑动状态初始化为DO_NOTHING
  193. slideState=DO_NOTHING;
  194. break;
  195. caseMotionEvent.ACTION_MOVE:
  196. xMove=event.getRawX();
  197. yMove=event.getRawY();
  198. //手指移动时,对比按下时的坐标,计算出移动的距离。
  199. intmoveDistanceX=(int)(xMove-xDown);
  200. intmoveDistanceY=(int)(yMove-yDown);
  201. //检查当前的滑动状态
  202. checkSlideState(moveDistanceX,moveDistanceY);
  203. //根据当前滑动状态决定如何偏移内容布局
  204. switch(slideState){
  205. caseSHOW_LEFT_MENU:
  206. contentLayoutParams.rightMargin=-moveDistanceX;
  207. checkLeftMenuBorder();
  208. contentLayout.setLayoutParams(contentLayoutParams);
  209. break;
  210. caseHIDE_LEFT_MENU:
  211. contentLayoutParams.rightMargin=-leftMenuLayoutParams.width-moveDistanceX;
  212. checkLeftMenuBorder();
  213. contentLayout.setLayoutParams(contentLayoutParams);
  214. caseSHOW_RIGHT_MENU:
  215. contentLayoutParams.leftMargin=moveDistanceX;
  216. checkRightMenuBorder();
  217. contentLayout.setLayoutParams(contentLayoutParams);
  218. break;
  219. caseHIDE_RIGHT_MENU:
  220. contentLayoutParams.leftMargin=-rightMenuLayoutParams.width+moveDistanceX;
  221. checkRightMenuBorder();
  222. contentLayout.setLayoutParams(contentLayoutParams);
  223. default:
  224. break;
  225. }
  226. break;
  227. caseMotionEvent.ACTION_UP:
  228. xUp=event.getRawX();
  229. intupDistanceX=(int)(xUp-xDown);
  230. if(isSliding){
  231. //手指抬起时,进行判断当前手势的意图
  232. switch(slideState){
  233. caseSHOW_LEFT_MENU:
  234. if(shouldScrollToLeftMenu()){
  235. scrollToLeftMenu();
  236. }else{
  237. scrollToContentFromLeftMenu();
  238. }
  239. break;
  240. caseHIDE_LEFT_MENU:
  241. if(shouldScrollToContentFromLeftMenu()){
  242. scrollToContentFromLeftMenu();
  243. }else{
  244. scrollToLeftMenu();
  245. }
  246. break;
  247. caseSHOW_RIGHT_MENU:
  248. if(shouldScrollToRightMenu()){
  249. scrollToRightMenu();
  250. }else{
  251. scrollToContentFromRightMenu();
  252. }
  253. break;
  254. caseHIDE_RIGHT_MENU:
  255. if(shouldScrollToContentFromRightMenu()){
  256. scrollToContentFromRightMenu();
  257. }else{
  258. scrollToRightMenu();
  259. }
  260. break;
  261. default:
  262. break;
  263. }
  264. }elseif(upDistanceX<touchSlop&&isLeftMenuVisible){
  265. //当左侧菜单显示时,如果用户点击一下内容部分,则直接滚动到内容界面
  266. scrollToContentFromLeftMenu();
  267. }elseif(upDistanceX<touchSlop&&isRightMenuVisible){
  268. //当右侧菜单显示时,如果用户点击一下内容部分,则直接滚动到内容界面
  269. scrollToContentFromRightMenu();
  270. }
  271. recycleVelocityTracker();
  272. break;
  273. }
  274. if(v.isEnabled()){
  275. if(isSliding){
  276. //正在滑动时让控件得不到焦点
  277. unFocusBindView();
  278. returntrue;
  279. }
  280. if(isLeftMenuVisible||isRightMenuVisible){
  281. //当左侧或右侧布局显示时,将绑定控件的事件屏蔽掉
  282. returntrue;
  283. }
  284. returnfalse;
  285. }
  286. returntrue;
  287. }
  288. /**
  289. *根据手指移动的距离,判断当前用户的滑动意图,然后给slideState赋值成相应的滑动状态值。
  290. *
  291. *@parammoveDistanceX
  292. *横向移动的距离
  293. *@parammoveDistanceY
  294. *纵向移动的距离
  295. */
  296. privatevoidcheckSlideState(intmoveDistanceX,intmoveDistanceY){
  297. if(isLeftMenuVisible){
  298. if(!isSliding&&Math.abs(moveDistanceX)>=touchSlop&&moveDistanceX<0){
  299. isSliding=true;
  300. slideState=HIDE_LEFT_MENU;
  301. }
  302. }elseif(isRightMenuVisible){
  303. if(!isSliding&&Math.abs(moveDistanceX)>=touchSlop&&moveDistanceX>0){
  304. isSliding=true;
  305. slideState=HIDE_RIGHT_MENU;
  306. }
  307. }else{
  308. if(!isSliding&&Math.abs(moveDistanceX)>=touchSlop&&moveDistanceX>0
  309. &&Math.abs(moveDistanceY)<touchSlop){
  310. isSliding=true;
  311. slideState=SHOW_LEFT_MENU;
  312. contentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,0);
  313. contentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
  314. contentLayout.setLayoutParams(contentLayoutParams);
  315. //如果用户想要滑动左侧菜单,将左侧菜单显示,右侧菜单隐藏
  316. leftMenuLayout.setVisibility(View.VISIBLE);
  317. rightMenuLayout.setVisibility(View.GONE);
  318. }elseif(!isSliding&&Math.abs(moveDistanceX)>=touchSlop&&moveDistanceX<0
  319. &&Math.abs(moveDistanceY)<touchSlop){
  320. isSliding=true;
  321. slideState=SHOW_RIGHT_MENU;
  322. contentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,0);
  323. contentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
  324. contentLayout.setLayoutParams(contentLayoutParams);
  325. //如果用户想要滑动右侧菜单,将右侧菜单显示,左侧菜单隐藏
  326. rightMenuLayout.setVisibility(View.VISIBLE);
  327. leftMenuLayout.setVisibility(View.GONE);
  328. }
  329. }
  330. }
  331. /**
  332. *在滑动过程中检查左侧菜单的边界值,防止绑定布局滑出屏幕。
  333. */
  334. privatevoidcheckLeftMenuBorder(){
  335. if(contentLayoutParams.rightMargin>0){
  336. contentLayoutParams.rightMargin=0;
  337. }elseif(contentLayoutParams.rightMargin<-leftMenuLayoutParams.width){
  338. contentLayoutParams.rightMargin=-leftMenuLayoutParams.width;
  339. }
  340. }
  341. /**
  342. *在滑动过程中检查右侧菜单的边界值,防止绑定布局滑出屏幕。
  343. */
  344. privatevoidcheckRightMenuBorder(){
  345. if(contentLayoutParams.leftMargin>0){
  346. contentLayoutParams.leftMargin=0;
  347. }elseif(contentLayoutParams.leftMargin<-rightMenuLayoutParams.width){
  348. contentLayoutParams.leftMargin=-rightMenuLayoutParams.width;
  349. }
  350. }
  351. /**
  352. *判断是否应该滚动将左侧菜单展示出来。如果手指移动距离大于左侧菜单宽度的1/2,或者手指移动速度大于SNAP_VELOCITY,
  353. *就认为应该滚动将左侧菜单展示出来。
  354. *
  355. *@return如果应该将左侧菜单展示出来返回true,否则返回false。
  356. */
  357. privatebooleanshouldScrollToLeftMenu(){
  358. returnxUp-xDown>leftMenuLayoutParams.width/2||getScrollVelocity()>SNAP_VELOCITY;
  359. }
  360. /**
  361. *判断是否应该滚动将右侧菜单展示出来。如果手指移动距离大于右侧菜单宽度的1/2,或者手指移动速度大于SNAP_VELOCITY,
  362. *就认为应该滚动将右侧菜单展示出来。
  363. *
  364. *@return如果应该将右侧菜单展示出来返回true,否则返回false。
  365. */
  366. privatebooleanshouldScrollToRightMenu(){
  367. returnxDown-xUp>rightMenuLayoutParams.width/2||getScrollVelocity()>SNAP_VELOCITY;
  368. }
  369. /**
  370. *判断是否应该从左侧菜单滚动到内容布局,如果手指移动距离大于左侧菜单宽度的1/2,或者手指移动速度大于SNAP_VELOCITY,
  371. *就认为应该从左侧菜单滚动到内容布局。
  372. *
  373. *@return如果应该从左侧菜单滚动到内容布局返回true,否则返回false。
  374. */
  375. privatebooleanshouldScrollToContentFromLeftMenu(){
  376. returnxDown-xUp>leftMenuLayoutParams.width/2||getScrollVelocity()>SNAP_VELOCITY;
  377. }
  378. /**
  379. *判断是否应该从右侧菜单滚动到内容布局,如果手指移动距离大于右侧菜单宽度的1/2,或者手指移动速度大于SNAP_VELOCITY,
  380. *就认为应该从右侧菜单滚动到内容布局。
  381. *
  382. *@return如果应该从右侧菜单滚动到内容布局返回true,否则返回false。
  383. */
  384. privatebooleanshouldScrollToContentFromRightMenu(){
  385. returnxUp-xDown>rightMenuLayoutParams.width/2||getScrollVelocity()>SNAP_VELOCITY;
  386. }
  387. /**
  388. *创建VelocityTracker对象,并将触摸事件加入到VelocityTracker当中。
  389. *
  390. *@paramevent
  391. *右侧布局监听控件的滑动事件
  392. */
  393. privatevoidcreateVelocityTracker(MotionEventevent){
  394. if(mVelocityTracker==null){
  395. mVelocityTracker=VelocityTracker.obtain();
  396. }
  397. mVelocityTracker.addMovement(event);
  398. }
  399. /**
  400. *获取手指在绑定布局上的滑动速度。
  401. *
  402. *@return滑动速度,以每秒钟移动了多少像素值为单位。
  403. */
  404. privateintgetScrollVelocity(){
  405. mVelocityTracker.computeCurrentVelocity(1000);
  406. intvelocity=(int)mVelocityTracker.getXVelocity();
  407. returnMath.abs(velocity);
  408. }
  409. /**
  410. *回收VelocityTracker对象。
  411. */
  412. privatevoidrecycleVelocityTracker(){
  413. mVelocityTracker.recycle();
  414. mVelocityTracker=null;
  415. }
  416. /**
  417. *使用可以获得焦点的控件在滑动的时候失去焦点。
  418. */
  419. privatevoidunFocusBindView(){
  420. if(mBindView!=null){
  421. mBindView.setPressed(false);
  422. mBindView.setFocusable(false);
  423. mBindView.setFocusableInTouchMode(false);
  424. }
  425. }
  426. classLeftMenuScrollTaskextendsAsyncTask<Integer,Integer,Integer>{
  427. @Override
  428. protectedIntegerdoInBackground(Integer...speed){
  429. intrightMargin=contentLayoutParams.rightMargin;
  430. //根据传入的速度来滚动界面,当滚动到达边界值时,跳出循环。
  431. while(true){
  432. rightMargin=rightMargin+speed[0];
  433. if(rightMargin<-leftMenuLayoutParams.width){
  434. rightMargin=-leftMenuLayoutParams.width;
  435. break;
  436. }
  437. if(rightMargin>0){
  438. rightMargin=0;
  439. break;
  440. }
  441. publishProgress(rightMargin);
  442. //为了要有滚动效果产生,每次循环使线程睡眠一段时间,这样肉眼才能够看到滚动动画。
  443. sleep(15);
  444. }
  445. if(speed[0]>0){
  446. isLeftMenuVisible=false;
  447. }else{
  448. isLeftMenuVisible=true;
  449. }
  450. isSliding=false;
  451. returnrightMargin;
  452. }
  453. @Override
  454. protectedvoidonProgressUpdate(Integer...rightMargin){
  455. contentLayoutParams.rightMargin=rightMargin[0];
  456. contentLayout.setLayoutParams(contentLayoutParams);
  457. unFocusBindView();
  458. }
  459. @Override
  460. protectedvoidonPostExecute(IntegerrightMargin){
  461. contentLayoutParams.rightMargin=rightMargin;
  462. contentLayout.setLayoutParams(contentLayoutParams);
  463. }
  464. }
  465. classRightMenuScrollTaskextendsAsyncTask<Integer,Integer,Integer>{
  466. @Override
  467. protectedIntegerdoInBackground(Integer...speed){
  468. intleftMargin=contentLayoutParams.leftMargin;
  469. //根据传入的速度来滚动界面,当滚动到达边界值时,跳出循环。
  470. while(true){
  471. leftMargin=leftMargin+speed[0];
  472. if(leftMargin<-rightMenuLayoutParams.width){
  473. leftMargin=-rightMenuLayoutParams.width;
  474. break;
  475. }
  476. if(leftMargin>0){
  477. leftMargin=0;
  478. break;
  479. }
  480. publishProgress(leftMargin);
  481. //为了要有滚动效果产生,每次循环使线程睡眠一段时间,这样肉眼才能够看到滚动动画。
  482. sleep(15);
  483. }
  484. if(speed[0]>0){
  485. isRightMenuVisible=false;
  486. }else{
  487. isRightMenuVisible=true;
  488. }
  489. isSliding=false;
  490. returnleftMargin;
  491. }
  492. @Override
  493. protectedvoidonProgressUpdate(Integer...leftMargin){
  494. contentLayoutParams.leftMargin=leftMargin[0];
  495. contentLayout.setLayoutParams(contentLayoutParams);
  496. unFocusBindView();
  497. }
  498. @Override
  499. protectedvoidonPostExecute(IntegerleftMargin){
  500. contentLayoutParams.leftMargin=leftMargin;
  501. contentLayout.setLayoutParams(contentLayoutParams);
  502. }
  503. }
  504. /**
  505. *使当前线程睡眠指定的毫秒数。
  506. *
  507. *@parammillis
  508. *指定当前线程睡眠多久,以毫秒为单位
  509. */
  510. privatevoidsleep(longmillis){
  511. try{
  512. Thread.sleep(millis);
  513. }catch(InterruptedExceptione){
  514. e.printStackTrace();
  515. }
  516. }
  517. }
以上代码注释已经写得非常详细,我再来简单解释一下。首先在onLayout()方法中分别获取到左侧菜单、右侧菜单和内容布局的参数,并将内容布局的宽度重定义成屏幕的宽度,这样就可以保证内容布局既能覆盖住下面的菜单布局,还能偏移出屏幕。然后在onTouch()方法中监听触屏事件,以判断用户手势的意图。这里事先定义好了几种滑动状态,DO_NOTHING表示没有进行任何滑动,SHOW_LEFT_MENU表示用户想要滑出左侧菜单,SHOW_RIGHT_MENU表示用户想要滑出右侧菜单,HIDE_LEFT_MENU表示用户想要隐藏左侧菜单,HIDE_RIGHT_MENU表示用户想要隐藏右侧菜单,在checkSlideState()方法中判断出用户到底是想进行哪一种滑动操作,并给slideState变量赋值,然后根据slideState的值决定如何偏移内容布局。接着当用户手指离开屏幕时,会根据当前的滑动距离,决定后续的滚动方向,通过LeftMenuScrollTask和RightMenuScrollTask来完成完整的滑动过程。另外在滑动的过程,内容布局上的事件会被屏蔽掉,主要是通过一系列的return操作实现的,对这一部分不理解的朋友,请参阅Android事件分发机制完全解析,带你从源码的角度彻底理解

然后我们看一下setScrollEvent方法,这个方法接收一个View作为参数,然后为这个View绑定了一个touch事件。这是什么意思呢?让我们来想象一个场景,如果内容布局是一个LinearLayout,我可以通过监听LinearLayout上的touch事件来控制它的偏移。但是如果内容布局的LinearLayout里面加入了一个ListView,而这个ListView又充满了整个LinearLayout,这个时候LinearLayout将不可能再被touch到了,这个时候我们就需要将touch事件注册到ListView上。setScrollEvent方法也就是提供了一个注册接口,touch事件将会注册到传入的View上。

接下来打开或新建activity_main.xml文件,加入如下代码:

  1. <com.example.bidirslidinglayout.BidirSlidingLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:id="@+id/bidir_sliding_layout"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent">
  6. <RelativeLayout
  7. android:id="@+id/left_menu"
  8. android:layout_width="270dip"
  9. android:layout_height="fill_parent"
  10. android:layout_alignParentLeft="true"
  11. android:background="#00ccff"
  12. android:visibility="invisible">
  13. <TextView
  14. android:layout_width="wrap_content"
  15. android:layout_height="wrap_content"
  16. android:layout_centerInParent="true"
  17. android:text="Thisisleftmenu"
  18. android:textColor="#000000"
  19. android:textSize="28sp"/>
  20. </RelativeLayout>
  21. <RelativeLayout
  22. android:id="@+id/right_menu"
  23. android:layout_width="270dip"
  24. android:layout_height="fill_parent"
  25. android:layout_alignParentRight="true"
  26. android:background="#00ffcc"
  27. android:visibility="invisible">
  28. <TextView
  29. android:layout_width="wrap_content"
  30. android:layout_height="wrap_content"
  31. android:layout_centerInParent="true"
  32. android:text="Thisisrightmenu"
  33. android:textColor="#000000"
  34. android:textSize="28sp"/>
  35. </RelativeLayout>
  36. <LinearLayout
  37. android:id="@+id/content"
  38. android:layout_width="320dip"
  39. android:layout_height="fill_parent"
  40. android:background="#e9e9e9">
  41. <ListView
  42. android:id="@+id/contentList"
  43. android:layout_width="fill_parent"
  44. android:layout_height="fill_parent"
  45. android:scrollbars="none"
  46. android:cacheColorHint="#00000000">
  47. </ListView>
  48. </LinearLayout>
  49. </com.example.bidirslidinglayout.BidirSlidingLayout>
可以看到,我们使用了自定义的BidirSlidingLayout作为根布局,然后依次加入了三个子布局分别作为左侧菜单、右侧菜单和内容的布局。左侧菜单和右侧菜单中都只是简单地放入了一个TextView用于显示一段文字,内容布局中放入了一个ListView。注意要让左侧菜单和父布局左边缘对齐,右侧菜单和父布局右边缘对齐。

最后打开或者创建MainActivity作为程序的主Activity,代码如下所示:

  1. publicclassMainActivityextendsActivity{
  2. /**
  3. *双向滑动菜单布局
  4. */
  5. privateBidirSlidingLayoutbidirSldingLayout;
  6. /**
  7. *在内容布局上显示的ListView
  8. */
  9. privateListViewcontentList;
  10. /**
  11. *ListView的适配器
  12. */
  13. privateArrayAdapter<String>contentListAdapter;
  14. /**
  15. *用于填充contentListAdapter的数据源。
  16. */
  17. privateString[]contentItems={"ContentItem1","ContentItem2","ContentItem3",
  18. "ContentItem4","ContentItem5","ContentItem6","ContentItem7",
  19. "ContentItem8","ContentItem9","ContentItem10","ContentItem11",
  20. "ContentItem12","ContentItem13","ContentItem14","ContentItem15",
  21. "ContentItem16"};
  22. @Override
  23. protectedvoidonCreate(BundlesavedInstanceState){
  24. super.onCreate(savedInstanceState);
  25. setContentView(R.layout.activity_main);
  26. bidirSldingLayout=(BidirSlidingLayout)findViewById(R.id.bidir_sliding_layout);
  27. contentList=(ListView)findViewById(R.id.contentList);
  28. contentListAdapter=newArrayAdapter<String>(this,android.R.layout.simple_list_item_1,
  29. contentItems);
  30. contentList.setAdapter(contentListAdapter);
  31. bidirSldingLayout.setScrollEvent(contentList);
  32. }
  33. }
这里我们给ListView填充了几条数据,又通过findViewById()方法获取到了BidirSlidingLayout对象,然后调用它的setScrollEvent()方法,将ListView进行绑定,这样就可以通过左右滑动ListView来展示左侧菜单和右侧菜单了。

好了,全部编码工作都已完成,现在让我们运行一下程序吧,效果如下图所示:


看起来还是挺不错的吧!并且更重要的是,以后我们在项目的任何地方都可以轻松加入双向滑动菜单功能,只需要以下两步即可:

1.在Acitivty的layout中引入我们自定义的BidirSlidingLayout布局,并且给这个布局要加入三个直接子元素。

2.在Activity中通过setScrollEvent方法,给一个View注册touch事件。

如此一来,一分钟实现双向滑动菜单功能妥妥的。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics