側滑效果展示
原理
要實現這種側滑效果,首先自定義一個滑動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了