DraggerViewHalper-任意摆放-底部拖动布局

简介: 先看效果图 源码Paste_Image.png001.gif002.gif先说一下为什么要写这两个控件DraggingPanelLayout15年的时候,我接到个需求, 做一个仿猿题库的布局,那时候在github上搜了很多SlidingUpLayout相关的布局,基本上是满足了需求,后来因为iOS的搭档没找到相关的控件,我们放弃了。

先看效果图 源码

Paste_Image.png
001.gif
002.gif

先说一下为什么要写这两个控件

  • DraggingPanelLayout

15年的时候,我接到个需求, 做一个仿猿题库的布局,那时候在github上搜了很多SlidingUpLayout相关的布局,基本上是满足了需求,后来因为iOS的搭档没找到相关的控件,我们放弃了。

  • FreeLayout

16年的时候,我接到一个需求,做一个任意摆放的View,那时候是用原生的onTouchEvent去实现的,现在学会了DraggerViewHelper,就顺便再写一次。

FreeLayout

<pre>
package org.alex.freelayout;

import android.content.Context;
import android.support.annotation.FloatRange;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;

/**
* 作者:Alex
* 时间:2016/11/11 16:16
* 简述:
* 子控件加上 android:tag="false" 表示不可以拖动
* 子控件加上 android:tag="true" 表示可任意拖动
*/
public class FreeLayout extends RelativeLayout {
private ViewDragHelper viewDragHelper;
private int width;
private int height;

public FreeLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView();
}

private void initView() {
    ViewDragHelperCallback dragHelperCallback = new ViewDragHelperCallback();
    viewDragHelper = ViewDragHelper.create(this, 1.0F, dragHelperCallback);
    /\*跟踪左边界拖动\*/
    viewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    return viewDragHelper.shouldInterceptTouchEvent(ev);
}

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

private final class ViewDragHelperCallback extends ViewDragHelper.Callback {

    /\*\*
     \* 手指释放的时候回调
     \*/
    public void onViewReleased(final View releasedChild, float xvel, float yvel) {
        release4BackBorder(releasedChild);
    }

    /\*\*
     \* 当captureview的位置发生改变时回调
     \*/
    @Override
    public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
        super.onViewPositionChanged(changedView, left, top, dx, dy);

    }


    /\*\*
     \* true的时候会锁住当前的边界,false则unLock。
     \*/
    public boolean onEdgeLock(int edgeFlags) {
        return false;
    }

    public int getViewHorizontalDragRange(View child) {
        return getMeasuredWidth() - child.getMeasuredWidth();
    }

    /\*\*
     \* 尝试捕获子view, 返回true表示允许。
     \*
     \* @param child     尝试捕获的view
     \* @param pointerId 指示器id?
     \*/
    public boolean tryCaptureView(View child, int pointerId) {
        return "true".equals(child.getTag());
    }

    /\*\*
     \* 处理水平方向上的拖动,返回值就是最终确定的移动的位置。
     \* 实际上就是判断如果这个坐标在layout之内 那我们就返回这个坐标值。
     \* 除此之外就是如果你的layout设置了padding的话,
     \* 也可以让子view的活动范围在padding之内的.
     \*
     \* @param child 被拖动到view
     \* @param left  移动到达的x轴的距离
     \* @param dx    建议的移动的x距离
     \*/
    public int clampViewPositionHorizontal(View child, int left, int dx) {
        return left;
    }


    @Override
    public int clampViewPositionVertical(View child, int top, int dy) {
        return top;
    }
}

/\*\*
 \* 回到 边沿处
 \*/
private void release4BackBorder(View releasedChild) {
    int left = releasedChild.getLeft();
    int right = releasedChild.getRight();
    int top = releasedChild.getTop();
    int bottom = releasedChild.getBottom();
    if ((top >= 0) && (left >= 0) && (right <= width) && (bottom <= height)) {
        return;
    }
    int finalLeft = width - releasedChild.getWidth();
    int finalTop = height - releasedChild.getHeight();
    /\*
    \* 实际情况证明,这里必须是区分 8 种情况,不能是4种情况,
    \* 否则在 飞速滑动的时候,子控件飞出左上角、右上角、左下角、右下角,
    \* 不会被拉回来的
    \* \*/
    if ((top >= 0) && (left < 0) && (right <= width) && (bottom <= height)) {
        /\*左\*/
        viewDragHelper.smoothSlideViewTo(releasedChild, 0, top);
    } else if ((top < 0) && (left > 0) && (right <= width) && (bottom <= height)) {
        /\*上\*/
        viewDragHelper.smoothSlideViewTo(releasedChild, left, 0);
    } else if ((top >= 0) && (left >= 0) && (right > width) && (bottom <= height)) {
        /\*右\*/
        viewDragHelper.smoothSlideViewTo(releasedChild, finalLeft, top);
    } else if ((top >= 0) && (left >= 0) && (right <= width) && (bottom > height)) {
        /\*下\*/
        viewDragHelper.smoothSlideViewTo(releasedChild, left, finalTop);
    } else if ((top < 0) && (left < 0) && (right <= width) && (bottom <= height)) {
        /\*左上\*/
        viewDragHelper.smoothSlideViewTo(releasedChild, 0, 0);
    } else if ((top < 0) && (left >= 0) && (right > width) && (bottom <= height)) {
        /\*右上\*/
        viewDragHelper.smoothSlideViewTo(releasedChild, finalLeft, 0);
    } else if ((top >= 0) && (left < 0) && (right <= width) && (bottom > height)) {
        /\*左下\*/
        viewDragHelper.smoothSlideViewTo(releasedChild, 0, finalTop);
    } else if ((top >= 0) && (left >= 0) && (right > width) && (bottom > height)) {
        /\*右下\*/
        viewDragHelper.smoothSlideViewTo(releasedChild, finalLeft, finalTop);
    }
    ViewCompat.postInvalidateOnAnimation(FreeLayout.this);
}

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

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    width = w;
    height = h;
}

/\*\*
 \* 数据转换: dp---->px
 \*/
public float dp2px(@FloatRange(from = 0) float dp) {
    return dp \* getResources().getDisplayMetrics().density;
}

/\*\*
 \* sp转px
 \*/
public int sp2px(@FloatRange(from = 0) float sp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
}

}

</pre>

DraggingPanelLayout

<pre>
package org.alex.draggingpanellayout;

import android.content.Context;
import android.support.annotation.FloatRange;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;

import org.alex.library.R;

/**
* 作者:Alex
* 时间:2016年11月12日
* 简述:
*/

public class DraggingPanelLayout extends RelativeLayout {
private View draggerView;
private int draggerViewHeight;
private ViewDragHelper viewDragHelper;
/* 距离顶部最小的距离*/
private static int minMarginTop;

public DraggingPanelLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView(context);
}

private void initView(Context context) {
    minMarginTop = (int) dp2px(32);
    ViewDragHelperCallback dragHelperCallback = new ViewDragHelperCallback();
    viewDragHelper = ViewDragHelper.create(this, 1.0F, dragHelperCallback);
    /\*跟踪左边界拖动\*/
    viewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    return viewDragHelper.shouldInterceptTouchEvent(ev);
}

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

private final class ViewDragHelperCallback extends ViewDragHelper.Callback {

    /\*\*
     \* 手指释放的时候回调
     \*/
    public void onViewReleased(final View releasedChild, float xvel, float yvel) {

    }

    /\*\*
     \* 当captureview的位置发生改变时回调
     \*/
    @Override
    public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
        super.onViewPositionChanged(changedView, left, top, dx, dy);

        /\*往上拖动 dy 为负\*/
        RelativeLayout.LayoutParams draggerViewLayoutParams = (RelativeLayout.LayoutParams) draggerView.getLayoutParams();
        draggerViewLayoutParams.topMargin = draggerViewLayoutParams.topMargin + dy;
        draggerView.setLayoutParams(draggerViewLayoutParams);

    }


    /\*\*
     \* true的时候会锁住当前的边界,false则unLock。
     \*/
    public boolean onEdgeLock(int edgeFlags) {
        return false;
    }

    public int getViewHorizontalDragRange(View child) {
        return getMeasuredWidth() - child.getMeasuredWidth();
    }

    /\*\*
     \* 尝试捕获子view, 返回true表示允许。
     \*
     \* @param child     尝试捕获的view
     \* @param pointerId 指示器id?
     \*/
    public boolean tryCaptureView(View child, int pointerId) {
        return child == draggerView;
    }

    @Override
    public int getViewVerticalDragRange(View child) {
        return getMeasuredHeight() - child.getMeasuredHeight();
    }

    /\*\*
     \* 处理水平方向上的拖动,返回值就是最终确定的移动的位置。
     \* 实际上就是判断如果这个坐标在layout之内 那我们就返回这个坐标值。
     \* 除此之外就是如果你的layout设置了padding的话,
     \* 也可以让子view的活动范围在padding之内的.
     \*
     \* @param child 被拖动到view
     \* @param left  移动到达的x轴的距离
     \* @param dx    建议的移动的x距离
     \*/
    public int clampViewPositionHorizontal(View child, int left, int dx) {
        return left;
    }


    @Override
    public int clampViewPositionVertical(View child, int top, int dy) {
        if (top > getHeight() - draggerViewHeight) {
            /\*底部边界\*/
            top = getHeight() - draggerViewHeight;
        } else if (top < minMarginTop) {
            /\*顶部边界\*/
            top = minMarginTop;
        }
        return top;
    }
}

@Override
protected void onFinishInflate() {
    super.onFinishInflate();
    draggerView = findViewById(R.id.dpl_dragger);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    draggerViewHeight = draggerView.getMeasuredHeight();
}

/\*\*
 \* 数据转换: dp---->px
 \*/
public float dp2px(@FloatRange(from = 0) float dp) {
    return dp \* getResources().getDisplayMetrics().density;
}

}

</pre>

目录
相关文章
|
15天前
Echarts图表设置x轴y轴均随滚轮滚动缩+放 区域缩放
Echarts图表设置x轴y轴均随滚轮滚动缩+放 区域缩放
|
8月前
|
前端开发
前端——背景图片显示以及悬浮状态下变色的情况
前端——背景图片显示以及悬浮状态下变色的情况
|
4月前
如何在屏幕的任意位置拖拽控制图片的移动?
如何在屏幕的任意位置拖拽控制图片的移动?
40 1
|
4月前
【sgDragMove】自定义组件:自定义拖拽组件,仅支持拖拽、设置吸附屏幕边界距离。
【sgDragMove】自定义组件:自定义拖拽组件,仅支持拖拽、设置吸附屏幕边界距离。
|
4月前
指定文字浮动区域,动起来的效果棒极了
指定文字浮动区域,动起来的效果棒极了
36 0
|
6月前
|
UED 容器
如何实现侧边两栏宽度固定,中间栏宽度自适应的布局?——双飞翼布局、圣杯(Holy Grails)布局
如何实现侧边两栏宽度固定,中间栏宽度自适应的布局?——双飞翼布局、圣杯(Holy Grails)布局
45 0
|
JavaScript 前端开发
菜单栏底部线条切换效果(标题滚动居中显示)
菜单栏底部线条切换效果(标题滚动居中显示)
108 0
菜单栏底部线条切换效果(标题滚动居中显示)
|
iOS开发
iOS开发 - 渐变导航条升级版(判断滚动的方向和改变方向时的位置)
iOS开发 - 渐变导航条升级版(判断滚动的方向和改变方向时的位置)
120 0
iOS开发 - 渐变导航条升级版(判断滚动的方向和改变方向时的位置)
|
前端开发 JavaScript 计算机视觉
css动画:文字向上移动并逐渐消失 点击按钮显示+1上移淡出
css动画:文字向上移动并逐渐消失 点击按钮显示+1上移淡出
936 0
css动画:文字向上移动并逐渐消失 点击按钮显示+1上移淡出
布局之悬浮显示更多文本并增加箭头指示效果
布局之悬浮显示更多文本并增加箭头指示效果
93 0
布局之悬浮显示更多文本并增加箭头指示效果