先上效果图:(宽度是自适应的)
一步一步来啦!
工程的结构:
(1)布局文件,这里需要四个layout布局文件,left.xml,right.xml,center.xml和启动用的activity_main.xml
启动layout布局文件 activity_main.xml 代码如下,
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".MainActivity" >
<com.zhong.haolin_my_view.ui.MySlidingMenu
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/MySlidingMenu">
</com.zhong.haolin_my_view.ui.MySlidingMenu>
</FrameLayout>
主界面布局文件 center.xml 代码如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/center_fragment"
android:background="@color/bg">
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="中间center"/>
<Button
android:id="@+id/button1"
android:layout_width="130dip"
android:layout_height="wrap_content"
android:layout_marginTop="220dip"
android:text="Button" />
<Button
android:id="@+id/button2"
android:layout_width="130dip"
android:layout_height="wrap_content"
android:text="Button"
android:layout_marginTop="120dip"
android:layout_marginLeft="80dip" />
</FrameLayout>
左侧菜单布局文件 left.xml 代码如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/left_fragment"
android:background="@color/bg_left">
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="左边菜单left"/>
</FrameLayout>
右侧菜单布局文件 right.xml 代码如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/right_fragment"
android:background="@color/bg_right">
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="右边菜单right"/>
</FrameLayout>
(2)启动类MainActivity.java 代码如下:
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MySlidingMenu slidingMenu = (MySlidingMenu)findViewById(R.id.MySlidingMenu);
slidingMenu.setView(
getLayoutInflater().inflate(R.layout.left, null),
getLayoutInflater().inflate(R.layout.right, null),
getLayoutInflater().inflate(R.layout.center, null));
Button button = (Button)findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
slidingMenu.showLeft();
}
});
Button button2 = (Button)findViewById(R.id.button2);
button2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
slidingMenu.showRight();
}
});
}
}
(3)MySlidingMenu滑动类,继承自RelativeLayout,有两层布局,第一层是LeftLayout和RightLayout,第二层是MySlidingView,其实也就是一个FramLayout,代码如下:
public class MySlidingMenu extends RelativeLayout {
/**
* 左边菜单
*/
private View LeftLayout;
/**
* 右边菜单
*/
private View rightLayout;
/*
* 主界面
*/
private MySlidingView contenerlayout;
public MySlidingMenu(Context context) {
super(context);
}
public MySlidingMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MySlidingMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// 添加左菜单
private void setLeftLatout(View view){
LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.FILL_PARENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
super.addView(view, layoutParams);
LeftLayout = view;
}
// 添加右菜单
private void setRightLayout(View view){
LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.FILL_PARENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
super.addView(view, layoutParams);
rightLayout = view;
}
// 添加主界面
private void setContenerLayout(View view){
LayoutParams layoutParams = new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT);
contenerlayout = new MySlidingView(getContext());
super.addView(contenerlayout, layoutParams);
contenerlayout.setView(view);
contenerlayout.setLeftLayout(LeftLayout);
contenerlayout.setRightlayout(rightLayout);
contenerlayout.invalidate();
}
// 添加左菜单,右菜单,主界面
public void setView(View leftView, View rightView, View mainView){
setLeftLatout(leftView);
setRightLayout(rightView);
setContenerLayout(mainView);
}
// 显示左菜单
public void showLeft(){
contenerlayout.showLeftView();
}
// 显示右菜单
public void showRight(){
contenerlayout.showRightView();
}
}
上面的代码还是比较简单,接下来才是重头菜,真正实现滑动的MySlidingView
(4)MySlidingView.java 代码如下:
public class MySlidingView extends ViewGroup {
/**
* 主布局
*/
private FrameLayout mContainer;
/**
* 超过此距离,控件才允许滑动
*/
private int mTouchSlop;
/**
* 记录是否能滑动布局
*/
private boolean mIsBeingDragged ;
/**
* 记录最后的一次触屏
*/
private float mLastMotionX ;
private float mLastMotionY ;
/**
* 平滑组件
*/
private Scroller mScroller;
/**
* 左侧菜单
*/
private View leftLayout;
/**
* 右侧菜单
*/
private View rightlayout;
/**
* 速度
*/
private static final int SNAP_VELOCITY = 100;
public MySlidingView(Context context) {
super(context);
init();
}
public MySlidingView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MySlidingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
// 同时绘制mContainer
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// L.i(getClass(), "onMeasure[widthMeasureSpec:"+widthMeasureSpec+",heightMeasureSpec:"+heightMeasureSpec+"]");
mContainer.measure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// L.i(getClass(), "onLayout[changed:"+changed+",left:"+left+",top:"+top+",right:"+right+",bottom:"+bottom+"]");
int width = right - left;
int height = bottom - top;
mContainer.layout(0, 0, width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
// end
private void init(){
mContainer = new FrameLayout(getContext());
mScroller = new Scroller(getContext());
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
super.addView(mContainer);
}
public void setView(View view) {
if(mContainer.getChildCount() > 0)
mContainer.removeAllViews();
mContainer.addView(view);
}
@Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
postInvalidate();// 刷新UI可以在UI中刷新
}
// 整个viewgroup一起滑动
@Override
public void computeScroll() {
if (!mScroller.isFinished()) {
if (mScroller.computeScrollOffset()) {
int oldX = getScrollX();
int oldY = getScrollY();
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
scrollTo(x, y);
}
// Keep on drawing until the animation has finished.
invalidate();
} else {
clearChildrenCache();
}
} else {
clearChildrenCache();
}
}
// 手指滑动检测,来决定viewgroup是否能滑动
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
// 记录触摸位置
mLastMotionX = x;
mLastMotionY = y;
mIsBeingDragged = false;
break;
case MotionEvent.ACTION_MOVE:
final float dx = x - mLastMotionX;
final float xDiff = Math.abs(dx);
final float yDiff = Math.abs(y - mLastMotionY);
// xDiff 要大于系统设置的这个距离,控件才能移动
// xDiff > yDiff 手指是否滑动
if (xDiff > mTouchSlop && xDiff > yDiff) {
mIsBeingDragged = true;
mLastMotionX = x;
}
break;
}
return mIsBeingDragged;
}
// 滑动
@Override
public boolean onTouchEvent(MotionEvent event) {
// 添加速度组件跟踪速度变化,计算出event的瞬时伪速度.再此不使用
// if(mVelocityTracker == null)
// mVelocityTracker = VelocityTracker.obtain();
// mVelocityTracker.addMovement(event);
final float x = event.getX();
final float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 正在滑动时,强制停止滑动
if(!mScroller.isFinished())
mScroller.abortAnimation();
mLastMotionX = x;
mLastMotionY = y;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_MOVE:
if(mIsBeingDragged){
final float disX = mLastMotionX - x;//手指滑动的距离
mLastMotionX = x;
float oldScroll = getScrollX(); //现有的位置坐标
float scrollX = oldScroll + disX; //要滑动到的坐标
// 向右滑,显示左界面
if(disX<0 && scrollX<0){
// L.i(getClass(), "[ 向右滑,显示左界面 ]");
float leftBound = -leftLayout.getWidth();
float rightBound = 0;
if(scrollX > rightBound) scrollX = rightBound;
if(scrollX < leftBound) scrollX = leftBound;
}
// 想左滑,显示右界面
else if(disX>0 && scrollX>0){
// L.i(getClass(), "[ 向左滑,显示右界面 ]");
float leftBound = 0;
float rightBound = rightlayout.getWidth();
if(scrollX<leftBound) scrollX = leftBound;
if(scrollX>rightBound) scrollX = rightBound;
}
// L.i(getClass(), "scrollTo[scrollX:"+scrollX+"]");
// 主要用于滑动和绝对固定,否则会超出左右菜单的边界
scrollTo((int) scrollX, getScrollY());
}
break;
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
int oldScroll = getScrollX();
int dx = 0;
// L.i(getClass(), "ACTION_MOVE[oldScroll:"+oldScroll+",volecityX:"+volecityX+"]");
// L.i(getClass(), "ACTION_MOVE[leftLayout.getWidth() / 2:"+leftLayout.getWidth() / 2
// +",rightlayout.getWidth() / 2:"+rightlayout.getWidth() / 2+"]");
if(oldScroll < 0){
// 显示左边菜单
if(oldScroll < -leftLayout.getWidth() / 2 ){
dx = -leftLayout.getWidth() - oldScroll;
// L.i(getClass(), "ACTION_MOVE[显示左边菜单]");
}
// 回归原位
else if(oldScroll >= -leftLayout.getWidth() / 2 ){
dx = - oldScroll;
// L.i(getClass(), "ACTION_MOVE[显示左边菜单 回归原位]");
}
}
// 判断往左滑
else{
// 显示右边菜单
if(oldScroll > rightlayout.getWidth() / 2 ){
dx = rightlayout.getWidth() - oldScroll;
// L.i(getClass(), "ACTION_MOVE[显示右边菜单]");
}
// 回归原位
else if(oldScroll <= rightlayout.getWidth() / 2 ){
dx = -oldScroll;
// L.i(getClass(), "ACTION_MOVE[显示右边菜单 回归原位]");
}
}
// L.i(getClass(), "ACTION_MOVE[dx:"+dx+"]");
// 保证只有三种状态。显示左界面、主界面、右界面中的一种,而不是只显示界面的一半
// 移动dx距离
smoothScrollTo(dx);
clearChildrenCache();
}
break;
}
return true;
}
// ViewGroup 平滑
void smoothScrollTo(int dx) {
int duration = 500;
mScroller.startScroll(getScrollX(), getScrollY(), dx, getScrollY(), duration);
invalidate();
}
// 显示左边菜单
public void showLeftView(){
// L.i(getClass(), "showLeftView()");
int x = getScrollX();
int leftWith = leftLayout.getWidth();
if(x == 0){// 展开左菜单
smoothScrollTo(-leftWith);
}//已经展开左菜单了,收缩左菜单
else if(x == -leftWith){
smoothScrollTo(leftWith);
}
}
// 显示右边菜单
public void showRightView(){
// L.i(getClass(), "showRightView()");
int x = getScrollX();
int rightWidth = rightlayout.getWidth();
if(x == 0){// 展开右菜单
smoothScrollTo(rightWidth);
}//已经展开右菜单了,收缩右菜单
else if(x == rightWidth){
smoothScrollTo(-rightWidth);
}
}
// 设置允许绘制
void enableChildrenCache(){
int childCount = getChildCount();
for(int i=0;i < childCount;i++){
final View view = getChildAt(i);
view.setDrawingCacheEnabled(true);
}
}
// 设置不允许绘制
void clearChildrenCache(){
int childCount = getChildCount();
for(int i=0;i<childCount;i++){
final View view = getChildAt(i);
view.setDrawingCacheEnabled(false);
}
}
public View getLeftLayout() {
return leftLayout;
}
public void setLeftLayout(View leftLayout) {
this.leftLayout = leftLayout;
}
public View getRightlayout() {
return rightlayout;
}
public void setRightlayout(View rightlayout) {
this.rightlayout = rightlayout;
}
}
打完收工。see you !