安卓开发之ViewDragHelper的使用及自定义可下拉展示内容的ViewGroup

本文介绍了如何在安卓开发中利用ViewDragHelper实现自定义ViewGroup DragDownLayout,支持下拉展示内容。详细讲解了ViewDragHelper的创建、Callback的使用,以及在XML布局和实际应用中的操作。通过覆盖Callback的方法,实现子组件的拖动限制和滑动效果。并提供了DragDownLayout的示例代码和GitHub链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、ViewDragHelper

一个自定义ViewGroup的工具类,它提供了许多有用的方法和状态允许用户去拖拽和绘制子View在自定义ViewGroup中的轨迹和位置。

  1. ViewDragHelper可以使用静态方法创建一个实例:

    ViewDragHelper.create(ViewGroup forParent,int sensitiveity,ViewDragHelper.Callback cb)
    
  2. 在自定义ViewGroup中,ViewDragHelper可以帮助我们来分析手势和处理拖动:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
            try {
            //处理触摸事件
            mDragHelper.processTouchEvent(event);
            } catch (Exception e) {
            e.printStackTrace();
            }
            //返回true,
            return true;
    }
    
  3. 使用ViewDragHelper来动态移动自定义ViewGroup中的控件:

    public boolean smoothSlideViewTo(View child, int finalLeft, int finalTop)
    
    // Animate the view child to the given (left, top) position.
    // 返回true 代表还没有移动到指定的位置,需要刷新界面,继续移动
    // 返回false 就停止工作
    
  4. 使用ViewDragHelper滑动边缘监听:

    // 监听左边缘滑动
    setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
    

二、 ViewDragHelper.Callback

ViewDragHelper.Callback集成了许多可覆写的方法,所有移动的控制在ViewDragHelper.Callback里面来实现。

  1. 是否可以捕捉ViewGroup中的子组件:

    public boolean tryCaptureView(View child, int pointerId) {
    
    //返回true,就代表着可对该子组件处理滑动事件。否则就不会处理。 
    
    return true;
    //只对特定的组件捕捉 return speChild == child;
    
    }
    

    // 使用ViewDragHelper的captureChildView捕捉子组件,该方法可以绕过tryCaptureView方法

  2. clampViewPositionHorizontal[Vertical]:

    处理子组件在水平或者竖直方向的滑动限制,在这个方法内部做子组件的边界处理,就是确保子组件不会滑过界。

    例如在竖直方向进行滑动时,一般先获取控件可滑动到的顶端Y值底端Y值,再进行一个取值

    @Override
    public int clampViewPositionVertical(View child, int top, int dy) {
    
    //手指触摸移动时实时回调, top表示要到的y位置
    
        int topBound = ...;
        int bottomBound = ...;
        return Math.min(Math.max(topBound, top), bottomBound);
    
    }
    
  3. onViewPositionChanged
    当前拖动的子组件位置变化时调用的方法。一般在该方法里调整其他子组件的位置。

    @Override
    public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
    
        //changedView为当前位置发生改变的View,left,top分别为该View的left和top坐标
    
    } 
    
  4. onViewReleased

    当手指释放的时候会调用的方法。在这个方法里实现松开时的滑动效果。

    @Override
    public void onViewReleased(View releasedChild, float xvel, float yvel) {
    
        //released 为释放的View,xvel,yvel分别为该手指离开时滑动和竖直滑动的速度
    
    } 
    
  5. getViewVertical[Horizontal]DragRangeHorizontal

     @Override
        public int getViewVerticalDragRange(View child) {
           //返回当前捕捉的child子组件的竖直滑动范围
            return ...;
        }
    
  6. onEdgeDragStarted(int edgeFlags, int pointerId)

    // 边缘滑动时回调
    @Override
    public void onEdgeDragStarted(int edgeFlags, int pointerId) {
       // edgeFlags为边缘滑动的方法标志
    
    }
    

    ……

三、ViewDragHelper的使用

自定义可下拉展示内容的的ViewGroup

效果图:

这里写图片描述

一、自定义ViewGroup:DragDownLayout

可支持padding和子组件宽高的wrap_content

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

/**
 * Created by cxm on 2016/8/22.
 */
public class DragDownLayout extends ViewGroup {

    private ViewDragHelper dragHelper;
    private View mDragbar, mContentView;
    private int dragRange;
    private OnOpenListener mOnOpenListener;
    private OnCloseListener mOnCloseListener;

    private boolean isOpen;//是否打开着

    public DragDownLayout(Context context) {
        super(context);
        init();
    }

    public DragDownLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public DragDownLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public DragDownLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        dragHelper = ViewDragHelper.create(this, mCallback);
    }


    @Override
    protected void onLayout(boolean b, int l, int t, int r, int bo) {

        mDragbar.layout(getPaddingLeft(), getPaddingTop(), getWidth()-getPaddingRight(), mDragbar.getMeasuredHeight()+getPaddingTop());

        mContentView.layout(getPaddingLeft(), getPaddingTop()-mContentView.getMeasuredHeight(), getWidth()-getPaddingRight(), getPaddingTop());

    }


    @Override
    public boolean onInterceptHoverEvent(MotionEvent event) {
        final int action = MotionEventCompat.getActionMasked(event);
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            dragHelper.cancel();
            return false;
        }
        return dragHelper.shouldInterceptTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        dragHelper.processTouchEvent(event);
        return true;
    }

    private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == mDragbar;
        }



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

            // top为mDragbar的top值
            mContentView.layout(getPaddingLeft(), top-mContentView.getHeight(), getWidth()-getPaddingRight(), top );

        }


        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            int topBound = getPaddingTop();//顶端边界
            int bottomBound = getHeight() - mDragbar.getHeight()-getPaddingBottom();//底端边界
            return Math.min(Math.max(topBound, top), bottomBound);
        }


    @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            // 禁止水平滑动
            return getPaddingLeft();
        }


        @Override
        public int getViewVerticalDragRange(View child) {
            dragRange = mContentView.getMeasuredHeight();
            return dragRange;
        }




        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {

            if (mContentView.getBottom() - getPaddingTop() > mContentView.getHeight() / 2) {
                smoothToBottom();
            } else if (mContentView.getBottom() - getPaddingTop() <= mContentView.getHeight() / 2) {
                smoothToTop();
            }
            invalidate();
        }
    };


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        mDragbar = getChildAt(0);
        mContentView = getChildAt(1);


        measureChild(mDragbar,widthMeasureSpec,heightMeasureSpec);

        int dragBarHeight = mDragbar.getMeasuredHeight();


        measureChild(mContentView,widthMeasureSpec,heightMeasureSpec);

        int contentHeight = mContentView.getMeasuredHeight();


        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), dragBarHeight+contentHeight+getPaddingBottom()+getPaddingTop());


    }




    private void smoothToTop() {
        if (dragHelper.smoothSlideViewTo(mDragbar, getPaddingLeft(), getPaddingTop())) {
            ViewCompat.postInvalidateOnAnimation(this);
            isOpen = false;
            if(mOnCloseListener!=null) mOnCloseListener.close();
        }
    }

    private void smoothToBottom() {
        if (dragHelper.smoothSlideViewTo(mDragbar, getPaddingLeft(), getHeight()-getPaddingBottom()-mDragbar.getHeight())) {
            ViewCompat.postInvalidateOnAnimation(this);
            isOpen = true;
            if(mOnOpenListener!=null) mOnOpenListener.open();
        }
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (dragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    public boolean isOpen(){
        return isOpen;
    }

    public void openContent(){
        if(!isOpen) smoothToBottom();
    }

    public void closeContent(){
        if(isOpen) smoothToTop();
    }

    public interface OnOpenListener{
        void open();
    }

    public interface OnCloseListener{
        void close();
    }



    public void setOnOpenListener(OnOpenListener mOnOpenListener) {
        this.mOnOpenListener = mOnOpenListener;
    }

    public void setOnCloseListener(OnCloseListener mOnCloseListener) {
        this.mOnCloseListener = mOnCloseListener;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() != 2) {
            throw new IllegalStateException("Just contain two Views/ViewGroups ");
        }
    }
}

二、在xml布局文件中使用DragDownLayout

支持DragDownLayout的padding,没有支持子View的margin。

拖拽控件和内容控件的高度可wrap_content / 具体值,宽度需为match_parent。

<com.cxmscb.cxm.dtest.DragDownLayout
    android:id="@+id/myDragDownLayout"
    android:background="#e0ffff"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:background="#e000ff"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:text="欢迎"
        android:textColor="#ffffff"
        android:gravity="center_vertical"
        android:paddingLeft="15dp"
        android:textSize="19sp" />

    <TextView
        android:id="@+id/content"
        android:background="#e000ff"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:text="Content"
        android:gravity="center"
        android:textSize="20sp"/>

</com.cxmscb.cxm.dtest.DragDownLayout>

三、对DragDownLayout设置监听

mDragDownLayout = (DragDownLayout) findViewById(R.id.myDragDownLayout);

mDragDownLayout.setOnOpenListener(new DragDownLayout.OnOpenListener() {
        @Override
        public void open() {
            Toast.makeText(MainActivity.this,"open",Toast.LENGTH_SHORT).show();
        }
    });

Github:DragDownLayout
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值