时间:2022-10-13 14:16:43 | 栏目:Android代码 | 点击:次
这里给大家提供一个类似QQ聊天那种可以左侧滑出菜单的自定义控件。希望对大家有帮助。参考了一些网友的做法,自己整理优化了一下,用法非常简单,就一个类,不需要自己写任何的代码,只要添加上布局就能实现侧滑菜单效果,非常方便。不多说,一看就懂。
先来看看效果:
先看看实现:
package com.kokjuis.travel.customView; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Point; import android.support.v4.widget.ViewDragHelper; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import com.kokjuis.travel.R; /** * <com.kokjuis.travel.customView.SwipeDragLayout android:id="@+id/swip_layout" android:layout_width="match_parent" android:layout_height="70dp" android:clickable="true" app:ios="true" app:click_to_close="true" > <LinearLayout android:id="@+id/id_back" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="right"> <Button android:id="@+id/btn_delete" android:layout_width="70dp" android:layout_height="70dp" android:background="#00ffff" android:text="删除" /> <Button android:id="@+id/btn_move" android:layout_marginLeft="1dp" android:layout_width="70dp" android:layout_height="70dp" android:background="#00ffff" android:text="移动" /> </LinearLayout> <RelativeLayout android:id="@+id/id_front" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white"> .........你的布局 </RelativeLayout> </com.kokjuis.travel.customView.SwipeDragLayout> * * * * 用法就是直接用这个控件把需要的布局包起来 * 可滑动的layout extends FrameLayout */ public class SwipeDragLayout extends FrameLayout { private static SwipeDragLayout mCacheView; private View contentView;//列表的view private View menuView;//滑出菜单的view private ViewDragHelper mDragHelper; private Point originPos = new Point(); private boolean isOpen, ios, clickToClose; private float offset; private float needOffset = 0.2f; private SwipeListener mListener; public SwipeDragLayout(Context context) { this(context, null); } public SwipeDragLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SwipeDragLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SwipeDragLayout); needOffset = array.getFloat(R.styleable.SwipeDragLayout_need_offset, 0.2f); //是否有回弹效果 ios = array.getBoolean(R.styleable.SwipeDragLayout_ios, false); clickToClose = array.getBoolean(R.styleable.SwipeDragLayout_click_to_close, false); init(); array.recycle(); } public static SwipeDragLayout getmCacheView() { return mCacheView; } //初始化dragHelper,对拖动的view进行操作 private void init() { mDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() { @Override public boolean tryCaptureView(View child, int pointerId) { return child == contentView; } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { if (releasedChild == contentView) { if (isOpen()) { if (offset != 1 && offset > (1 - needOffset)) { open(); } else if (offset == 1) { if (clickToClose) { close(); } } else { close(); } } else { if (offset != 0 && offset < needOffset) { close(); } else if (offset == 0) { getParent().requestDisallowInterceptTouchEvent(false); } else { open(); Log.d("Released and isOpen", "" + isOpen); if (mListener != null) { mListener.onOpened(SwipeDragLayout.this); } } } invalidate(); } } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { //滑动距离,如果启动效果,则可滑动3/2倍菜单宽度的距离 final int leftBound = getPaddingLeft() - (ios ? menuView.getWidth() * 3 / 2 : menuView.getWidth()); final int rightBound = getWidth() - child.getWidth(); final int newLeft = Math.min(Math.max(left, leftBound), rightBound); return newLeft; } @Override public int getViewHorizontalDragRange(View child) { return contentView == child ? menuView.getWidth() : 0; } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { final int childWidth = menuView.getWidth(); offset = -(float) (left - getPaddingLeft()) / childWidth; //offset can callback here if (mListener!=null){ mListener.onUpdate(SwipeDragLayout.this,offset); } } }); } public void setClickToClose(boolean clickToClose) { this.clickToClose = clickToClose; } public void setIos(boolean ios) { this.ios = ios; } public boolean isOpen() { return isOpen; } public void open() { mCacheView = SwipeDragLayout.this; mDragHelper.settleCapturedViewAt(originPos.x - menuView.getWidth(), originPos.y); isOpen = true; } public void smoothOpen(boolean smooth) { mCacheView = SwipeDragLayout.this; if (smooth) { mDragHelper.smoothSlideViewTo(contentView, originPos.x - menuView.getWidth(), originPos.y); } else { contentView.layout(originPos.x - menuView.getWidth(), originPos.y, menuView.getLeft(), menuView.getBottom()); } } //滑动关闭方法 private void smoothClose(boolean smooth) { if (smooth) { mDragHelper.smoothSlideViewTo(contentView, getPaddingLeft(), getPaddingTop()); postInvalidate(); } else { contentView.layout(originPos.x, originPos.y, menuView.getRight(), menuView.getBottom()); } isOpen = false; mCacheView = null; } public void close() { mDragHelper.settleCapturedViewAt(originPos.x, originPos.y); isOpen = false; mCacheView = null; if (mListener != null) { mListener.onClosed(SwipeDragLayout.this); } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: if (mCacheView != null) { if (mCacheView != this) { mCacheView.smoothClose(true); return true;//点击关闭后不执行其他click事件 }else if(isOpen()&&ev.getX()<menuView.getLeft()){ mDragHelper.smoothSlideViewTo(contentView, getPaddingLeft(), getPaddingTop()); postInvalidate(); return true;//点击关闭后不执行其他click事件 } getParent().requestDisallowInterceptTouchEvent(true); } break; } return mDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { mDragHelper.processTouchEvent(event); return true; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); originPos.x = contentView.getLeft(); originPos.y = contentView.getTop(); } @Override public void computeScroll() { if (mDragHelper.continueSettling(true)) { invalidate(); } } @Override protected void onFinishInflate() { super.onFinishInflate(); contentView = getChildAt(1); menuView = getChildAt(0); FrameLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.gravity = Gravity.RIGHT; menuView.setLayoutParams(params); //重写OnClickListener会导致关闭失效 if (contentView!=null){ contentView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (clickToClose&&isOpen()){ smoothClose(true); return; } if (mListener!=null){ mListener.onClick(SwipeDragLayout.this); } } }); } } @Override protected void onDetachedFromWindow() { if (mCacheView == this) { mCacheView.smoothClose(false); mCacheView = null; } super.onDetachedFromWindow(); } @Override public void setOnTouchListener(OnTouchListener l) { super.setOnTouchListener(l); } public void addListener(SwipeListener listener) { mListener = listener; } //滑动监听 public interface SwipeListener { /** * 拖动中,可根据offset 进行其他动画 * @param layout * @param offset 偏移量 */ void onUpdate(SwipeDragLayout layout, float offset); /** * 展开完成 * @param layout */ void onOpened(SwipeDragLayout layout); /** * 关闭完成 * @param layout */ void onClosed(SwipeDragLayout layout); /** * 点击内容layout {@link #onFinishInflate()} * @param layout */ void onClick(SwipeDragLayout layout); } }
必要的一些配置,添加到attrs.xml里:
<declare-styleable name="SwipeDragLayout"> <attr name="need_offset" format="float" /> <attr name="ios" format="boolean" /> <attr name="click_to_close" format="boolean" /> </declare-styleable>
看看用法,非常简单,直接放在你想要的item布局里面就可以了:
<com.kokjuis.travel.customView.SwipeDragLayout android:id="@+id/swip_layout" android:layout_width="match_parent" android:layout_height="70dp" android:clickable="true" app:ios="true" app:click_to_close="true" > <LinearLayout android:id="@+id/id_back" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="right"> <Button android:id="@+id/btn_delete" android:layout_width="70dp" android:layout_height="70dp" android:background="#00ffff" android:text="删除" /> <Button android:id="@+id/btn_move" android:layout_marginLeft="1dp" android:layout_width="70dp" android:layout_height="70dp" android:background="#00ffff" android:text="移动" /> </LinearLayout> <RelativeLayout android:id="@+id/id_front" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white"> //你自己的布局..... </RelativeLayout> </com.kokjuis.travel.customView.SwipeDragLayout>