这个代码只能配本文的布局文件使用,因为在代码中写死了是SlidingMenu里是两个TextView。大家可以参考里面的滑动实现,还有测量和布局的实现。
这里采用了一个很笨的实现方案:即在整个ViewGroup向右滑动,为了保持MainView不动,我让MainView在ViewGroup向右滑动使Menu滑出时向左移动。其实可以采用只让MenuView(mSubView)动的方式去实现,只是想玩一下,看效果。
总结一下写这个View时应该注意的细节:
1.判断当前动作是向左还是向右,不能用ev.getX()-mLastX,因为两次的都是相等的,因为两次事件发生的太近了,最好使用VelocityTracker判断速度的方向(值的正负)。
2.如果只是需要ViewGroup的某个控件动,可以为某个控件设置一个Scroller(一般的View中都会维护一个mScroller成员,如果要用的话,用户就可以去设置),然后调用它的invalidate,一般的View都会把onComputeScroll()安照常规写好(如TextView),可参照下面mScrollerForMainView
package com.example.zhangjinbiao.myapplication;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
import android.widget.TextView;
public class SlideMenu extends ViewGroup {
// x轴的上一次位置
private float mLastx;
// 模拟弹性滑动
private Scroller mScrooler;
private Scroller mScrollerForMainView;
// 标记当前现实状态,默认为true显示的是主界面
private boolean mIsMain = true;
//main view,below to the subview
private View mMainView;
//subView,the menu,which is obove the main view
private View mSubView;
public SlideMenu(Context context) {
this(context, null);
}
public SlideMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mScrooler = new Scroller(context);
mScrollerForMainView = new Scroller(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/* super.onMeasure(widthMeasureSpec, heightMeasureSpec);
View left = this.getChildAt(0);
left.measure(left.getLayoutParams().width, heightMeasureSpec);
View main = this.getChildAt(1);
main.measure(widthMeasureSpec, heightMeasureSpec);*/
/* final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
final int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
final int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);*/
mMainView = getChildAt(0);
mSubView = getChildAt(1);
int height = 0;
int width = 0;
if(mMainView != null && mSubView != null){
measureChildren(widthMeasureSpec, heightMeasureSpec);
height = mMainView.getMeasuredHeight() > mSubView.getMeasuredHeight() ?
mMainView.getMeasuredHeight() : mSubView.getMeasuredHeight();
width = mMainView.getMeasuredWidth() > mSubView.getMeasuredWidth() ?
mMainView.getMeasuredWidth() : mSubView.getMeasuredWidth();
if(mMainView instanceof TextView){
TextView tv = (TextView)mMainView;
tv.setScroller(mScrollerForMainView);
}
}
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
/* View left = this.getChildAt(0);
left.layout(-left.getLayoutParams().width, 0, 0, b);
View main = this.getChildAt(1);
main.layout(l, t, r, b);*/
if(mMainView != null){
int right = r - l;
int bottom = b - t;
mMainView.layout(0, 0, right, bottom);
}
if (mSubView != null) {
int left = -(mSubView.getMeasuredWidth());
int right = left + mSubView.getMeasuredWidth();
int bottom = b - t;
mSubView.layout(left, 0, right, bottom);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN :
// getX是返回的相对于当前view左边缘的横轴距离,getRawx是返回的是相对于屏幕左边缘的距离,
// 因为这里的viewgroup是撑满全屏的所以用这两个方法的效果都是一样的
mLastx = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE :
float x = event.getX();
//上一次x轴位置减去当前X轴的值为这一次滑动的增量
float deltaX = mLastx - x;
//加上原本内容在X轴的偏移量得到它将要便宜的量
int scrollX = getScrollX() + (int) deltaX;
if (scrollX > 0) {
//大于0的时候代表侧滑菜单已经出来了所以直接scrollTo(0,0)即可
scrollTo(0, 0);
scrollToForMainView(0);
} else if (scrollX > -mSubView.getWidth()) {
//小于侧滑菜单的宽度的负值的时候则代表侧滑菜单已经完全隐藏起来了
scrollTo(scrollX, 0);
scrollToForMainView(-scrollX);
} /*else {
//此时侧滑菜单既没有完全隐藏也没有完全现实根据增量值进行正常的scrollBy即可
scrollBy((int) deltaX, 0);
}*/
/*int deltaX = mLastx - x;
//加上原本内容在X轴的偏移量得到它将要便宜的量
int scrollX = getScrollX() + deltaX;
if(-scrollX > mSubView.getWidth()/2){
//if scrollx larger than half of thd width of the menu, show the menu
}*/
mLastx = x;
break;
case MotionEvent.ACTION_UP :
// 获取手指抬起时候的内容偏移量
final int upX = getScrollX();
final float currentX = event.getX();
final float dx = mLastx - currentX;
if (upX < -mSubView.getWidth()/ 2) {
// 滑动距离大于了左侧菜单的一半则将菜单显示出来
switchToMenu();
} else {
// 反之菜单收起
switchToScreen();
}
break;
}
return true;
}
private void switchToScreen() {
int startX = getScrollX();// 起始的X轴偏移量
int deltaX;
deltaX = -startX;
mScrooler.startScroll(startX, 0, deltaX, 0, Math.abs(deltaX) * 3);
invalidate();
mIsMain = true;
scrollMainViewWhenSwitch();
getX();
}
@Override
public void computeScroll() {
if (mScrooler.computeScrollOffset()) {
scrollTo(mScrooler.getCurrX(), mScrooler.getCurrY());
//postInvalidate();
}
}
public void switchToMenu() {
int startX = getScrollX();// 起始的X轴偏移量
int deltaX;
// 如果要显示的是菜单界面则目标的偏移x点是菜单的左边界
/*deltaX = -getChildAt(0).getLayoutParams().width - startX;*/
deltaX = -mSubView.getWidth() - startX;
mScrooler.startScroll(startX, 0, deltaX, 0, Math.abs(deltaX) * 3);
invalidate();
mIsMain = false;
scrollMainViewWhenSwitch();
/* mIsMain = !mIsMain;
switchToScreen();*/
}
void scrollMainViewWhenSwitch(){
int mainViewStartX= mMainView.getScrollX();
int mainViewDeltaX;
if(mIsMain == true){
mainViewDeltaX = -mainViewStartX;
}else{
mainViewDeltaX = mSubView.getWidth()-mainViewStartX;
}
mScrollerForMainView.startScroll(mainViewStartX, 0, mainViewDeltaX, 0,Math.abs(mainViewDeltaX) * 3);
mMainView.invalidate();
}
void scrollToForMainView(int x){
mMainView.scrollTo(x, 0);
}
}
下面是MainActivity:
public class MainActivity extends Activity implements View.OnLayoutChangeListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sliding_menu);
}
}
下面是ViewGroup的布局文件
<?xml version="1.0" encoding="utf-8"?>
<com.example.zhangjinbiao.myapplication.SlideMenu xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimaryDark"
android:text="wo ai ni a hua qi lai lalala hello world"
android:gravity="center"/>
<TextView
android:layout_width="200dp"
android:layout_height="match_parent"
android:background="@color/colorAccent"
android:text="hi! i am menu"
android:gravity="center"/>
</com.example.zhangjinbiao.myapplication.SlideMenu>