Android开发侧滑效果的实现
发布日期:2021-06-28 21:30:30 浏览次数:2 分类:技术文章

本文共 9016 字,大约阅读时间需要 30 分钟。

在公司做开发的时候接到一个任务就是实现侧滑效果(酷狗比较老的版本的那种策划效果,QQ6.0以上的版本也是侧滑效果),遇到这种侧滑效果的话怎么实现?

首先应该考虑到以下几种办法:

1,使用系统自带的DrawerLayout这个控件

2,自定义ViewGroup+手势处理类(实现起来代码量有点多)

3,自定义ScrollView

在这里我们使用第三种方式自定义ScrollView的方式来实现。首先我们可以直观的想一下侧滑效果的布局,手指按在屏幕上往右滑,此时下层的布局就会展示出来,并且由小到大,上面的能够往右滑的这个布局逐渐变小,现在也就明白了,我们需要定义两个布局,假设上面的布局叫做内容布局,命名为layout_home_content,下面的布局命名为layout_home_menu,先暂时不考虑缩放问题,当前首要的任务就是把两个布局实现,并且能够滑动。下面是两个布局的具体实现代码:

layout_home_content.xml(这个布局很简单,只放了一个TextView)

layout_home_menu.xml(这个布局相对复杂一点,主要分为三大块,最上面是用户信息,中间放了一个ListView来显示能够点击的条目,最下面是退出按钮)

在定义完了上面两个布局之后,在主布局activity_main中引用这两个布局,其中SlidingMenu就是我们自定义的继承自ScrollView的滑动布局,如果你的包名和我的不一样,需要修改为你的完整包名。

好了,现在我们把布局绘制完成了(其中布局中的图片资源不再上传,大家可以随便找一个代替),现在运行一下:

为什么出现这样的结果呢?home_content布局这么窄的原因是因为home_content布局的宽高自定为wrap_content,放放在自定义的ScrollView中形成的。

接下来开始解决问题:

1,自定内容页面的宽度:

这里需要用到一个onFinishInflate(),这个方法是在布局解析完成后调用

rt android.view.ViewGroup;import android.view.WindowManager;import android.widget.HorizontalScrollView;/** * Created by yly on 2019/8/10. */public class SlidingMenu extends HorizontalScrollView {    private int menuWidth;    public SlidingMenu(Context context) {        super(context);    }    public SlidingMenu(Context context, AttributeSet attrs) {        super(context, attrs);    }    public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        //获取自定义的右边一小块的宽度        TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.SlidingMenu);        float rightMargin=typedArray.getDimension(R.styleable.SlidingMenu_menuRightMargin,dip2px(context,50));        typedArray.recycle();        menuWidth= (int) (getScreenWidth(context)-rightMargin);    }    @Override    protected void onFinishInflate() {        super.onFinishInflate();        //1,指定内容页面的宽度是整个屏幕的宽度,       ViewGroup container= (ViewGroup) getChildAt(0);//这是SlidingMenu布局下的第一层LinearLayout布局        int childCount=container.getChildCount();        if (childCount>2){            throw new RuntimeException("不能放置超过两个布局");        }        // 2,菜单页面的宽度等于屏幕的宽度减去右边一小步分的宽度(menuRightMargin)        View menuView=container.getChildAt(0);        ViewGroup.LayoutParams menuLayoutParams=menuView.getLayoutParams();        menuLayoutParams.width=menuWidth;        menuView.setLayoutParams(menuLayoutParams);        //7.0以下的手机不加这一行是可以的,但是高版就不行了        View contentView=container.getChildAt(1);        ViewGroup.LayoutParams contentLayoutParams=contentView.getLayoutParams();        contentLayoutParams.width=getScreenWidth(getContext());        contentView.setLayoutParams(contentLayoutParams);    }    private int dip2px(Context context, float dpValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (dpValue * scale + 0.5f);    }    /**     * 获得屏幕高度     *     * @param context     * @return     */    private int getScreenWidth(Context context) {        WindowManager wm = (WindowManager) context                .getSystemService(Context.WINDOW_SERVICE);        DisplayMetrics outMetrics = new DisplayMetrics();        wm.getDefaultDisplay().getMetrics(outMetrics);        return outMetrics.widthPixels;    }}

好了,现在运行一下,内容页面正常显示了。接下来处理页面滑动,和缩放问题:(代码中有详细的注释)

package com.example.myproject;        import android.content.Context;        import android.content.res.TypedArray;        import android.support.v4.view.ViewCompat;        import android.util.AttributeSet;        import android.util.DisplayMetrics;        import android.util.Log;        import android.view.MotionEvent;        import android.view.View;        import android.view.ViewGroup;        import android.view.WindowManager;        import android.widget.HorizontalScrollView;/** * Email 240336124@qq.com * Created by Darren on 2017/6/19. * Version 1.0 * Description: */public class SlidingMenu extends HorizontalScrollView {    // 菜单的宽度    private int mMenuWidth;    private View mContentView,mMenuView;    // GestureDetector 处理快速滑动    public SlidingMenu(Context context) {        this(context, null);    }    public SlidingMenu(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        // 初始化自定义属性        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlidingMenu);        float rightMargin = array.getDimension(                R.styleable.SlidingMenu_menuRightMargin, dip2px(context, 50));        // 菜单页的宽度是 = 屏幕的宽度 - 右边的一小部分距离(自定义属性)        mMenuWidth = (int) (getScreenWidth(context) - rightMargin);        array.recycle();    }    // 1.宽度不对(乱套了),指定宽高    @Override    protected void onFinishInflate() {        // 这个方法是布局解析完毕也就是 XML 布局文件解析完毕        super.onFinishInflate();        // 指定宽高 1.内容页的宽度是屏幕的宽度        // 获取LinearLayout        ViewGroup container = (ViewGroup) getChildAt(0);        int childCount = container.getChildCount();        if (childCount != 2) {            throw new RuntimeException("只能放置两个子View!");        }        mMenuView = container.getChildAt(0);        // 设置只能通过 LayoutParams ,        ViewGroup.LayoutParams menuParams = mMenuView.getLayoutParams();        menuParams.width = mMenuWidth;        // 7.0 以下的手机必须采用下面的方式        mMenuView.setLayoutParams(menuParams);        // 2.菜单页的宽度是 屏幕的宽度 - 右边的一小部分距离(自定义属性)        mContentView = container.getChildAt(1);        ViewGroup.LayoutParams contentParams = mContentView.getLayoutParams();        contentParams.width = getScreenWidth(getContext());        mContentView.setLayoutParams(contentParams);        // 2. 初始化进来是关闭 发现没用        // scrollTo(mMenuWidth,0);    }    // 4. 处理右边的缩放,左边的缩放和透明度,需要不断的获取当前滚动的位置    @Override    protected void onScrollChanged(int l, int t, int oldl, int oldt) {        super.onScrollChanged(l, t, oldl, oldt);        Log.e("TAG", "l -> " + l);// 变化是 mMenuWidth - 0        // 算一个梯度值        float scale = 1f * l / mMenuWidth;// scale 变化是 1 - 0        // 右边的缩放: 最小是 0.7f, 最大是 1f        float rightScale = 0.7f + 0.3f * scale;        // 设置右边的缩放,默认是以中心点缩放        // 设置缩放的中心点位置        ViewCompat.setPivotX(mContentView,0);        ViewCompat.setPivotY(mContentView, mContentView.getMeasuredHeight() / 2);        ViewCompat.setScaleX(mContentView,rightScale);        ViewCompat.setScaleY(mContentView, rightScale);        // 菜单的缩放和透明度        // 透明度是 半透明到完全透明  0.5f - 1.0f        float leftAlpha = 0.5f + (1-scale)*0.5f;        ViewCompat.setAlpha(mMenuView,leftAlpha);        // 缩放 0.7f - 1.0f        float leftScale = 0.7f + (1-scale)*0.3f;        ViewCompat.setScaleX(mMenuView,leftScale);        ViewCompat.setScaleY(mMenuView, leftScale);        // 最后一个效果 退出这个按钮刚开始是在右边,安装我们目前的方式永远都是在左边        // 设置平移,先看一个抽屉效果        // ViewCompat.setTranslationX(mMenuView,l);        // 平移 l*0.7f        ViewCompat.setTranslationX(mMenuView, 0.25f*l);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);        // 2. 初始化进来是关闭        scrollTo(mMenuWidth, 0);    }    // 3.手指抬起是二选一,要么关闭要么打开    @Override    public boolean onTouchEvent(MotionEvent ev) {        // 1. 获取手指滑动的速率,当期大于一定值就认为是快速滑动 , GestureDetector(系统提供好的类)        // 2. 处理事件拦截 + ViewGroup 事件分发的源码实践        //    当菜单打开的时候,手指触摸右边内容部分需要关闭菜单,还需要拦截事件(打开情况下点击内容页不会响应点击事件)        if (ev.getAction() == MotionEvent.ACTION_UP) {            // 只需要管手指抬起 ,根据我们当前滚动的距离来判断            int currentScrollX = getScrollX();            if (currentScrollX > mMenuWidth / 2) {                // 关闭                closeMenu();            } else {                // 打开                openMenu();            }            // 确保 super.onTouchEvent() 不会执行            return true;        }        return super.onTouchEvent(ev);    }    /**     * 打开菜单 滚动到 0 的位置     */    private void openMenu() {        // smoothScrollTo 有动画        smoothScrollTo(0, 0);    }    /**     * 关闭菜单 滚动到 mMenuWidth 的位置     */    private void closeMenu() {        smoothScrollTo(mMenuWidth, 0);    }    /**     * 获得屏幕高度     *     * @param context     * @return     */    private int getScreenWidth(Context context) {        WindowManager wm = (WindowManager) context                .getSystemService(Context.WINDOW_SERVICE);        DisplayMetrics outMetrics = new DisplayMetrics();        wm.getDefaultDisplay().getMetrics(outMetrics);        return outMetrics.widthPixels;    }    /**     * Dip into pixels     */    private int dip2px(Context context, float dpValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (dpValue * scale + 0.5f);    }}

最后的效果就是

// 1. 获取手指滑动的速率,当于一定值就认为是快速滑动 , GestureDetector(系统提供好的类)// 2. 处理事件拦截 + ViewGroup 事件分发的源码实践//    当菜单打开的时候,手指触摸右边内容部分需要关闭菜单,还需要拦截事件(打开情况下点击内容页不会响应点击事件)

转载地址:https://blog.csdn.net/yaoyaoyao_123/article/details/99095765 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:方法分派——了解虚拟机是怎么执行重载和重写的方法
下一篇:Android开发ScrollView嵌套ListView只显示一行出现的原因以及解决办法

发表评论

最新留言

留言是一种美德,欢迎回访!
[***.207.175.100]2024年04月16日 19时47分53秒