前言
在应用中,上拉抽屉是一个很常见的控件,它可以很优雅地隐藏与显示一些附加内容,比如评论列表等。
教程
自定义控件
首先需要编写一个自定义控件类,以继承自LinearLayout
为例:
SlideUpLayout.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| public class SlideUpLayout extends LinearLayout {
private View bar; private View content; private Scroller scroller; private int downY;
public SlideUpLayout(Context context) { this(context, null); }
public SlideUpLayout(Context context, AttributeSet attrs) { super(context, attrs); }
@SuppressLint("DrawAllocation") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); scroller = new Scroller(getContext()); bar = getChildAt(0); content = getChildAt(1); }
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); bar.layout(0, getMeasuredHeight() - bar.getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight()); content.layout(0, getMeasuredHeight(), getMeasuredWidth(), bar.getBottom() + content.getMeasuredHeight()); }
@SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downY = (int) event.getY(); break; case MotionEvent.ACTION_MOVE: int offsetY = (int) event.getY() - downY; int toScroll = getScrollY() - offsetY; if (toScroll < 0) { toScroll = 0; } else if (toScroll > content.getMeasuredHeight()) { toScroll = content.getMeasuredHeight(); } scrollTo(0, toScroll); downY = (int) event.getY(); break; case MotionEvent.ACTION_UP: int offsetScroll = getScrollY(); if (offsetScroll > content.getMeasuredHeight() / 2) { scroller.startScroll(getScrollX(), getScrollY(), 0, content.getMeasuredHeight() - offsetScroll, 500); } else { scroller.startScroll(getScrollX(), getScrollY(), 0, -offsetScroll, 500); } invalidate(); break; } return true; }
@Override public void computeScroll() { super.computeScroll(); if (scroller.computeScrollOffset()) { scrollTo(scroller.getCurrX(), scroller.getCurrY()); invalidate(); } }
}
|
布局
重点在于net.coolkk.test.SlideUpLayout
这个控件(请将前部分包名改为自己项目的包名),它的内部有ID为SlideUpLayoutBar
和SlideUpLayoutContent
的两个控件,前者为可视部分,后者为隐藏部分。
activity_main.xml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/image_activity_main_background" tools:context=".MainActivity">
<net.coolkk.test.SlideUpLayout android:id="@+id/SlideUpLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/SlideUpLayoutBar" android:layout_width="match_parent" android:layout_height="60dp" android:background="@color/colorPrimary">
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/SlideUpLayoutContent" android:layout_width="match_parent" android:layout_height="300dp" android:background="@color/colorAccent">
</androidx.constraintlayout.widget.ConstraintLayout>
</net.coolkk.test.SlideUpLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
|
演示
参考资料