Android实现双向滑动特效
一、背景和需求
在现代Android开发中,用户界面的交互效果越来越受到开发者的重视,双向滑动特效作为一种常见的交互方式,可以显著提升用户体验和应用的视觉效果,本文将详细介绍如何在Android应用中实现双向滑动特效,并提供具体的代码示例和步骤说明。
二、双向滑动特效的原理
双向滑动特效通常涉及三个部分:左侧菜单、右侧菜单和内容布局,左侧菜单位于屏幕左边缘对齐,右侧菜单位于屏幕右边缘对齐,而内容布局则占满整个屏幕并压在左侧和右侧菜单的上面,通过手指向左或向右滑动,可以实现左右两侧菜单的显示与隐藏。
左侧菜单
位置:屏幕左边缘对齐
功能:响应用户的向右滑动手势,显示或隐藏
右侧菜单
位置:屏幕右边缘对齐
功能:响应用户的向左滑动手势,显示或隐藏
位置:占满整个屏幕
功能:响应滑动操作,通过偏移位置来显示对应的菜单
三、实现步骤
创建Android项目
新建一个Android项目,命名为BidirSlidingLayout
。
定义布局文件
在res/layout
目录下创建一个名为activity_main.xml
的布局文件,定义左侧菜单、右侧菜单和内容布局的布局结构。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!-左侧菜单 --> <LinearLayout android:id="@+id/leftMenuLayout" android:layout_width="240dp" android:layout_height="match_parent" android:orientation="vertical" android:background="#FF0000" android:layout_alignParentLeft="true"/> <!-右侧菜单 --> <LinearLayout android:id="@+id/rightMenuLayout" android:layout_width="240dp" android:layout_height="match_parent" android:orientation="vertical" android:background="#00FF00" android:layout_alignParentRight="true"/> <!-内容布局 --> <FrameLayout android:id="@+id/contentLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_toLeftOf="@id/rightMenuLayout" android:layout_toRightOf="@id/leftMenuLayout"/> </RelativeLayout>
3. 编写核心类BidirSlidingLayout
在项目中创建一个名为BidirSlidingLayout
的类,继承自RelativeLayout
并实现OnTouchListener
接口,该类是实现双向滑动特效的核心类。
package com.example.bidirslidinglayout; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.RelativeLayout; public class BidirSlidingLayout extends RelativeLayout implements OnTouchListener { // 滚动显示和隐藏左侧布局时,手指滑动需要达到的速度。 public static final int SNAP_VELOCITY = 200; // 滑动状态的一种,表示未进行任何滑动。 public static final int DO_NOTHING = 0; // 滑动状态的一种,表示正在滑出左侧菜单。 public static final int SHOW_LEFT_MENU = 1; // 滑动状态的一种,表示正在滑出右侧菜单。 public static final int SHOW_RIGHT_MENU = 2; // 滑动状态的一种,表示正在隐藏左侧菜单。 public static final int HIDE_LEFT_MENU = 3; // 滑动状态的一种,表示正在隐藏右侧菜单。 public static final int HIDE_RIGHT_MENU = 4; // 记录当前的滑动状态 private int slideState; // 屏幕宽度值。 private int screenWidth; // 在被判定为滚动之前用户手指可以移动的最大值。 private int touchSlop; // 记录手指按下时的横坐标。 private float xDown; // 记录手指按下时的纵坐标。 private float yDown; // 记录手指移动时的横坐标。 private float xMove; // 记录手指移动时的纵坐标。 private float yMove; // 记录手机抬起时的横坐标。 private float xUp; // 左侧菜单当前是显示还是隐藏,只有完全显示或隐藏时才会更改此值,滑动过程中此值无效。 private boolean isLeftMenuVisible; // 右侧菜单当前是显示还是隐藏,只有完全显示或隐藏时才会更改此值,滑动过程中此值无效。 private boolean isRightMenuVisible; // 是否正在滑动。 private boolean isSliding; // 左侧菜单布局对象。 private View leftMenuLayout; // 右侧菜单布局对象。 private View rightMenuLayout; // 内容布局对象。 private View contentLayout; // 用于监听滑动事件的View。 private View mBindView; // 左侧菜单布局的参数。 private MarginLayoutParams leftMenuLayoutParams; // 右侧菜单布局的参数。 private MarginLayoutParams rightMenuLayoutParams; // 内容布局的参数。 private RelativeLayout.LayoutParams contentLayoutParams; // 用于计算手指滑动的速度。 private VelocityTracker mVelocityTracker; public BidirSlidingLayout(Context context, AttributeSet attrs) { super(context, attrs); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); screenWidth = wm.getDefaultDisplay().getWidth(); touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: xDown = event.getRawX(); yDown = event.getRawY(); mVelocityTracker.computeCurrentVelocity(1000); isSliding = false; isLeftMenuVisible = leftMenuLayout.getVisibility() == View.VISIBLE; isRightMenuVisible = rightMenuLayout.getVisibility() == View.VISIBLE; break; case MotionEvent.ACTION_MOVE: if (!isSliding) { float deltaX = xDown event.getRawX(); if (Math.abs(deltaX) > touchSlop) { isSliding = true; slideState = deltaX > 0 ? SHOW_LEFT_MENU : SHOW_RIGHT_MENU; } } if (isSliding) { xMove = event.getRawX(); yMove = event.getRawY(); float offset = xDown xMove; contentLayout.offsetLeftAndRight((int) offset); } break; case MotionEvent.ACTION_UP: xUp = event.getRawX(); if (isSliding) { slide(); isSliding = false; } break; case MotionEvent.ACTION_CANCEL: isSliding = false; break; } return true; } private void slide() { if (slideState == SHOW_LEFT_MENU) { // 滑出左侧菜单 int distance = (int) (xDown xUp); if (distance < -screenWidth / 2) { // 如果滑动距离超过屏幕宽度的一半,则显示左侧菜单,否则隐藏它。 leftMenuLayoutParams.leftMargin = 0; rightMenuLayoutParams.leftMargin = screenWidth / 2; contentLayoutParams.leftMargin = (int) (-screenWidth / 2); isLeftMenuVisible = true; isRightMenuVisible = false; } else { // 如果滑动距离没有超过屏幕宽度的一半,则隐藏左侧菜单。 leftMenuLayoutParams.leftMargin = screenWidth / 2; rightMenuLayoutParams.leftMargin = 0; contentLayoutParams.leftMargin = 0; isLeftMenuVisible = false; isRightMenuVisible = true; } } else if (slideState == SHOW_RIGHT_MENU) { // 滑出右侧菜单 int distance = (int) (xMove xUp); if (distance > screenWidth / 2) { // 如果滑动距离超过屏幕宽度的一半,则显示右侧菜单,否则隐藏它。 leftMenuLayoutParams.leftMargin = -screenWidth / 2; rightMenuLayoutParams.leftMargin = 0; contentLayoutParams.leftMargin = (int) (screenWidth / 2); isLeftMenuVisible = false; isRightMenuVisible = true; } else { // 如果滑动距离没有超过屏幕宽度的一半,则隐藏右侧菜单。 leftMenuLayoutParams.leftMargin = 0; rightMenuLayoutParams.leftMargin = screenWidth / 2; contentLayoutParams.leftMargin = 0; isLeftMenuVisible = true; isRightMenuVisible = false; } } else if (slideState == HIDE_LEFT_MENU) { // 隐藏左侧菜单(当用户从左侧菜单返回内容布局时) leftMenuLayoutParams.leftMargin = screenWidth / 2; rightMenuLayoutParams.leftMargin = 0; contentLayoutParams.leftMargin = 0; isLeftMenuVisible = false; isRightMenuVisible = true; } else if (slideState == HIDE_RIGHT_MENU) { // 隐藏右侧菜单(当用户从右侧菜单返回内容布局时) leftMenuLayoutParams.leftMargin = 0; rightMenuLayoutParams.leftMargin = screenWidth / 2; contentLayoutParams.leftMargin = 0; isLeftMenuVisible = true; isRightMenuVisible = false; } leftMenuLayout.setLayoutParams(leftMenuLayoutParams); rightMenuLayout.setLayoutParams(rightMenuLayoutParams); contentLayout.setLayoutParams(contentLayoutParams); } }
绑定监听滑动事件的View
在MainActivity
中绑定监听滑动事件的View,并将BidirSlidingLayout
添加到布局中。
package com.example.bidirslidinglayout; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.ListView; import android.widget.ArrayAdapter; import android.widget.ListAdapter; import java.util.ArrayList; import java.util.List; import android.widget.Toast; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.provider.Settings; import android.content.ContentResolver; import android.database.Cursor; import android.database.sqlite.*; import android.database.*; import android.database.sqlite.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*; import android.database.*;
小伙伴们,上文介绍了“android实现双向滑动特效”的内容,你了解清楚吗?希望对你有所帮助,任何问题可以给我留言,让我们下期再见吧。