您的位置:首页 > 其它

QQ聊天界面侧滑删除

2016-08-19 20:33 239 查看

侧滑删除

效果图:



实现步骤:

1. 创建SwipeLayout

public class SwipeLayout extends FrameLayout {
public SwipeLayout(Context context) {
super(context);
}
public SwipeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
}


2. 创建SwipeLayout的布局

<com.itcast.jq.swipelayout.view.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="55dp" >

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="right"
android:orientation="horizontal" >

<TextView
android:layout_width="55dp"
android:layout_height="match_parent"
android:background="#44ff0000"
android:gravity="center"
android:text="删除" />

<TextView
android:layout_width="55dp"
android:layout_height="match_parent"
android:background="#4400ff00"
android:gravity="center"
android:text="取消" />
</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#33ff0000" >

<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:paddingLeft="10dp"
android:text="帅的人" />
</LinearLayout>

</com.itcast.jq.swipelayout.view.SwipeLayout>


3. 重写回调方法获取控件尺寸及初始化位置

@Override
protected void onFinishInflate() {
super.onFinishInflate();
// System.out.println("---------onFinishInflate");

if (getChildCount() < 2) {
throw new IllegalStateException("SwipeLayout must has at least 2 children");
}

mBackView = getChildAt(0);
mFrontView = getChildAt(1);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// System.out.println("---------onMeasure");

// 注意不能在onFinishInflate方法中获取宽高
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();

mBackWidth = mBackView.getMeasuredWidth();
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// System.out.println("---------onMeasure");
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// System.out.println("---------onMeasure");

// 设置两个子控件的初始化位置
mFrontView.layout(0, 0, mWidth, mHeight);
mBackView.layout(mWidth, 0, mWidth + mBackWidth, mHeight);
}


4. 使用ViewDragHelper实现拖动效果

public SwipeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {
// 1. 创建ViewDragHelper对象
mDragHelper = ViewDragHelper.create(this, mCallback);
}

// 4. 处理回调方法
Callback mCallback = new Callback() {

// 根据返回决定是否可以拖动
public boolean tryCaptureView(View child, int pointerId) {
return true;
};

// 根据返回值决定被拖动的视图将要显示的位置
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
};
};

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 2. 让ViewDragHelper决定是否拦截事件
return mDragHelper.shouldInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// 3. 传触摸事件传给ViewDragHelper处理
mDragHelper.processTouchEvent(event);

// 按下时需要返回true以便能持续地接收到触摸事件
if (event.getAction() == MotionEvent.ACTION_DOWN) {
return true;
}
return super.onTouchEvent(event);
}


5. 限制滑动范围

在clampViewPositionHorizontal方法中进行两个View的滑动范围限制

// 根据返回值决定被拖动的视图将要显示的位置
public int clampViewPositionHorizontal(View child, int left, int dx) {
// 限制frontView拖动的范围, [-mBackWidth, 0]
if (child == mFrontView) {
if (left < -mBackWidth) {
left = -mBackWidth;
} else if (left > 0) {
left = 0;
}
}

if (child == mBackView) {
// 限制backView拖动的范围: [mWidth-mBackWidth, mWidth]
if (left < mWidth-mBackWidth) {
left = mWidth-mBackWidth;
} else if (left > mWidth) {
left = mWidth;
}
}
return left;
}


6. 关联滑动效果

在onViewPositionChanged方法通过offsetLeftAndRight方法关联滑动

// 当子控件位置发生改变后调用
public void onViewPositionChanged(View changedView,
int left, int top, int dx, int dy) {
// 关联两个子控件的滑动
if (changedView == mFrontView) { // 滑动mFrontView时,同时滑动mBackView
mBackView.offsetLeftAndRight(dx);
} else if (changedView == mBackView) {// 滑动mBackView时,同时滑动mFrontView
mFrontView.offsetLeftAndRight(dx);
}

// 解决往左滑时mBackView空白的问题,同时兼容低版本2.3
invalidate();
};


7. 打开或关闭SwipeLayout

// 松开手释放子控件时回调
// xvel: 往右甩速度大于0
public void onViewReleased(View releasedChild, float xvel, float yvel) {
int left = mFrontView.getLeft();
if (xvel == 0 && left < -mBackWidth /2) {
open();
} else if (xvel < 0) { // 往左甩
open();
} else {
close();
}
};
/** 关闭SwipeLayout*/
public void close() {
// 平滑滚动到某一位置: 第一步
mDragHelper.smoothSlideViewTo(mFrontView, 0, 0);
ViewCompat.postInvalidateOnAnimation(this);
}

/** 打开SwipeLayout */
public void open() {
mDragHelper.smoothSlideViewTo(mFrontView, -mBackWidth, 0);
ViewCompat.postInvalidateOnAnimation(this);
}

// 平滑滚动到某一位置: 第二步
@Override
public void computeScroll() {
super.computeScroll();
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}


8. 监听滑动状态(打开、关闭、准备打开、准备关闭)

// 当子控件位置发生改变后调用
public void onViewPositionChanged(View changedView,
int left, int top, int dx, int dy) {
...
// 监听滑动状态(打开,关闭,准备打开,准备关闭)
listenDragStatus();
};
// ===========监听拖动状态(begin)============
public enum DragStatus {
OPEN, CLOSE, DRAGGING
}
private DragStatus mCurrentStatus = DragStatus.CLOSE;

public DragStatus getDragStatus() {
return mCurrentStatus;
}

public interface OnDragListener {
public void open(SwipeLayout swipeLayout);
public void close(SwipeLayout swipeLayout);

/** 准备打开 */
public void onStartOpen(SwipeLayout swipeLayout);
/** 准备关闭 */
public void onStartClose(SwipeLayout swipeLayout);
}

private OnDragListener mOnDragListener;

public void setOnDragListener(OnDragListener onDragListener) {
this.mOnDragListener = onDragListener;
}

/** 监听拖动状态  */
protected void listenDragStatus() {
int left = mFrontView.getLeft();
DragStatus status;
if (left == 0) {
status = DragStatus.CLOSE;
} else if (left == -mBackWidth) {
status = DragStatus.OPEN;
} else {
status = DragStatus.DRAGGING;
}

if (mOnDragListener != null) {
if (status == DragStatus.OPEN) {
mOnDragListener.open(this);
} else if (status == DragStatus.CLOSE) {
mOnDragListener.close(this);
} else { // 当前状态为拖动状态
// 上一次状态为关闭,当前状态为拖动,则表示准备打开
if (mCurrentStatus == DragStatus.CLOSE) {
mOnDragListener.onStartOpen(this);
} else if (mCurrentStatus == DragStatus.OPEN) {
// 上一次状态为打开,当前状态为拖动,则表示准备关闭
mOnDragListener.onStartClose(this);
}
}
}
mCurrentStatus = status;
}
// ===========监听拖动状态(end)============


9. 集成到ListView(显示列表)

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mListView = (ListView) findViewById(R.id.list_view);
mAdapter = new ContactAdapter(this,
Arrays.asList(Constant.LIST_DATAS2));
mListView.setAdapter(mAdapter);
}
public class ContactAdapter extends MyBaseAdapter<String> {

public ContactAdapter(Context context, List<String> listDatas) {
super(context, listDatas);
}

@Override
public BaseHolder<String> createViewHolder(Context context,
ViewGroup parent, String bean, int position) {
return new ContactHolder(context, parent, this, position, bean);
}
}
public class ContactHolder extends BaseHolder<String> {
private TextView tvName;

public ContactHolder(Context context, ViewGroup parent,
MyBaseAdapter<String> adapter, int position, String bean) {
super(context, parent, adapter, position, bean);
}

@Override
public View createView(Context context, ViewGroup parent, int position, String bean) {
return super.inflater.inflate(R.layout.item_list, parent, false);
}

@Override
protected void initView() {
tvName = (TextView) super.itemView.findViewById(R.id.tv_name);
}

@Override
protected void onRefreshViews(String bean, int position) {
tvName.setText(bean);
}
}


10. 只打开一个SwipeLayout

public class SwipeLayoutManager {
// 单例
private static SwipeLayoutManager instance = new SwipeLayoutManager();
private SwipeLayoutManager() {
}
public static SwipeLayoutManager getInstance() {
return instance;
}

/** 用来记录打开的SwipeLayout */
private SwipeLayout mOpenSwipeLayout;

public SwipeLayout getOpenSwipeLayout() {
return mOpenSwipeLayout;
}

/** 记录列表中打开的SwipeLayout */
public void setOpenSwipeLayout(SwipeLayout swipeLayout) {
this.mOpenSwipeLayout = swipeLayout;
}

/** 关闭之前打开的SwipeLayout */
public void closeSwipeLayout() {
if (mOpenSwipeLayout != null) {
mOpenSwipeLayout.close();
mOpenSwipeLayout = null; // 关闭后需要置为空
}
}
}
// 列表的Holder类的查找item子控件的方法
protected void initView() {
...
SwipeLayout layout =  (SwipeLayout) super.itemView;
layout.setOnDragListener(new OnDragListener() {
@Override
public void onOpen(SwipeLayout swipeLayout) {
}
// 准备打开某一个列表项的SwipeLayout时,先关闭之前打开的,再保存当前打开的
@Override
public void onStartOpen(SwipeLayout swipeLayout) {
if (SwipeLayoutManager.getInstance().getOpenSwipeLayout()
!= swipeLayout) {
SwipeLayoutManager.getInstance().closeSwipeLayout();
}
SwipeLayoutManager.getInstance().setOpenSwipeLayout(swipeLayout);
}

@Override
public void onStartClose(SwipeLayout swipeLayout) {
}
@Override
public void onClose(SwipeLayout swipeLayout) {
}
});
}


11. 优化体验:如果左右滑动请求父控件不要拦截事件

当左右滑动时,请求列表不要拦截事件
// 当子控件位置发生改变后调用
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
...
// 如果是左右滑动SwipeLayout,请求父控件ListView不要拦截事件,则
// ListView不会上下滚动,并且会把触摸事件传递下来,让SwipeLayout处理
if (Math.abs(dx) > Math.abs(dy)) {
requestDisallowInterceptTouchEvent(true);
}
}


12. 滚动列表时关闭打开的SwipeLayout

// 滚动列表时,关闭打开的SwipeLayout
mListView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
SwipeLayoutManager.getInstance().closeSwipeLayout();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});


13. 列表项中的操作菜单点击事件

// 在列表的Holder类中添加
protected void initView() {
tvName = (TextView) super.itemView.findViewById(R.id.tv_name);
tv_delete = (TextView) super.itemView.findViewById(R.id.tv_delete);
tv_cancel = (TextView) super.itemView.findViewById(R.id.tv_cancel);

OnClickListener onClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
SwipeLayoutManager.getInstance().closeSwipeLayout();
switch (v.getId()) {
case R.id.tv_delete:
showToast("取消:" + bean);
break;
case R.id.tv_cancel:
showToast("删除:" + bean);
break;
}
}
};
tv_delete.setOnClickListener(onClickListener);
tv_cancel.setOnClickListener(onClickListener);
...
}


完整源码:点击下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息