大家好,本篇博客带来的一个效果是仿二手车之家的竖直拖动蓝的一个效果,具体如下:
为什么会选择写一个这样的效果呢?是因为这里牵涉到了两个知识点,一个是最近正在学习的事件分发,还有一个是ViewDragHelper这个拖拽的工具类。
首先 我们先实现这个一个拖拽的效果:
下面的红色的区域块,可以在竖直方向上拖动,当区域仅限于后面颜色的高度,话不多说,直接上代码:
/**
* Created by DELL on 2017/9/12.
* Description :
*/
public class VerticalDragView extends FrameLayout {
private ViewDragHelper mDragHelper;
private View mFirstView;
private View mDragView;
private int mFirstViewHeight;
public VerticalDragView(@NonNull Context context) {
this(context,null);
}
public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDragHelper = ViewDragHelper.create(this,mCallback);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
int childCount = getChildCount();
if(childCount != 2){
throw new RuntimeException("子Vview必须为两个view");
}
mFirstView = getChildAt(0);
mDragView = getChildAt(1);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//获取第一个子View的高度 方便后面判断只能在搞高度范围内被拖动
mFirstViewHeight = mFirstView.getMeasuredHeight();
Log.i("JUSTH","firstViewHeight=="+mFirstViewHeight);
}
//创建ViewDragHelper的callBack对象
private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
/**
* 返回true 代表能拖到 返回false 代表不能拖动
* @param child
* @param pointerId
* @return
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
//代表只有在拖动的view是第二个view的时候,才允许拖动
return child == mDragView;
}
//当触摸释放时 会执行该方法
/**
* 当拖动距离大于firstView的一半高度是 打开 否则 关闭
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if(mDragView.getTop() > mFirstViewHeight/2){
mDragHelper.settleCapturedViewAt(0,mFirstViewHeight);
}else if(mDragView.getTop() <= mFirstViewHeight/2){
mDragHelper.settleCapturedViewAt(0,0);
}
invalidate();
}
//管理竖直方向上是否能被拖动 以及被拖动的范围
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
if(top < 0){
top = 0;
}
if(top>mFirstViewHeight){
top = mFirstViewHeight;
}
return top;
}
};
//相应drawerHelper中的释放滚动事件
@Override
public void computeScroll() {
if(mDragHelper.continueSettling(true)){
invalidate();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return true;
}
}
好了,到这里,拖拽的效果就已经写完了!
下面,我们接着看,看二手车之家的效果,可以拖拽的部分,应该是一个类似ListView,RecycleView这样的一个效果,那我们们这里就有listView来代替吧,只要实现了事件的拦截与分发,不管使用哪种控件,都是一样的:
/**
* Created by DELL on 2017/9/12.
* Description :
*/
public class VerticalDragView extends FrameLayout {
private ViewDragHelper mDragHelper;
private View mFirstView;
private View mDragView;
private int mFirstViewHeight;
private boolean isFirstViewOpen = false;
public VerticalDragView(@NonNull Context context) {
this(context,null);
}
public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDragHelper = ViewDragHelper.create(this,mCallback);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
int childCount = getChildCount();
if(childCount != 2){
throw new RuntimeException("子Vview必须为两个view");
}
mFirstView = getChildAt(0);
mDragView = getChildAt(1);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//获取第一个子View的高度 方便后面判断只能在搞高度范围内被拖动
mFirstViewHeight = mFirstView.getMeasuredHeight();
Log.i("JUSTH","firstViewHeight=="+mFirstViewHeight);
}
//创建ViewDragHelper的callBack对象
private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
/**
* 返回true 代表能拖到 返回false 代表不能拖动
* @param child
* @param pointerId
* @return
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
//代表只有在拖动的view是第二个view的时候,才允许拖动
return child == mDragView;
}
//当触摸释放时 会执行该方法
/**
* 当拖动距离大于firstView的一半高度是 打开 否则 关闭
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if(mDragView.getTop() > mFirstViewHeight/2){
mDragHelper.settleCapturedViewAt(0,mFirstViewHeight);
isFirstViewOpen = true;
}else if(mDragView.getTop() <= mFirstViewHeight/2){
mDragHelper.settleCapturedViewAt(0,0);
isFirstViewOpen = false;
}
invalidate();
}
//管理竖直方向上是否能被拖动 以及被拖动的范围
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
if(top < 0){
top = 0;
}
if(top>mFirstViewHeight){
top = mFirstViewHeight;
}
return top;
}
};
//相应drawerHelper中的释放滚动事件
@Override
public void computeScroll() {
if(mDragHelper.continueSettling(true)){
invalidate();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return true;
}
int downY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//当第一个view打开的时候 拦截事件
if(isFirstViewOpen){
return true;
}
//当listView下拉的时候,当其可见的最上面一个条目是第一条 且该listview在最上端的时候 拦截事件
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
downY = (int) ev.getY();
mDragHelper.processTouchEvent(ev);
break;
case MotionEvent.ACTION_MOVE:
if((ev.getY() - downY)>0 && !canChildScrollUp()){
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
private boolean isFirst(){
ListView listView = (ListView) mDragView;
return listView.getFirstVisiblePosition() == 0;
}
public boolean canChildScrollUp() {
if (android.os.Build.VERSION.SDK_INT < 14) {
if (mDragView instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mDragView;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
.getTop() < absListView.getPaddingTop());
} else {
return ViewCompat.canScrollVertically(mDragView, -1) || mDragView.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mDragView, -1);
}
}
}
效果如下:(虽然可能是丑了点,但毕竟效果是有了)
好了,今天就到这里吧!