安卓侧边栏实现

安卓侧滑布局实现

来,先上效果图
这里写图片描述

package xiaolin.widget;

import android.content.Context;;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;

import xiaolin.utils.Utils;

/**
 * 只有平移效果的侧边栏
 * Created by XiaoLin on 2016/1/14.
 */
public class ATranslationLayout extends ViewGroup {

    public static final String TAG = "ATranslationLayout";
    private static final int MININERCEPTXVILOCITY = 8;   // 最小拦截速度(dp)
    private static final int MINTOUCHVILOCITY = 300;     // 最小触发速度(dp)
    private static final float drawerTime = 2.5f;       // 动画时间

    private Scroller mScroller = null;
    private VelocityTracker mVelocity = null;
    private OnTranslationListener mOnTranslationListener = null;

    private boolean isTouchRight = false;
    private boolean isBeingDragged = false;
    private boolean isUnableToDragged = false;
    private PointF mLastInterceptPointF = new PointF();
    private float mLastTouchX = 0;
    private float mFirstTouchX = 0;

    protected int menuWidth = 0;                    // 菜单宽度
    protected boolean mIsOpenMenu = false;       // 是否显示菜单
    protected boolean isDraggedToRight = false;            // 是否向右边移动

    public ATranslationLayout(Context context) {
        super(context);
        init();
    }

    public ATranslationLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ATranslationLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mScroller = new Scroller(getContext());
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(width, height);
        menuWidth = (int) (width * 0.75f);         // 菜单宽度
        int childCount = getChildCount();
        if (childCount == 0) {
            return;
        }
        View menuView = getChildAt(0);
        // 合成菜单的测量宽度
        int menuWidthSpec = MeasureSpec.makeMeasureSpec(menuWidth, MeasureSpec.EXACTLY);
        int menuheightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
        menuView.measure(menuWidthSpec, menuheightSpec);
        for (int i = 1; i < childCount; i++) {
            View childView1 = getChildAt(i);
            childView1.measure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        if (childCount == 0) {
            return;
        }
        View menuView = getChildAt(0);
        // 设置子视图的位置,左上角坐标,宽高
        menuView.layout(-menuView.getMeasuredWidth(), 0, 0, menuView.getMeasuredHeight());

        int left = 0;
        for (int i = 1; i < childCount; i++) {
            View childView = getChildAt(i);
            int w = childView.getMeasuredWidth();
            childView.layout(left, 0, left + w, childView.getMeasuredHeight());
            left += w;
        }
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            int x = mScroller.getCurrX();
            scrollTo(x, 0);
            // 调用invalidate()无效
            postInvalidate();
            if (mOnTranslationListener != null) {
                mOnTranslationListener.progress(isDraggedToRight, -x / (float)menuWidth);
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        int action = event.getAction();
        PointF pointF = new PointF(event.getX(), event.getY());
        if (action != 0) {
            if (isBeingDragged) {
                return true;
            }
            if (isUnableToDragged) {
                return false;
            }
        }
        if (action == MotionEvent.ACTION_DOWN) {
            mLastInterceptPointF.set(pointF);
            isBeingDragged = false;
            isUnableToDragged = false;
            if(mScroller.isFinished()){
                isDraggedToRight = !isOpenMenu();
            }
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
                return true;
            } else if (mIsOpenMenu) {
                if (event.getX() > menuWidth) {
                    isBeingDragged = true;
                }
            }
        } else if (action == MotionEvent.ACTION_MOVE) {
            float dx = Math.abs(pointF.x - mLastInterceptPointF.x);
            float dy = Math.abs(pointF.y - mLastInterceptPointF.y);
            if (dy > dx) {  // 上下滑动
                isUnableToDragged = true;
            } else if (pointF.x < mLastInterceptPointF.x && !mIsOpenMenu) { // 往左滑动&&已关窗
                isUnableToDragged = true;
            } else if (pointF.x > mLastInterceptPointF.y && mIsOpenMenu) {    // 往右滑动&&已开窗
                isUnableToDragged = true;
            } else if (dx > dy && dx > Utils.dpToPx(MININERCEPTXVILOCITY, getResources())) {  // 往右并且大于一定速度
                isBeingDragged = true;
            }
            mLastInterceptPointF.x = pointF.x;
            mLastInterceptPointF.y = pointF.y;
        }
        return isBeingDragged;
    }

    // 触摸事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        float x = event.getX();
        if (mVelocity == null) {
            mVelocity = VelocityTracker.obtain();
            mLastTouchX = x;
            if (mIsOpenMenu && x > menuWidth) {
                isTouchRight = true;
                mFirstTouchX = x;
            } else {
                isTouchRight = false;
            }
        }
        mVelocity.addMovement(event);
        if (action == MotionEvent.ACTION_DOWN) {// 可能无此动作

        } else if (action == MotionEvent.ACTION_MOVE) { // 正在滑动
            float dx = x - mLastTouchX;
            drawerViewBy((int) dx);
            mLastTouchX = x;
        } else if (action == MotionEvent.ACTION_UP) {
            if (isTouchRight && mFirstTouchX == x) {
                openMenu(false);
                mVelocity.recycle();
                mVelocity = null;
                return true;
            } else {
                mVelocity.computeCurrentVelocity(1000);
                if (mVelocity.getXVelocity() < -Utils.dpToPx(MINTOUCHVILOCITY, getResources())) {
                    openMenu(false);
                } else if (mVelocity.getXVelocity() > Utils.dpToPx(MININERCEPTXVILOCITY, getResources())) {
                    openMenu(true);
                } else {
                    if (-getScrollX() > menuWidth / 2) {
                        openMenu(true);
                    } else {
                        openMenu(false);
                    }
                }
                mVelocity.recycle();
                mVelocity = null;
            }
        }
        return true;
    }

    // 按键监听
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        int code = event.getKeyCode();
        int action = event.getAction();
        if (code == KeyEvent.KEYCODE_BACK && action == KeyEvent.ACTION_UP) {
            if (mIsOpenMenu) {
                openMenu(false);
                return true;
            }
        }
        return super.dispatchKeyEvent(event);
    }

    // 滚动距离并判断范围
    protected void drawerViewBy(int dx) {
        dx = -dx;
        int x = getScrollX();
        if (x + dx > 0) {
            scrollTo(0, 0);
        } else if (x + dx < -menuWidth) {
            scrollTo(-menuWidth, 0);
        } else {
            scrollTo(x + dx, 0);
        }
        if (mOnTranslationListener != null) {
            mOnTranslationListener.progress(isDraggedToRight, -getScrollX() / (float) menuWidth);
        }
    }

    public void openMenu(Boolean isOpen) {
        mIsOpenMenu = isOpen;
        int x = getScrollX();
        if (isOpen) {
            int time = (int) (Math.abs(-menuWidth - x) / Utils.dpToPx(1f, getResources()) * drawerTime);
            mScroller.startScroll(x, 0, -menuWidth - x, 0, time);
            if (x == 0) {
                isDraggedToRight = true;
            }
        } else {
            int time = (int) (Math.abs(x) / Utils.dpToPx(1f, getResources()) * drawerTime);
            mScroller.startScroll(x, 0, -x, 0, time);
            if (x == -menuWidth) {
                isDraggedToRight = false;
            }
        }
        if (mOnTranslationListener != null) {
            mOnTranslationListener.fold(mIsOpenMenu);
        }
        postInvalidate();
    }

    // 是打开菜单
    public boolean isOpenMenu() {
        return mIsOpenMenu;
    }

    // 设置监听
    public void setFoldingListener(OnTranslationListener listener) {
        this.mOnTranslationListener = listener;
    }

    // 监听接口
    public interface OnTranslationListener {
        void fold(boolean isOpen);
        void progress(boolean isToRight, float progress);// 0.0f-1.0f
    }

}

package xiaolin.utils;

import android.content.res.Resources;
import android.util.TypedValue;

/**
 * Created by Administrator on 2016/1/17.
 */
public class Utils {

    /**
     * dp转为像素
     * @param dp
     * @param resources
     * @return
     */
    public static float dpToPx(float dp, Resources resources){
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.getDisplayMetrics());
    }
}


然后只要在xml里使用就行了,里面放的是侧边的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#cacaca">

    <xiaolin.widget.ATranslationLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            android:src="@drawable/bg" />

    </xiaolin.widget.ATranslationLayout>

</LinearLayout>

效果图在上面

如果里面添加两个布局的话就会变成这样

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#cacaca">

    <xiaolin.widget.ATranslationLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="centerCrop"
            android:src="@drawable/bg" />

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/img_bg"
            android:scaleType="centerCrop"/>

    </xiaolin.widget.ATranslationLayout>

</LinearLayout>

这里写图片描述
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值