最近项目需要一个可移动的viewgroup控件,这方面还是比较弱的(因为之前都没怎么做这个,而且网上的自定义控件文章很多,看了好久还是没有自信。我想还是没有彻底理解把,唉)在此感谢zhy大神,他的文章总是帮助好多。感谢大神,感谢开源精神。
SlidingPaneLayout和DrawerLayout这两个google的控件不知道大家有没有用过,据说都用到了viewdraghelper。因为我没有看过源码,只是听说。但是这已经说明了它的强大,不是吗?此处有‘赢笑’图片。为什么叫helper类呢,因为android手势处理还是比较复杂的,所以这个帮助类是帮助我们的快速实现的。
- 创建实例(通过静态工厂方法创建的)
- 触摸相关的方法的调用
ViewDragHelper.Callback实例的编写(大部分方法都在callback里面)
开始自定义控件首先当然是继承RelativeLayout(布局viewgroup)
- ViewDragHelper的初始化
- ViewDragHelper.Callback
- 触摸相关的方法的调用
代码
初始化viewdraghelper
/**
* 1.0f传入的值越大,越来越难拉
*/
mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
重写callback的方法
此方法决定哪个view可以滑动
/**
* tryCaptureView如果返回ture则表示可以捕获该view,你可以根据传入的第一个view参数决定哪些可以捕获
* @param child
* @param pointerId
* @return
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mAutoBackView;
// return true;
}
设定左右上下滑动的范围
/**
* 设定左右上下滑动的范围
* @param child
* @param left
* @param dx
* @return
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return (getWidth() - mAutoBackView.getWidth())/2;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
int topBound = getPaddingTop();
int bottomBound = getHeight() - mBottomView.getHeight() - topBound - mAutoBackView.getHeight();
int newTop = Math.min(Math.max(top,topBound),bottomBound);
return newTop;
}
手指释放时调用的方法
/**
* 手指释放的时候回调
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if (releasedChild == mAutoBackView) {
// mAutoBackView.getScrollY()
// Log.d("yzj------",""+mAutoBackView.getScrollY());
// Log.d("yzj------","mAutoBackOriginPos.x:"+mAutoBackOriginPos.x+"mAutoBackOriginPos.y:"+mAutoBackOriginPos.y);
mDragger.settleCapturedViewAt(mAutoBackOriginPos.x, mAutoBackOriginPos.y);
invalidate();
flag = false;
}
super.onViewReleased(releasedChild, xvel, yvel);
}
当在边缘滑动的时候调用
/**
* 在边缘滑动的时候调用
* @param edgeFlags
* @param pointerId
*/
@Override
public void onEdgeTouched(int edgeFlags, int pointerId) {
super.onEdgeTouched(edgeFlags, pointerId);
}
view位置改变时调用
/**
* view位置改变时调用
* @param changedView
* @param left
* @param top
* @param dx
* @param dy
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if(top == getHeight() - mBottomView.getHeight() - getPaddingTop() - mAutoBackView.getHeight() && (flag == false)){
Log.d("yzj------","move to the most");
flag = true;
}
}
接下来是触摸相关的方法的调用
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mDragger.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragger.processTouchEvent(event);
return true;
}
layout完成时调用
/**
* 得到初始化位置
*
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mAutoBackOriginPos.x = mAutoBackView.getLeft();
mAutoBackOriginPos.y = mAutoBackView.getTop();
}
完成inflate时的调用
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mAutoBackView = getChildAt(1);
mBottomView = getChildAt(2);
}
通过以上几个方法的调用我相信能帮你实现很多的东西。最后放上全部的代码
public class VDRLayout extends RelativeLayout {
//helper类
private ViewDragHelper mDragger;
//回弹view
private View mAutoBackView;
//记录初始位置
private Point mAutoBackOriginPos = new Point();
private View mBottomView;
private boolean flag;
public VDRLayout(Context context, AttributeSet attrs) {
this(context, null, 0);
}
public VDRLayout(Context context) {
this(context, null);
}
public VDRLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/**
* 1.0f传入的值越大,越来越难拉
*/
mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
/**
* tryCaptureView如果返回ture则表示可以捕获该view,你可以根据传入的第一个view参数决定哪些可以捕获
* @param child
* @param pointerId
* @return
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mAutoBackView;
// return true;
}
/**
* 设定左右上下滑动的范围
* @param child
* @param left
* @param dx
* @return
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return (getWidth() - mAutoBackView.getWidth())/2;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
int topBound = getPaddingTop();
int bottomBound = getHeight() - mBottomView.getHeight() - topBound - mAutoBackView.getHeight();
int newTop = Math.min(Math.max(top,topBound),bottomBound);
return newTop;
}
/**
* 手指释放的时候回调
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if (releasedChild == mAutoBackView) {
// mAutoBackView.getScrollY()
// Log.d("yzj------",""+mAutoBackView.getScrollY());
// Log.d("yzj------","mAutoBackOriginPos.x:"+mAutoBackOriginPos.x+"mAutoBackOriginPos.y:"+mAutoBackOriginPos.y);
mDragger.settleCapturedViewAt(mAutoBackOriginPos.x, mAutoBackOriginPos.y);
invalidate();
flag = false;
}
super.onViewReleased(releasedChild, xvel, yvel);
}
/**
* 在边缘滑动的时候调用
* @param edgeFlags
* @param pointerId
*/
@Override
public void onEdgeTouched(int edgeFlags, int pointerId) {
super.onEdgeTouched(edgeFlags, pointerId);
}
/**
* view位置改变时调用
* @param changedView
* @param left
* @param top
* @param dx
* @param dy
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if(top == getHeight() - mBottomView.getHeight() - getPaddingTop() - mAutoBackView.getHeight() && (flag == false)){
Log.d("yzj------","move to the most");
flag = true;
}
}
});
//允许哪个边缘可以调用
mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_BOTTOM);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
//决定我们是否应该拦截当前的事件
return mDragger.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//处理事件
mDragger.processTouchEvent(event);
return true;
}
@Override
public void computeScroll() {
if (mDragger.continueSettling(true)) {
invalidate();
}
}
/**
* 得到初始化位置
*
* @param changed
* @param l
* @param t
* @param r
* @param b
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mAutoBackOriginPos.x = mAutoBackView.getLeft();
mAutoBackOriginPos.y = mAutoBackView.getTop();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mAutoBackView = getChildAt(1);
mBottomView = getChildAt(2);
}
}