仿QQ6.0主页面侧滑效果

效果展示:

自定义SlidingMenu效果展示

实现方法:

实现的方式多种多样:
  2.1.1 自定义ViewGroup ,处理其onTouch事件
  2.1.2 FrameLayout + 手势处理类GestureDetector
  2.2.3 使用Google自带的DrawerLayout 对其进行修改
  2.2.4 继承自水平滚动HorizontalScrollView

代码编写

 第一步:创建自定义控件类 继承ViewGroup 

第二步:编写布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.wust.myslidingmenu.mySlidingMenu
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="A"
            android:background="#f00"/>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="B"
            android:background="#0f0"/>

    </com.wust.myslidingmenu.mySlidingMenu>

</LinearLayout>

第三步:写逻辑代码

思路:

1、先分别获取 内容 或 菜单 这两个子代(因为子代数量有限,所以不用遍历),这一步是在 onMeasure 方法里面做的

2、获取屏幕宽高,是在构造方法里做的

3、摆放布局,是在 onLayout 方法里做的

4、根据滑动像素移动

package com.wust.myslidingmenu;

import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;

public class mySlidingMenu extends ViewGroup {

    private View mMenu;
    private View mContent;
    private int mScreenWidth;
    private int mScreenHeight;
    private int menuWidth;
    private int contentWidth;

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

    public mySlidingMenu(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public mySlidingMenu(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;
        mScreenHeight = metrics.heightPixels;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //定义一下自己的宽高
        int width = 0;
        int height = 0;
        //获取菜单栏,我这里是以 TextView 为例进行讲解
        mMenu = getChildAt(0);
        //获取内容栏
        mContent = getChildAt(1);
        //测量两个子代的宽高 只有测量了才能获取子代的宽高
        measureChild(mMenu,widthMeasureSpec,heightMeasureSpec);
        measureChild(mContent,widthMeasureSpec,heightMeasureSpec);
        //设置菜单栏 内容栏的宽度,一般菜单栏要窄一点,所以要减去一个 400
        menuWidth = mMenu.getLayoutParams().width = mScreenWidth - 400;
        contentWidth = mContent.getLayoutParams().width = mScreenWidth;
        //设置自定义ViewGroup的宽高 这里得首先获取 屏幕的宽高
        width = menuWidth + contentWidth;
        height = mScreenHeight;
        setMeasuredDimension(width,height);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        mMenu.layout(-menuWidth,0,0,mScreenHeight);
        mContent.layout(0,0,contentWidth,mScreenHeight);
    }
}

做到这一步,我们的基础布局差不多可以出来了

实质图解

第四步:处理人机交互

谈到人机交互,必可少的 重写 onTouch() 事件

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                //处理手指按下事件,主要是获取一下 按下时的位置,其实我们只用关心 X 方向
                mStartX = (int) event.getX();
                mStartY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                int endX = (int) event.getX();
                //获取手指移动了多少像素
                int dx = endX - mStartX;
                if (dx > 0){
                    //手指往右滑动了,其实这里大家并不用刻意去记,写的时候自己试一下,不行就反过来呗
                    if (getScrollX()-dx < -menuWidth){
                        //如果发现滑动距离超过了 菜单栏的宽度,直接滚动到 菜单栏边界
                        scrollTo(-menuWidth,0);
                    }else {
                        scrollBy(-dx,0);
                    }
                }else {
                    //手指往左滑了
                    if (getScrollX()-dx > 0){
                        //如果发现滑动距离超过了 内容的宽度,直接滚动到 (0,0)
                        scrollTo(0,0);
                    }else {
                        scrollBy(-dx,0);
                    }
                }
                break;
        }
        return true;
    }

写到这里,你的屏幕是可以滑动了的

第五步:优化,让体验感更好

优化一:判断手指抬起时页面是否划过 二分之一,然后决定页面的关闭

               简单是实现,没有用scroller

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch(event.getAction()){
            、、、
            case MotionEvent.ACTION_UP:
            {
                if (getScrollX() < -menuWidth/2){
                    scrollTo(-menuWidth,0);
                }else {
                    scrollTo(0,0);
                }
            }
                break;
        }
        return true;
    }

      使用scroller来实现

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

        //创建一个 scroller
        mScroller = new Scroller(context);
    }

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch(event.getAction()){
            、、、
            case MotionEvent.ACTION_UP:
            {
                if (getScrollX() < -menuWidth/2){
                    mScroller.startScroll(getScrollX(),0,-(getScrollX()+menuWidth),0,300);
                    invalidate(); //这个函数不能掉,在他会反复掉computeScroll()来计算是否到达目的坐标
                }else {
                    mScroller.startScroll(getScrollX(),0,-getScrollX(),0,300);
                    invalidate();
                }
            }
                break;
        }
        return true;
    }

//系统自己会调用
@Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
            invalidate();
        }
    }

 对于scroller 的使用可以参照 Scroller与computeScroll处理滑动 

优化二:缩放功能

这个地方值的注意的就是 如果你上面 选用的是 scroller 方法实现的滚动 那这个缩放 就得 在 computeScroll()中也调用一次

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch(event.getAction()){
            case MotionEvent.ACTION_MOVE:
                int endX = (int) event.getX();
                //只要移动了就得缩放,所以缩放得放在这个地方
                //首先得确定缩放因子
                float scaleRate = Math.abs(getScrollX())/(float)(menuWidth);
                myScale(scaleRate);
                、、、
                break;
        }
        return true;
    }

private void myScale(float scaleRate) {
        mMenu.setScaleX((float) (0.7 + 0.3*scaleRate));
        mMenu.setScaleY((float) (0.7 + 0.3*scaleRate));

        mContent.setScaleX((float) (1.0 - 0.3*scaleRate));
        mContent.setScaleY((float) (1.0 - 0.3*scaleRate));
        mContent.setPivotX(0);
    }

//下面这个代码块是你用了scoller才需要的
@Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()){
            float scaleRate = Math.abs(getScrollX())/(float)(menuWidth);
            myScale(scaleRate);
            、、、
        }
    }

缩放效果展示

有偿提问

如果大家觉得这篇文章帮助你了,可以支持一下。

有偿提问

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

super码王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值