先看看效果图:
自定义布局控件:
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>
本文章如有地方需要修改请在下方评论中指出。