在看了大神的自定义view之汽车折叠列表之后,发现这个效果还是挺不错的。恰巧在项目中也用的到,就仔细研究一下,不能再老是复制粘贴,不知所以然,下面就来分享一下,自己也做一下总结。希望对自己和别人都有所帮助。
1.创建VerticalDragListView类继承布局FrameLayout,当然类是可以随便取名
创建ViewDragHelper 对象同时,创建回调。
public class VerticalDragListView extends FrameLayout {
//可以认为这是系统给我们写好的工具类
private ViewDragHelper mDragHelper;
public VerticalDragListView(Context context) {
this(context, null);
}
public VerticalDragListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VerticalDragListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDragHelper=ViewDragHelper.create(this,mDragHelperCallback);
}
private ViewDragHelper.Callback mDragHelperCallback=new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
//指定该子view是否可以拖动,就是child
return true;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//垂直拖动的位置
return top;
}
//列表垂直拖动
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//水平拖动的位置
return left;
}
};
2.只有列表可以拖动,其他控件不能拖动
重写onFinishInflate()方法 取出列表view
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//获取子布局个数。只能是2个,否则抛异常
int childCount=getChildCount();
if (childCount!=2){
throw new RuntimeException("VerticalDragListView 只能包含两个子布局");
}
//取出第2个子view,可以拖动的
mDragListView=getChildAt(1);
}
判断是否是列表view,否则不可以拖动
@Override
public boolean tryCaptureView(View child, int pointerId) {
//指定该子view是否可以拖动,就是child
//只有列表可以拖动
return mDragListView==child;
}
只需要垂直拖动所以不需要重写
//列表只能垂直拖动
/* @Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//水平拖动的位置
return left;
}*/
判断拖动的距离只能是后面view的高度,先测量后面view的高度,在写逻辑
测量:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed){
//测量view的高度
View menuView=getChildAt(0);
mMenuHeight=menuView.getMeasuredHeight();
}
}
逻辑:
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//随时垂直拖动移动的位置
if (top<=0){
top=0;
}
if (top >= mMenuHeight){
top=mMenuHeight;
}
return top;
}
3. 手指松开的时候两者选择之一,要么打开要么关闭
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
if (releasedChild==mDragListView){
if (mDragListView.getTop() >mMenuHeight/2){
//滚动到菜单的高度(打开)
mDragHelper.settleCapturedViewAt(0,mMenuHeight);
}else{
// 滚动到0的位置(关闭)
mDragHelper.settleCapturedViewAt(0,0);
}
//重绘
invalidate();
}
}
@Override
public void computeScroll() {
if (mDragHelper.continueSettling(true)){
invalidate();
}
}
4.解决列表滑动冲突
private float mDownY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(mMenuIsOpen){
return true;
}
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
mDownY=ev.getY();
//让DragHelper处理一个完整事件
mDragHelper.processTouchEvent(ev);
break;
case MotionEvent.ACTION_MOVE:
float moveY=ev.getY();
if ((moveY-mDownY) >0 && !canChildScrollUp()){
//向下滑动并且滚动到顶部 拦截不让listview处理
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
/**
* 判断view是否滚动到最顶部,还能不能向上滚
* @return
*/
public boolean canChildScrollUp() {
if (android.os.Build.VERSION.SDK_INT < 14) {
if (mDragListView instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mDragListView;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
.getTop() < absListView.getPaddingTop());
} else {
return ViewCompat.canScrollVertically(mDragListView, -1) || mDragListView.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mDragListView, -1);
}
}