先看看效果图:
自定义布局控件:
public class DragLayout extends FrameLayout {
private int title; //限制上滑后的顶部标题高度大小
private Status mStatus = Status.Open; //默认底部是不上滑的
private View mTopContent;
private View mMainContent;
private ViewDragHelper mDragHelper;
private int mDragRange,mMainTop,mWidth,mHeight; //mDragRange为拖拽范围,mMainTop为底部面板离父布局顶部的长度
public DragLayout(@NonNull Context context) {
this(context,null);
}
public DragLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public DragLayout(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDragHelper = ViewDragHelper.create(this,0.5f, mCallBack);
}
ViewDragHelper.Callback mCallBack=new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
boolean directionCheck=mDragHelper.checkTouchSlop(ViewDragHelper.DIRECTION_VERTICAL,pointerId);
return (child==mMainContent||child==mTopContent)&&directionCheck;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
if (mMainTop+dy<title){
return title;
}else if(mMainTop+dy>mDragRange+title){
return mDragRange+title;
}
return top;
}
@Override
public int getViewVerticalDragRange(View child) {
return mDragRange;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if(changedView==mMainContent){
mMainTop=top;
}else{
mMainTop+=dy;
}
if (mMainTop < title) {
mMainTop = title;
} else if (mMainTop > mDragRange+title) {
mMainTop = mDragRange+title;
}
if (changedView == mTopContent) {
layoutContent(); //如果拖动的是顶部面板则进行强制布局移动
}
dispatchDragEvent(mMainTop);
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if (yvel > 0) {
open();
} else if (yvel == 0 && mMainTop > mDragRange * 0.5f) {
open();
} else {
close();
}
}
};
public void close() {
mMainTop = title;
// 执行动画,返回true代表有未完成的动画, 需要继续执行
if (mDragHelper.smoothSlideViewTo(mMainContent, 0, mMainTop)) {
// 注意:参数传递根ViewGroup
ViewCompat.postInvalidateOnAnimation(this);
}
}
private void layoutContent() {
mMainContent.layout(0, mMainTop, mWidth, mHeight+mMainTop);
mTopContent.layout(0, -mHeight/6, mWidth, mHeight);
}
public void open() {
mMainTop = mDragRange+title;
if (mDragHelper.smoothSlideViewTo(mMainContent, 0, mMainTop)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
protected void dispatchDragEvent(int mainTop) {
float percent = mainTop / (float)mDragRange;
ViewHelper.setTranslationY(mTopContent, mHeight / 8 - mHeight / 8 * percent); //实现顶部下滑
if (mListener != null) {
mListener.onDraging(percent);
}
Status lastStatus = mStatus;
if (updateStatus(mainTop) != lastStatus) {
if (mListener == null) {
return;
}
if (lastStatus == Status.Draging) {
if (mStatus == Status.Close) {
mListener.onClose();
} else if (mStatus == Status.Open) {
mListener.onOpen();
}
}
}
}
public interface OnLayoutDragingListener {
void onOpen();
void onClose();
void onDraging(float percent);
}
private OnLayoutDragingListener mListener;
public void setOnLayoutDragingListener(OnLayoutDragingListener l) {
mListener = l;
}
private Status updateStatus(int mainTop) {
if (mainTop == title) {
mStatus = Status.Close;
} else if (mainTop == mDragRange+title) {
mStatus = Status.Open;
} else {
mStatus = Status.Draging;
}
return mStatus;
}
public enum Status {
Open, Close, Draging
}
public Status getStatus() {
return mStatus;
}
public void setStatus(Status mStatus) {
this.mStatus = mStatus;
}
@Override
public boolean onInterceptTouchEvent(android.view.MotionEvent ev) {
//将Touch事件传递给ViewDragHelper
return mDragHelper.shouldInterceptTouchEvent(ev);
};
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
//将Touch事件传递给ViewDragHelper
mDragHelper.processTouchEvent(event);
} catch (Exception e) {
}
return true;
}
@Override
public void computeScroll() {
// 高频率调用,决定是否有下一个变动等待执行
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
mMainContent.layout(0,mMainTop, mWidth, mMainTop+mHeight);
mTopContent.layout(0, -mHeight/6, mWidth, mHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//拿到宽高
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
//设置拖动范围
mDragRange = (int)(mHeight*0.4);
mMainTop=(int)(mHeight*0.5);
title=mMainTop-mDragRange;
}
/**
* 填充结束时获得两个子布局的引用
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
int childCount = getChildCount();
// 必要的检验
if (childCount < 2) {
throw new IllegalStateException(
"You need two childrens in your content");
}
if (!(getChildAt(0) instanceof ViewGroup)
|| !(getChildAt(1) instanceof ViewGroup)) {
throw new IllegalArgumentException(
"Your childrens must be an instance of ViewGroup");
}
mTopContent = getChildAt(0);
mMainContent = getChildAt(1);
}
}
提示:可以在外部获得DragLayout对象后设置OnLayoutDragingListener来监听拖动过程
public class MainActivity extends AppCompatActivity {
private FragmentManager fragmentManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager=getSupportFragmentManager();
fragmentManager.beginTransaction().add(R.id.container,new FirstFragment()).commit();
}
}
public class FirstFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.first_fragment_layout,container,false);
return view;
}
}
<com.example.duodian.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/fl_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="40dp"
android:paddingLeft="10dp"
android:paddingTop="45dp"
android:background="#f43">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="这是顶面板" />
</LinearLayout>
<LinearLayout
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="40dp"
android:paddingLeft="10dp"
android:paddingTop="50dp"
android:background="#ae3">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="这是主面板" />
</LinearLayout>
</com.example.duodian.DragLayout>
本文章如有地方需要修改请在下方评论中指出。