04.自定义View(SlidingView仿QQ侧滑)

感谢红橙Darren博主

布局文件中



    
        
            
                
            
            
                
            
        
    


package com.rzm.commonlibrary.views;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;

import com.rzm.commonlibrary.R;
import com.rzm.commonlibrary.utils.LogUtils;

/**
 * Created by renzhenming on 2018/3/28.
 *
 * SlidingView继承自HorizontalScrollView,所以在布局中使用的时候,需要设置一个唯一的子View,然后
 * 往这个子 view中添加侧边栏和主栏
 */

public class SlidingView extends HorizontalScrollView{

    private final String TAG = getClass().getSimpleName();

    //侧滑菜单的宽度
    private final int mMenuWidth;

    private final Context mContext;

    //默认的侧边栏滑出最大位置距离右边屏幕的距离
    private float mMenuPaddingRight = 100;

    //侧边菜单布局
    private View mMenuView;

    //主页内容布局,包括用户的contentView和我们添加的阴影效果
    private ViewGroup mContentView;

    //侧边栏menu是否打开
    private boolean mMenuOpened;

    //设置抽屉模式
    private boolean mParallaxMode;

    //手势监听器
    private GestureDetector mGestureDetector;

    //快速滑动打开收起侧边栏的敏感指数,越大敏感度越小,约不容易打开关闭
    //不能过大也能过小,
    private float mSensitivity = 1000;

    //设置阴影
    private ImageView mShadowIv;

    //视差模式下,默认的侧边栏缩进的距离相对于侧边栏宽度的比例
    private float mTransitionPercent = 0.8f;

    //阴影透明度能达到的最深颜色值
    private String mAlphaColor="#99000000";

    public SlidingView(Context context) {
        this(context,null);
    }

    public SlidingView(Context context, AttributeSet attrs) {
        this(context, attrs,-1);
    }

    public SlidingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mContext = context;

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SlidingView);
        mMenuPaddingRight = typedArray.getDimension(R.styleable.SlidingView_menuPaddingRight, dp2px((int) mMenuPaddingRight));
        mParallaxMode = typedArray.getBoolean(R.styleable.SlidingView_menuParallax, mParallaxMode);

        //侧边栏的宽度
        mMenuWidth = (int) (getScreenWidth() - mMenuPaddingRight);
        typedArray.recycle();

        mGestureDetector = new GestureDetector(context, new GestureListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //如果需要GestureDetector处理,则由其处理
        if (mGestureDetector.onTouchEvent(ev)){
            return mGestureDetector.onTouchEvent(ev);
        }

        switch (ev.getAction()){
            case MotionEvent.ACTION_UP:
                //这个位置指的是,当前这个view和手机屏幕左边届交点距离view最左侧的距离,这里最小为0,不会为负
                //当侧边栏滑出的过程中,scrollX呈递减的趋势,直到=0
                int scrollX = getScrollX();
                LogUtils.d(TAG,"scrollX:"+scrollX);
                if (scrollX > mMenuWidth/2){
                    //滑出了一部分,但是还没有到宽度的1/2
                    closeMenu();
                }else{
                    openMenu();
                }
                return false;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 打开侧边栏
     */
    private void openMenu() {
        smoothScrollTo(0,0);
        mMenuOpened = true;
    }

    /**
     * 关闭侧边栏
     */
    private void closeMenu() {
        smoothScrollTo(mMenuWidth,0);
        mMenuOpened = false;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        //默认设置侧滑菜单隐藏
        if (changed){
            //scrollTo 让当前屏幕左边界滑动到(mMenuWidth,0)这个坐标位置
            scrollTo(mMenuWidth, 0);
        }
    }


    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        //获取根view
        ViewGroup rootView = (ViewGroup) getChildAt(0);

        int childCount = rootView.getChildCount();
        if (childCount > 2){
            throw new IllegalStateException("you should add two child view at most");
        }

        //获取两个布局
        mMenuView = rootView.getChildAt(0);

        //给内容添加阴影效果
        mContentView = new FrameLayout(mContext);
        ViewGroup.LayoutParams contentParams = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
        mContentView.setLayoutParams(contentParams);
        //获取原来的内容布局,并把原来的内容布局从LinearLayout中异常
        View oldContentView = rootView.getChildAt(1);
        rootView.removeView(oldContentView);

        //把原来的内容View 和 阴影加到我们新创建的内容布局中
        mContentView.addView(oldContentView);
        //创建阴影
        mShadowIv = new ImageView(mContext);
        mShadowIv.setBackgroundColor(Color.parseColor(mAlphaColor));
        mContentView.addView(mShadowIv);
        //把包含阴影的新的内容View 添加到 LinearLayout中
        rootView.addView(mContentView);

        //设置二者宽度
        mMenuView.getLayoutParams().width =  mMenuWidth;
        mContentView.getLayoutParams().width = getScreenWidth();
    }

    private float dp2px(int dp){
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,getResources().getDisplayMetrics());
    }

    /**
     * 屏幕宽度
     * @return
     */
    public int getScreenWidth(){
        Resources resources = getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        return metrics.widthPixels;
    }

    private class GestureListener extends GestureDetector.SimpleOnGestureListener{
        // 处理手势快速滑动
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

            //velocityX,velocityY都是朝坐标系正方向滑动为正,负方向为负,表示正方向上单位时间的速度
            //velocityX为正,表示单位时间内,
            LogUtils.d(TAG,"velocityX:"+velocityX+",velocityY:"+velocityY);

            if (mMenuOpened){
                if (velocityX < -mSensitivity){
                    toggleMenu();
                    return true;
                }
            }else{
                if (velocityX>mSensitivity){
                    toggleMenu();
                    return true;
                }
            }

            return super.onFling(e1, e2, velocityX, velocityY);
        }
    }
    /**
     * 切换菜单的状态
     */
    private void toggleMenu() {
        if(mMenuOpened){
            closeMenu();
            LogUtils.d(TAG,"close");
        }else{
            openMenu();
            LogUtils.d(TAG,"open");
        }
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);

        // l 是 当前滚动的x距离 指的是当前view和屏幕左侧边界相交点距离view最左侧的距离
        LogUtils.e(TAG,"onScrollChanged:l===="+l);

        if (mParallaxMode) {
            //在onLayout中,默认会将这个SlidingView向左移动一个侧边栏宽度
            //这个时候onScrollChanged会被调用,这时候会执行这行代码,将侧边栏向x轴正方向移动一定比例的l宽度,实际上
            //也此时,侧边栏已经被放在了主也contentView的下边,此时,滑动contentView的时候,会达到一种视觉差异效果
            mMenuView.setTranslationX(l * mTransitionPercent);
        }
        //给内容添加阴影效果 - 计算梯度值
        float gradientValue = l * 1f / mMenuWidth;
        LogUtils.e(TAG,"gradientValue:"+gradientValue);

        //这是 1 - 0 变化的值
        //给内容添加阴影效果 - 给阴影的View指定透明度 0 - 1 变化的值
        float shadowAlpha = 1 - gradientValue;

        LogUtils.e(TAG,"shadowAlpha:"+shadowAlpha);
        mShadowIv.setAlpha(shadowAlpha);
    }

}

你可能感兴趣的:(04.自定义View(SlidingView仿QQ侧滑))