使用viewdraghelper打造属于自己的draglayout(抽屉开关)

zhchoutai zhchoutai     2022-11-04     182

关键词:

使用ViewDragHelper打造属于自己的DragLayout(抽屉开关 )


DrawLayout这个自己定义的空间非经常见。qq,网易新闻。知乎等等,都有这样的效果,那这样的效果是如何实现的呢?本篇博客将带你来如何实现它。

转载请注明原博客地址: http://blog.csdn.net/gdutxiaoxu/article/details/51935896

废话不多说,先来看一下 效果

技术分享图片

首先我们先来看一下我们要如何使用它

事实上只须要两个 步骤,使用起来 非常方便

1.在XML文件

DragLayout至少要有两个孩子。且都是 ViewGroup或者ViewGroup的实现类

<com.xujun.drawerLayout.drag.DragLayout
    android:id="@+id/dl"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg"
    app:range="480"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="50dp"
        android:paddingLeft="10dp"
        android:paddingRight="50dp"
        android:paddingTop="50dp">

        <ImageView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:src="@drawable/head"/>

        <ListView
            android:id="@+id/lv_left"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </ListView>
    </LinearLayout>

    <com.xujun.drawerLayout.drag.MyLinearLayout
        android:id="@+id/mll"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ffffff"
        android:orientation="vertical">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#18B6EF"
            android:gravity="center_vertical">

            <ImageView
                android:id="@+id/iv_header"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_marginLeft="15dp"
                android:src="@drawable/head"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:text="Header"/>
        </RelativeLayout>

        <ListView
            android:id="@+id/lv_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </ListView>
    </com.xujun.drawerLayout.drag.MyLinearLayout>

</com.xujun.drawerLayout.drag.DragLayout>

在代码中若想为其设置监听器,

分别能够监听打开的 时候,关闭的时候,拖动的时候,能够在里面做对应的处理,同一时候我还增加了 自己定义属性能够通过 app:range=”480”或者setRange()方法,就可以设置打开抽屉的范围。

 mDragLayout.setDragStatusListener(new OnDragStatusChangeListener() 

            @Override
            public void onOpen() 
                Utils.showToast(MainActivity.this, "onOpen");
                // 左面板ListView随机设置一个条目
                Random random = new Random();
                Log.i(TAG, "onOpen:=" +mDragLayout.getRange());
                int nextInt = random.nextInt(50);
                mLeftList.smoothScrollToPosition(nextInt);

            

            @Override
            public void onDraging(float percent) 
                Log.d(TAG, "onDraging: " + percent);// 0 -> 1
                // 更新图标的透明度
                // 1.0 -> 0.0
                ViewHelper.setAlpha(mHeaderImage, 1 - percent);
            

            @Override
            public void onClose() 
                Utils.showToast(MainActivity.this, "onClose");
                // 让图标晃动
                ObjectAnimator mAnim = ObjectAnimator.ofFloat(mHeaderImage, "translationX", 15.0f);
                mAnim.setInterpolator(new CycleInterpolator(4));
                mAnim.setDuration(500);
                mAnim.start();
            
        );

实现方式

关于ViewDragHelper的一些 讨论

DrawLayout在网上的 实现方式非常多,千奇百怪。有一些是直接监听 onTouchEvent事件,处理Activon_Move,Action_Down,Action_up等动作。这样实现的话略微有点复杂。本篇博客是使用ViewDragHelper来 处理触摸事件和拖拽事件的的,ViewDragHelper是2013Google IO大会推出的,目的是为了给开发人员提供一个处理触摸事件,节省开发人员的时间。

关于Google官方 关于ViewDragHelper的解释,简单来说就是处理ViewGroup的 触摸事件和拖拽事件

ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number of useful operations and state tracking for allowing a user to drag and reposition views within their parent ViewGroup.

实现思路

  • 1) 我是通过继承FrameLayout来实现的,相比較于继承ViewGroup来实现。这样有一个优点就是省去了自己重写 onMeasure (),onLayout ()方法

  • 2)在构造方法里面初始化mDragHelper,mSensitivity代表打开抽屉的 难易程度,是Float类型。至于mCallback是什么,以下会具体讲,这里先不着急。

public DragLayout(Context context, AttributeSet attrs, int defStyle) 
    super(context, attrs, defStyle);
    initAttars(context, attrs);
    // a.初始化 (通过静态方法)
    mDragHelper = ViewDragHelper.create(this, mSensitivity, mCallback);

  • 3)重写 onInterceptTouchEvent和onTouchevent 方法 ,将事件交给
// b.传递触摸事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) 
    // 传递给mDragHelper
    return mDragHelper.shouldInterceptTouchEvent(ev);


/***
 * 将事件交给mDragHelper处理
 *
 * @param event
 * @return
 */
@Override
public boolean onTouchEvent(MotionEvent event) 
    try 
        mDragHelper.processTouchEvent(event);
     catch (Exception e) 
        e.printStackTrace();
    
    // 返回true, 持续接受事件
    return true;
  • 4)重写onFinishInflate方法。在里面拿到 我们的側滑菜单mLeftContent和主菜单mMainContent
@Override
protected void onFinishInflate() 
    super.onFinishInflate();

    // 容错性检查 (至少有俩子View, 子View必须是ViewGroup的子类)
    if (getChildCount() < 2) 
        throw new IllegalStateException("布局至少有俩孩子. Your ViewGroup must have 2 children at " +
                "least.");
    
    if (!(getChildAt(0) instanceof ViewGroup && getChildAt(1) instanceof ViewGroup)) 
        throw new IllegalArgumentException("子View必须是ViewGroup的子类. Your children must be an " +
                "instance of ViewGroup");
    

    mLeftContent = (ViewGroup) getChildAt(0);
    mMainContent = (ViewGroup) getChildAt(1);



以下我们一起来看一下这个mCallBack是什么东西

看之前我们须要了解Status和OnDragStatusChangeListener这两个东西。

  • Status代表DrawLayout 当前的状态,是否是打开,关闭还是拖拽。
  • OnDragStatusChangeListener是个监听器。在DrawLayout状态改变的时候会回调相关的方法。方便与外界进行通讯。

  • 我们能够通过 setDragStatusListener(OnDragStatusChangeListener mListener);这种方法设置监听
/**
 * 状态枚举
 */
public static enum Status 
    Close, Open, Draging;


/**
 * 抽屉开关的监听器
 */
public interface OnDragStatusChangeListener 
    void onClose();

    void onOpen();

    void onDraging(float percent);


接下来我们来看ViewDragHelper.Callback几个基本的方法


tryCaptureView(View child, int pointerId)
Called when the user’s input indicates that they want to capture the given child view with the pointer indicated by pointerId.
onViewCaptured(View capturedChild, int activePointerId)
Called when a child view is captured for dragging or settling.

getViewHorizontalDragRange(View child)
Return the magnitude of a draggable child view’s horizontal range of motion in pixels.

clampViewPositionHorizontal(View child, int left, int dx)
Restrict the motion of the dragged child view along the horizontal axis.

onViewPositionChanged(View changedView, int left, int top, int dx, int dy)
Called when the captured view’s position changes as the result of a drag or settle.

onViewReleased(View releasedChild, float xvel, float yvel)
Called when the child view is no longer being actively dragged.

谷歌官方的连接;https://developer.android.com/reference/android/support/v4/widget/ViewDragHelper.Callback.html


以下的代码有关于这几个方法的中文解释,这里就不具体解说了

ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() 
    // d. 重写事件

    // 1. 依据返回结果决定当前child能否够拖拽
    // child 当前被拖拽的View
    // pointerId 区分多点触摸的id
    @Override
    public boolean tryCaptureView(View child, int pointerId) 
        Log.d(TAG, "tryCaptureView: " + child);
        return mToogle;
    

    @Override
    public void onViewCaptured(View capturedChild, int activePointerId) 
        Log.d(TAG, "onViewCaptured: " + capturedChild);
        // 当capturedChild被捕获时,调用.
        super.onViewCaptured(capturedChild, activePointerId);
    

    @Override
    public int getViewHorizontalDragRange(View child) 
        // 返回拖拽的范围, 不正确拖拽进行真正的限制. 只决定了动画运行速度
        Log.i(TAG, "getViewHorizontalDragRange:mRange=" +mRange);
        return mRange;
    

    // 2. 依据建议值 修正将要移动到的(横向)位置   (重要)
    // 此时没有发生真正的移动
    public int clampViewPositionHorizontal(View child, int left, int dx) 
        // child: 当前拖拽的View
        // left 新的位置的建议值, dx 位置变化量
        // left = oldLeft + dx;
        Log.d(TAG, "clampViewPositionHorizontal: "
                + "oldLeft: " + child.getLeft() + " dx: " + dx + " left: " + left);

        if (child == mMainContent) 
            left = fixLeft(left);
        
        return left;
    

    // 3. 当View位置改变的时候, 处理要做的事情 (更新状态, 伴随动画, 重绘界面)
    // 此时,View已经发生了位置的改变
    @Override
    public void onViewPositionChanged(View changedView, int left, int top,
                                      int dx, int dy) 
        // changedView 改变位置的View
        // left 新的左边值
        // dx 水平方向变化量
        super.onViewPositionChanged(changedView, left, top, dx, dy);
        Log.d(TAG, "onViewPositionChanged: " + "left: " + left + " dx: " + dx);

        int newLeft = left;
        if (changedView == mLeftContent) 
            // 把当前变化量传递给mMainContent
            newLeft = mMainContent.getLeft() + dx;
        

        // 进行修正
        newLeft = fixLeft(newLeft);

        if (changedView == mLeftContent) 
            // 当左面板移动之后, 再强制放回去.
            mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight);
            mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 + mHeight);
        
        // 更新状态,运行动画
        dispatchDragEvent(newLeft);

        // 为了兼容低版本号, 每次改动值之后, 进行重绘
        invalidate();
    

    // 4. 当View被释放的时候, 处理的事情(运行动画)
    @Override
    public void onViewReleased(View releasedChild, float xvel, float yvel) 
        // View releasedChild 被释放的子View
        // float xvel 水平方向的速度, 向右为+
        // float yvel 竖直方向的速度, 向下为+
        Log.d(TAG, "onViewReleased: " + "xvel: " + xvel + " yvel: " + yvel);
        super.onViewReleased(releasedChild, xvel, yvel);

        // 推断运行 关闭/开启
        // 先考虑全部开启的情况,剩下的就都是关闭的情况
        if (xvel == 0 && mMainContent.getLeft() > mRange / 2.0f) 
            open();
         else if (xvel > 0) 
            open();
         else 
            close();
        

    

    @Override
    public void onViewDragStateChanged(int state) 
        // TODO Auto-generated method stub
        super.onViewDragStateChanged(state);
    

;

事实上主要思路就是

  • 1)在方法public boolean tryCaptureView(View child, int pointerId)处理那些child能够被捕捉。这里我们返回true表示全部的都能够被捕捉
  • 2)在public int clampViewPositionHorizontal(View child, int left, int dx)方法中依据child返回将要移动的水平位置的偏移量

  • 3)在 void onViewPositionChanged(View changedView, int left, int top, int dx, int dy)方法中处理要做的事情 包含更新状态, 伴随动画, 重绘界面等

public void onViewPositionChanged(View changedView, int left, int top,  int dx, int dy) 

      // 进行修正
   newLeft = fixLeft(newLeft);

  if (changedView == mLeftContent) 
    // 当左面板移动之后, 再强制放回去.
    mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight);
    mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 +    mHeight);

    if (changedView == mLeftContent) 
    // 把当前变化量传递给mMainContent
    newLeft = mMainContent.getLeft() + dx;
   




    // 更新状态,运行动画
    dispatchDragEvent(newLeft);

    // 为了兼容低版本号, 每次改动值之后, 进行重绘
    invalidate();


protected void dispatchDragEvent(int newLeft) 
    float percent = newLeft * 1.0f / mRange;
    //0.0f -> 1.0f
    Log.d(TAG, "percent: " + percent);

    if (mListener != null) 
        mListener.onDraging(percent);
    

    // 更新状态, 运行回调
    Status preStatus = mStatus;
    mStatus = updateStatus(percent);
    if (mStatus != preStatus) 
        // 状态发生变化
        if (mStatus == Status.Close) 
            // 当前变为关闭状态
            if (mListener != null) 
                mListener.onClose();
            
         else if (mStatus == Status.Open) 
            if (mListener != null) 
                mListener.onOpen();
            
        
    

    //    * 伴随动画:
    animViews(percent);


  • 4)在void onViewReleased(View releasedChild, float xvel, float yvel)的时候处理要做的事情,包含更新状态, 伴随动画, 重绘界面等。这里就不列出代码了,有兴趣的话下载源代码看看

转载请注明原博客地址: http://blog.csdn.net/gdutxiaoxu/article/details/51935896

源代码下载地址: https://github.com/gdutxiaoxu/drawLayout.git

关于很多其它自己定义View的样例,可查看以下我的一些博客。同一时候假设大家认为还能够的。欢迎在github上面 star或者fork,谢谢。

经常使用的自己定义View样例一(FlowLayout)

自己定义View经常使用样例二(点击展开隐藏控件,九宫格图片控件)

经常使用的自己定义View样例三(MultiInterfaceView多界面处理)

经常使用的自己定义控件四(QuickBarView)








打造属于自己的宝可梦终端(代码片段)

打造属于自己的宝可梦终端Features719uniquePokemonSelectPokemonbynameorbyindexnumberAbilitytochangetheDesktopWallpaper&theTerminalbackgroundInternalsearchsystemforfindingPokemonSupportsiTerm2,ConEmu,Terminology 查看详情

使用chatgpt加个人微信公众号打造属于自己的ai助手(代码片段)

使用ChatGPT加个人微信公众号打造属于自己的AI助手1、总体介绍2、实现2.1搭建微信公众号后台2.2用Python实现和ChatGPT的交互(核心)2.2.1启动/关闭浏览器2.2.2开启监听(实现程序和浏览器交互)2.2.3人机验证(重... 查看详情

ViewDragHelper:如何使用它?

】ViewDragHelper:如何使用它?【英文标题】:ViewDragHelper:howtouseit?【发布时间】:2013-07-2007:12:01【问题描述】:在GoogleIO2013中,Google宣布了新版本的支持库,其中包括ViewDragHelper类。我查看了文档,但找不到此类的任何用法示例。... 查看详情

快来一起实现属于自己的自行车吧!

 纯CSS3打造自行车 废话不想多说,我们直接上图和代码最实际。 我们先来看看效果图吧,看了效果图之后你们就会有动力去打造属于你们自己的自行车啦   怎么样,帅不帅呀,快来打造帅帅的自行车吧 ... 查看详情

利用python实现黑客帝国代码雨,打造属于自己的黑客帝国

导语看安全类文章的时候,发现文章前面经常会加个代码雨的特效图,感觉拿来用python实现一下当成一个小案例还是不错的。让我们愉快地开始吧~开发工具**Python版本:**3.6.4相关模块:pygame模块;以及一些pyth... 查看详情

android使用viewdraghelper实现侧滑菜单(代码片段)

...的几种方式写到的四种外,Androidv4包中还提供了一个ViewDragHelper类来帮助我们更加方便地处理滑动事件,ViewDragHelper使得View与View之间的滑动交互更加简单方便。不过在学习ViewDragHelper处理滑动事件前需要掌握View的事件处... 查看详情

谷歌正式发布android12,ui更好看,应用更快,打造独属于自己的定制化属性

焕然一新的Android12今年5月的GoogleI/O大会上,谷歌推出了Android12系统,这是原生安卓系统史上最大的设计变化,从此旧貌换新颜。不只是外观,Android12的功能改进也令人瞩目,对比起挤牙膏的那位——Android11... 查看详情

用centos7纯手工打造属于自己的软路由服务器

...但是作为“懂”Linux以及计算机网络的我来说,如果直接使用现成的软路由系统总感觉低估了自己的能力……而且我家里也已经有了一台Linux服务器,要是能做到物尽其用那就最好了。于是我很早就计划着搭建一台属于自己的软... 查看详情

如何打造属于自己的3d打印机上位机?这篇文章带你了解一下!(代码片段)

击上方“嵌入式应用研究院”,选择“置顶/星标公众号”干货福利,第一时间送达!建议不是本行又感兴趣的小伙伴们先看下面两篇了解一下Marlin:开源Marlin2.x源代码架构学习笔记3D打印机marlin固件框架与GCode命令总结... 查看详情

horizontaldraglayout-模仿qqclient的item滑动删除

...n.net/lmj623565791/article/details/46858663hongyang的文章。之前看过ViewDragHelper类也读过一些demo一直都是半知半解且之前一些自己定义的ViewGroup大都不是按这样的方式来写,这一次抓紧一次自己写一个demo熟悉ViewDragHelper。ViewDragHelper存在于v... 查看详情

使用腾讯位置服务javascriptapigl打造自己的3d地图(代码片段)

本文目录一、介绍二、开始使用(一)注册成为腾讯位置服务开发者(二)创建应用和Key三、地图显示(一)在地图页面引入JS库(二)定义显示地图的容器(三)初始化并显示地图四、效... 查看详情

viewdraghelper

...uchEvent这2个方法是非常麻烦的事情,好在谷歌后来推出了ViewDragHelper这个类。可以极大方便我们自定义viewgroup.先看一个简单效果一个layout里有2个图片其中有一个可以滑动一个不能滑 这个效果其实还蛮简单的(原谅我让臭脚... 查看详情

将自己的博客园,打造成个人知乎

将自己的博客园,打造成个人知乎将自己的博客园,打造成个人知乎将自己的博客园,打造成个人知乎将自己的博客园,打造成个人知乎将自己的博客园,打造成个人知乎将自己的博客园,打造成个人知乎将自己的博客园,打造... 查看详情

打造自己的html5视频播放器

...重新学习了一下html5的video部分,以前只是停留在标签的使用上,这一次决定深入了解相关的API,并运用这些API打造一个简单的视频播放器。所谓“打造自己的”,就是要自己重写video标签的控制栏部分,实现包括播放、暂停、进... 查看详情

打造自己的html5视频播放器

...重新学习了一下html5的video部分,以前只是停留在标签的使用上,这一次决定深入了解相关的API,并运用这些API打造一个简单的视频播放器。所谓“打造自己的”,就是要自己重写video标签的控制栏部分,实现包括播放、暂停、进... 查看详情

java使用jsptagfiles&jspelfunctions打造你自己的页面模板

...0后,你不再需要大刀阔斧地定义一堆TagSupport或BodyTagSupport,使用JSPTagFiles技术可以实现功能强大的页面模板技术.在这里抛砖引玉,结合项目开发,简单介绍TagFiles技术的应用.至于详细教程与资料,请大家参考JavaEETutorial,上面有详细的E... 查看详情

(转)viewdraghelper实现qq5.0侧滑并处理与viewpager的滑动冲突

最近在做项目,涉及到类似QQ的页面的滑动。但是却遇到了侧滑和ViewPager冲突的问题,头疼了很长时间,最后在网上发现了这篇博客,转载过来供自己学习参考(写这篇博客的原创作者,因为我发现这篇博客的地方也是别人转载的... 查看详情

从零开始-打造自己的虚拟实验室-2(代码片段)

...拟存储:虚拟磁盘文件:VHDx文件动态扩展:常用,实验使用差分磁盘:快速部署多台虚拟机时,创建一个系统的base盘,使用该base盘快速创建多个虚拟机系统的base盘必须为“封装状态”,无系统的SID状态Base系统创建最佳实践手... 查看详情