自定义view 之 仿QQ侧滑菜单

先看看QQ的侧滑效果

这里写图片描述

分析一下

  • 先上原理图(不知道能否表达的清楚 ==)
    这里写图片描述

-首先这里使用了 android 的HorizontalScrollView 水平滑动布局作为容器,当然我们需要继承它自定义一个侧滑视图
- 这个容器里面有一个父布局(一般用LinerLayout,本demo用的是),这个父布局里面有且只有两个子控件(布局),初始状态菜单页的位置在Y轴上存在偏移这样可以就可以形成主页叠在菜单页的上方的视觉效果;然后在滑动的过程程中 逐渐修正偏移,最后菜单页和主页并排排列。原理搞清了实现起来就不是事儿了……

具体实现

布局代码

 <fierce_luk.com.sideslipviewdemo2.SideslipView
        android:id="@+id/my_veiw"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none"
        luk:leftPanding="200dp">
        <!--如果菜单在左边直接用  LinearLayout-->
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/image2"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@mipmap/homepage"
                android:gravity="center"
                android:tag="0"
                android:text="菜单"
                android:textColor="@color/colorAccent"
                android:textSize="60sp" />
            <TextView
                android:id="@+id/image1"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/color1"
                android:gravity="center"
                android:tag="1"
                android:text="主页面"
                android:textColor="@color/colorAccent"
                android:textSize="60sp" />
        </FrameLayout>


        <!--<fragment-->
        <!--android:name="com.luk.bluetoothapp.fragment.MyBluetoothDevice"-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="match_parent"-->
        <!--android:tag="1" />-->
        <!--<fragment-->
        <!--android:name="com.luk.bluetoothapp.fragment.HomeFragment"-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="match_parent"-->
        <!--android:tag="0" />-->
    </fierce_luk.com.sideslipviewdemo2.SideslipView>

自定义的侧滑视图
最核心的部分

public class SideslipView extends HorizontalScrollView {

    private int mScreenWidth;//屏幕宽度
    private int mMenuLeftPadding;
    private int mBluetoothWidth;//菜单的宽度
    private int mHalfMenuWidth;

    View home;
    View bluetooth;

    protected boolean isOpen;
    protected boolean isFirst = true;

    public SideslipView(Context context) {
        // super 改 this
        this(context, null);
    }

    public SideslipView(Context context, AttributeSet attrs) {
        // super 改 this
        this(context, attrs, 0);
    }

    public SideslipView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //测量屏幕宽度
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics metrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(metrics);
        mScreenWidth = metrics.widthPixels;
        // mScreenWidth = context.getResources().getDisplayMetrics().widthPixels;
        Log.e("TAG", "MyScrollView: mScreenWidth" + mScreenWidth);
        //获取 自定义的属性值
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyScrollView);
        int n = a.length();
        for (int i = 0; i < n; i++) {
            int arrt = a.getIndex(i);
            switch (arrt) {
                case R.styleable.MyScrollView_leftPanding:
                    mMenuLeftPadding = (int) a.getDimension(R.styleable.MyScrollView_leftPanding, 0);
                    break;
                default:
                    break;
            }
        }
        Log.e("TAG", "MyScrollView: mMenuLeftPadding" + mMenuLeftPadding);
        a.recycle();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (isFirst) {
            //获取子布局 并设置宽度
            //如果菜单在左边 用LinearLayout就行
//            LinearLayout layout = (LinearLayout) this.getChildAt(0);
            /**此处因为 把侧边拉出页面设置在了右边 所有用 FrameLayout
             * 不然在设置偏移量时 隐藏的侧边菜单会跑到主页面的上面*/
            FrameLayout layout = (FrameLayout) this.getChildAt(0);
            home = layout.getChildAt(1);
            bluetooth = layout.getChildAt(0);
            LayoutParams params = new LayoutParams(mBluetoothWidth,
                    getResources().getDisplayMetrics().heightPixels);
            params.setMargins(mScreenWidth, 0, 0, 0);

            bluetooth.setLayoutParams(params);
            mBluetoothWidth = mScreenWidth - mMenuLeftPadding;
            home.getLayoutParams().width = mScreenWidth;
            bluetooth.getLayoutParams().width = mBluetoothWidth;
            mHalfMenuWidth = mBluetoothWidth / 2;

            isFirst = false;

        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        //首先隐藏 Bluetooth
        if (changed)
            this.scrollTo(0, mBluetoothWidth);
    }

    Animation anim;

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_UP:
                int scrollX = getScrollX();
                if (scrollX >= mHalfMenuWidth) {
                    Log.e("TAG", "====");
                    this.smoothScrollTo(mBluetoothWidth, 0);
                    isOpen = true;
                } else {
                    this.smoothScrollTo(0, 0);
                    isOpen = false;
                }
                //必须消耗事件
                return true;
        }
        return super.onTouchEvent(ev);
        //return true;
    }


    /**
     * 打开菜单栏
     */
    protected void openMenu() {
        if (isOpen)
            return;
        this.smoothScrollTo(mBluetoothWidth, 0);
        isOpen = true;
    }

    /**
     * 关闭菜单栏
     */
    protected void closeMenu() {
        if (!isOpen)
            return;
        this.smoothScrollTo(0, 0);
        isOpen = false;
    }

    /**
     * 按钮切换菜单
     */
    public void toggleMenu() {
        if (isOpen)
            closeMenu();
        else
            openMenu();
    }

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

        super.onScrollChanged(l, t, oldl, oldt);
//此处 l 起始值为零(没有偏移)
        Log.e("TAG", "l=" + l + " t=" + t);
        Log.e("TAG", "oldl=" + oldl + " oldt=" + oldt);
        // scale 在 1 到 0 之间
        float scale = l * 1.0f / mBluetoothWidth;
        /**
         * 抽屉式侧滑
         * scaleLeft  从默认偏移量到偏移量 为零
         *实现
         * */
        float scaleLeft = 0.4f - 0.4f * scale;
        /**设置 X 轴方向的偏移量**/
        ViewHelper.setTranslationX(bluetooth, -(mBluetoothWidth * scaleLeft));
        Log.e("TAG", "mBluetoothWidth+" + mBluetoothWidth);
        Log.e("TAG", "=============" + mBluetoothWidth * scale + "scale" + scale);

/*
        *//**设置缩放时的 轴心点**//*
        //此处轴心为右边界的中点
        ViewHelper.setPivotY(home, mScreenWidth);
        ViewHelper.setPivotX(home, home.getHeight() / 2);
        *//**设置 XY轴方向的 缩放动画(从 1 到 0.9)**//*
        float pivoXY = 1 - 0.4f * scale;
        ViewHelper.setScaleX(home, pivoXY);
        ViewHelper.setScaleY(home, pivoXY);*/
/*
        *//**设置透明度**//*
        //从 1 到 0.6;
        float alpha = 1 - 0.4f * scale;
        ViewHelper.setAlpha(home, alpha);*/

    }

}

扩展

添加之定义属性 让用户配置菜单距离右边的边距的值;
首先在values文件夹下新建一个attr.xml,写入以下内容:

<resources>
    <declare-styleable name="MyScrollView">
        <attr name="rightPanding" format="dimension" />
        <attr name="leftPanding" format="dimension" />
    </declare-styleable>
</resources>

在布局里设置边距

 <fierce_luk.com.sideslipviewdemo2.SideslipView
        android:id="@+id/my_veiw"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none"
        luk:leftPanding="200dp">

-

- 源码下载Demo源码点击下载

  • 其他的就不赘述了,看看效果(主页加了透明度这样可以清楚的看到两个页面位置的关系)
    这里写图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值