侧滑效果展示
原理
要实现这种侧滑效果,首先自定义一个滑动Layout继承自RelativeLayout,里面有两个View分别是用来存放内容和菜单布局的,然后记录手指一动的距离更改内容View的leftMargin(注意:只有leftMargin的值是负数才能够向左偏移,如果是正数的话那么会向右压缩),在内容View移动的同时,调用菜单的setTranslateX()方法就可以达到菜单移动了
代码
首先上代码:
<cn.karent.slide.UI.SlideLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--侧滑菜单-->
<RelativeLayout
android:id="@+id/slide_menu"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_marginRight="-150dp"
android:background="@android:color/holo_purple"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="侧滑菜单"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="测试按钮"/>
</RelativeLayout>
<!--内容View-->
<RelativeLayout
android:id="@+id/slide_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="@android:color/holo_red_dark">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是内容"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
android:layout_centerInParent="true"/>
</RelativeLayout>
</cn.karent.slide.UI.SlideLayout>
注意,我直接让菜单项往右偏移一半,这样就不会有完全滚动的效果,而是菜单的偏移比内容的偏移更慢一点,下面上自定义Layout的代码:
package cn.karent.slide.UI;
import android.content.Context;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import cn.karent.slide.R;
import cn.karent.slide.util.ScreenUtil;
/**
* Created by wan on 2016/12/6.
* 侧滑菜单,内容会偏移
*/
public class SlideLayout extends RelativeLayout {
private Context mContext;
private static final int MOTION_VELOCITY = 300;
/*
是否是第一次调用onLayout方法
*/
private boolean mLoadOnece = false;
/*
左边内容的布局参数
*/
private MarginLayoutParams mLeftParams;
/*
右边菜单的布局参数
*/
private MarginLayoutParams mMenuParams;
/*
左边的内容View
*/
private View mLeftView;
/*
侧滑菜单View
*/
private View mMenuView;
private float mOldX;
private float mOldY;
/*
* 屏幕的宽度
*/
private int mScreenWidth;
/**
* 控件能够移动到的左边界,内容View能向左偏移的最大边界
*/
private int mLeftEdge ;
public SlideLayout(Context context) {
super(context);
mContext = context;
}
public SlideLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
}
/**
* 布局
*/
public void onLayout(boolean change, int l, int t, int r, int b) {
super.onLayout(change, l, t, r, b);
if( change && !mLoadOnece) {
mLeftEdge = ScreenUtil.dp2px(300);
mLeftView = findViewById(R.id.slide_content);
mMenuView = findViewById(R.id.slide_menu);
mLeftParams = (MarginLayoutParams) mLeftView.getLayoutParams();
mMenuParams = (MarginLayoutParams)mMenuView.getLayoutParams();
DisplayMetrics dm = getResources().getDisplayMetrics();
mScreenWidth = dm.widthPixels;
mLoadOnece = true;
}
}
/**
* 事件拦截
* @param e
* @return
* true 表示拦截子类的Touch事件
* false 表示
*/
public boolean onInterceptTouchEvent(MotionEvent e) {
int action = e.getAction();
switch( action ) {
case MotionEvent.ACTION_DOWN:
return false;
case MotionEvent.ACTION_MOVE:
mOldX = e.getRawX();
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
public boolean onTouchEvent(MotionEvent e) {
int action = e.getAction();
switch( action ) {
case MotionEvent.ACTION_DOWN:
//e.getX()得到的是相对当前容器的坐标
mOldX = e.getRawX();
mOldY = e.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float x = e.getRawX();
float y = e.getRawY();
if( y - mOldY > 20 || y - mOldY < -20) {
return false;
}
//获取移动了多少个px
int moveX = (int)(x - mOldX);
mOldX = x;
mOldY = y;
//侧滑
modifyLeftMargin(mLeftParams.leftMargin + moveX);
break;
case MotionEvent.ACTION_UP:
//当手指拿起时,计算是向哪边滑动
int l = mLeftParams.leftMargin > (-mLeftEdge / 2) ? 0 : -mLeftEdge;
new SmoothScrollTack().execute(l, mLeftParams.leftMargin);
break;
}
return true;
}
/**
* 修改View的边距来达到移动的效果
* @param leftMargin
*/
private void modifyLeftMargin(int leftMargin) {
//如果左边距大于0代表将要向右变压缩,应该禁止
if( leftMargin > 0 ) {
mLeftParams.leftMargin = 0;
mMenuView.setTranslationX(0);
//控制左边界滑动
} else if( leftMargin < -mLeftEdge) {
mLeftParams.leftMargin = -mLeftEdge;
mMenuView.setTranslationX(-mLeftEdge / 2);
} else {
mLeftParams.leftMargin = leftMargin;
mMenuView.setTranslationX(leftMargin / 2);
}
mLeftParams.width = mScreenWidth;
mLeftView.setLayoutParams(mLeftParams);
}
/**
* 当侧滑停止的时候来处理接下来的滑动
*/
private class SmoothScrollTack extends AsyncTask<Integer, Integer, Integer> {
@Override
protected void onProgressUpdate(Integer... values) {
int leftMargin = values[0];
modifyLeftMargin(leftMargin);
}
/**
* 计算下一个leftMargin值
* @param params
* @return
*/
@Override
protected Integer doInBackground(Integer... params) {
int targetLeftMargin = params[0];
int currentLeftMargin = params[1];
int leftMargin = currentLeftMargin;
//计算增加的步长
int step = targetLeftMargin == 0 ? 10 : -10;
while(true) {
leftMargin += step;
//判断是否滑动完成
if( leftMargin < -mLeftEdge ) {
leftMargin = -mLeftEdge;
break;
}
if( leftMargin > 0 ) {
leftMargin = 0;
break;
}
publishProgress(leftMargin);
try {
Thread.sleep(5);
} catch( InterruptedException e) {
e.printStackTrace();
}
}
publishProgress(leftMargin);
return leftMargin;
}
}
}
ScreenUtil:
package cn.karent.slide.util;
import android.util.DisplayMetrics;
import android.util.Log;
/**
* Created by wan on 2016/12/22.
*
*/
public class ScreenUtil {
/**
* 获取屏幕的密度
* @return
*/
public static float getDensity() {
DisplayMetrics dm = MyApplication.getContext().getResources().getDisplayMetrics();
float density = dm.density;
Log.d("density", density + "");
return density;
}
public static DisplayMetrics getDislayMetrics() {
return MyApplication.getContext().getResources().getDisplayMetrics();
}
public static int px2dp(float p) {
float density = getDensity();
return (int)(p / density + 0.5f);
}
public static int dp2px(float dip) {
return (int)(dip * getDensity() + 0.5f);
}
}
获取全局的Context:
package cn.karent.slide.util;
import android.app.Application;
import android.content.Context;
/**
* Created by wan on 2016/12/22.
* 获取全局Context对象
*/
public class MyApplication extends Application {
private static Context mContext;
@Override
public void onCreate() {
mContext = getApplicationContext();
super.onCreate();
}
public static Context getContext() {
return mContext;
}
}
androidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.karent.slide">
<application
android:name=".util.MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@android:style/Theme.NoTitleBar">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
只要在下面使用android:name=”.util.MyApplication”那么我们自己定义的Application就会覆盖系统默认的Application了