效果图:
布局中去指定自定义FrameLayout:
<!-- 自定义仿QQ列表Item滑动 --> <view.custom.shangguigucustomview.MyCustomFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="80dp" android:orientation="vertical" tools:context=".ShangGuiguTestActivity" android:background="@android:color/darker_gray"> <TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="80dp" android:background="@android:color/white" android:textColor="@android:color/black" android:textSize="20dp" android:text="你有一条消息未读" android:gravity="center" android:drawableLeft="@mipmap/ic_launcher_round" android:paddingRight="40dp" /> <TextView android:id="@+id/tv_menu" android:layout_width="wrap_content" android:layout_height="80dp" android:background="@android:color/holo_orange_dark" android:textColor="#f00000" android:textSize="20dp" android:text="删除" android:gravity="center" android:paddingLeft="30dp" android:paddingRight="30dp" /> </view.custom.shangguigucustomview.MyCustomFrameLayout>
自定义FrameLayout:
public class MyCustomFrameLayout extends FrameLayout { // 继承于FrameLayout属于继承ViewGroup,因为它包含了孩子 private static final String TAG = MyCustomFrameLayout.class.getSimpleName(); /** * 拿到两个控件的宽和三个的高 * @param context * @param attrs */ private int myHeigth; // 统一的高度 private int myContentWidth; // 中间内容TextView的宽度 private int myMenuWidth; // 菜单的宽度 private Scroller scroller; public MyCustomFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); scroller = new Scroller(context); } private View tvContent; private View tvMenu; /** * 当布局加载完成?后就会回调此方法 */ @Override protected void onFinishInflate() { super.onFinishInflate(); tvContent = getChildAt(0); // 拿到第一个孩子 tvMenu = getChildAt(1); // 得到第二个孩子 } /** * 测量两个孩子的高宽 * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); myContentWidth = tvContent.getMeasuredWidth(); // 等价于getMeasuredWidth();因为这个是后去父控件的宽度,父控件和这个孩子控件宽度是一样的 myHeigth = getMeasuredHeight(); myMenuWidth = tvMenu.getMeasuredWidth(); } /** * 为何不让实现者强制重写,应该在FrameLayout中已经重写过了 * @param changed * @param left * @param top * @param right * @param bottom */ @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); // 指定位置 tvContent.layout(0, 0, myContentWidth, myHeigth); tvMenu.layout(myContentWidth, 0 , myContentWidth + myMenuWidth, myHeigth); } private float startX; private int x; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: startX = event.getX(); break; case MotionEvent.ACTION_UP: float upEnd = event.getX(); float thisUp = upEnd; // float myx = getScrollX() - thisUp; if (getScrollX() > myMenuWidth / 2) { // x = myMenuWidth; openM(); } else { // x = 0; closeM(); } /*invalidate(); scroller.startScroll(getScrollX(), getScrollY(), x, Math.abs(1000));*/ // scrollTo(x, getScrollY()); break; case MotionEvent.ACTION_MOVE: int endX = (int) event.getX(); int thisX = (int) (endX - startX); int scrollX = getScrollX(); Log.i(TAG, ">>>>> scrollX" + scrollX); x = scrollX - thisX; // 非法值的屏蔽 if (x < 0) { x = 0; } else if (x > myMenuWidth) { x = myMenuWidth; } scrollTo(x, getScrollY()); startX = event.getX(); break; default: break; } return true; } private void openM() { invalidate(); int dx = myMenuWidth - getScrollX(); scroller.startScroll(getScrollX(), getScrollY(), dx, Math.abs(1000)); } private void closeM() { invalidate(); int dx = 0 - getScrollX(); scroller.startScroll(getScrollX(), getScrollY(), dx, Math.abs(1000)); } @Override public void computeScroll() { super.computeScroll(); if (scroller.computeScrollOffset()) { int currX = scroller.getCurrX(); scrollTo(currX, 0); invalidate(); } } }