Android筆記(一):ViewDragHelper實現底部上滑同時底部下滑
阿新 • • 發佈:2019-02-14
先看看效果圖:
自定義佈局控制元件:
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(); } }
注意:DragLayout必須要有且只有兩個子控制元件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>
本文章如有地方需要修改請在下方評論中指出。