最近在网上学到一篇类似QQ消息左滑删除的demo,完善了下代码,感觉还不错,特此分享一波:
CustomSwipeListView.java 是个继承自ListView的类,里面调用了自定义View 类SwipeItemView.java的一个收缩的方法。其实QQ消息删除这个动画可以有很多种方法实现,这里我们介绍的方法是:常规ListView的每个Item展示方式不变,只是在Adapter类里面去绑定该显示的数据和删除等字样。具体详见如下:
自定义的ListView:
package com.ryg.slideview;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
public class CustomSwipeListView extends ListView {
private static final String TAG = "ListViewCompat";
public static SwipeItemView mFocusedItemView;
private int mPosition;
public CustomSwipeListView(Context context) {
super(context);
}
public CustomSwipeListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSwipeListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void shrinkListItem(int position) {
View item = getChildAt(position);
if (item != null) {
try {
((SwipeItemView) item).shrink();
}
catch (ClassCastException e) {
e.printStackTrace();
}
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
int x = (int) event.getX();
int y = (int) event.getY();
// 我们想知道当前点击了哪一行
int position = pointToPosition(x, y);
Log.e(TAG, "postion=" + position);
if (position != INVALID_POSITION) {
int firstPos = getFirstVisiblePosition();
mFocusedItemView = (SwipeItemView) getChildAt(position - firstPos);
}
}
default:
break;
}
if (mFocusedItemView != null) {
mFocusedItemView.onRequireTouchEvent(event);
}
return super.onTouchEvent(event);
}
}
自定义的布局:
package com.ryg.slideview;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.TextView;
public class SwipeItemView extends LinearLayout {
private static final String TAG = "SlideView";
private Context mContext;
private LinearLayout mViewContent;
private RelativeLayout mHolder;
private Scroller mScroller;
private OnSlideListener mOnSlideListener;
private int mHolderWidth;
private int mLastX = 0;
private int mLastY = 0;
private static final int TAN = 2;
private boolean isHorizontalMove = true;// 判断是否横滑
public interface OnSlideListener {
public static final int SLIDE_STATUS_OFF = 0;
public static final int SLIDE_STATUS_START_SCROLL = 1;
public static final int SLIDE_STATUS_ON = 2;
/**
* @param view
* current SlideView
* @param status
* SLIDE_STATUS_ON or SLIDE_STATUS_OFF
*/
public void onSlide(View view, int status);
}
public SwipeItemView(Context context) {
super(context);
initView();
}
public SwipeItemView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
private void initView() {
mContext = getContext();
// 初始化弹性滑动对象
mScroller = new Scroller(mContext);
// 设置其方向为横向
setOrientation(LinearLayout.HORIZONTAL);
// 将slide_view_merge加载进来
View.inflate(mContext, R.layout.slide_view_merge, this);
mViewContent = (LinearLayout) findViewById(R.id.view_content);
mHolder = (RelativeLayout) findViewById(R.id.holder);
mHolder.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
mHolderWidth = mHolder.getMeasuredWidth();
mHolder.setLayoutParams(new LinearLayout.LayoutParams(mHolderWidth, LayoutParams.MATCH_PARENT));
}
public void setButtonText(CharSequence text) {
((TextView) findViewById(R.id.delete)).setText(text);
}
public void setContentView(View view) {
mViewContent.addView(view);
}
public void setOnSlideListener(OnSlideListener onSlideListener) {
mOnSlideListener = onSlideListener;
}
public void shrink() {
if (getScrollX() != 0) {
this.smoothScrollTo(0, 0);
}
}
public void onRequireTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
int scrollX = getScrollX();
Log.d(TAG, "x=" + x + " y=" + y);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
isHorizontalMove = true;// 按下默认为true,当移上下则为false
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
if (mOnSlideListener != null) {
mOnSlideListener.onSlide(this, OnSlideListener.SLIDE_STATUS_START_SCROLL);
}
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
// 上下移动
if (Math.abs(deltaX) < Math.abs(deltaY) * TAN) {
isHorizontalMove = false;
break;
}
if (!isHorizontalMove)
break;
int newScrollX = scrollX - deltaX;
if (deltaX != 0) {
if (newScrollX < 0) {
newScrollX = 0;
}
else if (newScrollX > mHolderWidth) {
newScrollX = mHolderWidth;
}
this.scrollTo(newScrollX, 0);
}
break;
}
case MotionEvent.ACTION_UP: {
int newScrollX = 0;
if (scrollX - mHolderWidth * 0.15 > 0) {
newScrollX = mHolderWidth;
}
this.smoothScrollTo(newScrollX, 0);
if (mOnSlideListener != null) {
mOnSlideListener.onSlide(this, newScrollX == 0 ? OnSlideListener.SLIDE_STATUS_OFF : OnSlideListener.SLIDE_STATUS_ON);
}
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
}
private void smoothScrollTo(int destX, int destY) {
int scrollX = getScrollX();
int delta = destX - scrollX;
mScroller.startScroll(scrollX, 0, delta, 0, Math.abs(delta) * 2);
invalidate();
}
@Override
public void computeScroll() {//由父视图调用,用于通知子视图在必要时更新 mScrollX 和 mScrollY 的值。 该操作主要用于子视图使用 Scroller 进行动画滚动时
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
public boolean getisHorizontalMove() {
return isHorizontalMove;
}
}
Adapter:
package com.ryg.slideview;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.ryg.slideview.SwipeItemView.OnSlideListener;
public class HistoryListViewAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private List<HistoryListItemObject> mMessageItems;
private Context context;
private SwipeItemView mLastSlideViewWithStatusOn;
public HistoryListViewAdapter(Context context, List<HistoryListItemObject> mMessageItems) {
mInflater = LayoutInflater.from(context);
this.mMessageItems = mMessageItems;
this.context = context;
}
@Override
public int getCount() {
return mMessageItems.size();
}
@Override
public Object getItem(int position) {
return mMessageItems.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@SuppressLint("InflateParams")
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
SwipeItemView slideView = (SwipeItemView) convertView;
if (slideView == null) {
View itemView = mInflater.inflate(R.layout.history_listview_items, null);
slideView = new SwipeItemView(context);
slideView.setContentView(itemView);
holder = new ViewHolder(slideView);
slideView.setOnSlideListener(new OnSlideListener() {
@Override
public void onSlide(View view, int status) {
// TODO Auto-generated method stub
if (mLastSlideViewWithStatusOn != null && mLastSlideViewWithStatusOn != view) {
mLastSlideViewWithStatusOn.shrink();
}
if (status == SLIDE_STATUS_ON) {
mLastSlideViewWithStatusOn = (SwipeItemView) view;
}
}
});
slideView.setTag(holder);
}
else {
holder = (ViewHolder) slideView.getTag();
}
HistoryListItemObject item = mMessageItems.get(position);
if (CustomSwipeListView.mFocusedItemView != null) {
CustomSwipeListView.mFocusedItemView.shrink();
}
holder.icon.setImageResource(item.getIconRes());
holder.title.setText(item.getTitle());
holder.msg.setText(item.getMsg());
holder.deleteHolder.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
mMessageItems.remove(position);
Toast.makeText(context, String.valueOf(position), Toast.LENGTH_SHORT).show();
notifyDataSetChanged();
}
});
return slideView;
}
private static class ViewHolder {
public ImageView icon;
public TextView title;
public TextView msg;
public ViewGroup deleteHolder;
ViewHolder(View view) {
icon = (ImageView) view.findViewById(R.id.icon);
title = (TextView) view.findViewById(R.id.title);
msg = (TextView) view.findViewById(R.id.msg);
deleteHolder = (ViewGroup) view.findViewById(R.id.holder);
}
}
}
布局:
没有删除布局的Item布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:gravity="center_vertical"
android:paddingBottom="5dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp" >
<ImageView
android:id="@+id/icon"
android:layout_width="160dp"
android:layout_height="50dp"
android:layout_marginRight="5dp"
android:scaleType="fitStart"
android:src="@drawable/wechat_icon" />
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/icon"
android:gravity="right"
android:hint="title"
android:textColor="@color/black"
android:textSize="15sp" />
<TextView
android:id="@+id/msg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/icon"
android:layout_alignLeft="@id/title"
android:gravity="right"
android:hint="message"
android:textColor="@color/grey" />
</RelativeLayout>
Merge成最终的Item的布局:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="@+id/view_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
</LinearLayout>
<RelativeLayout
android:id="@+id/holder"
android:layout_width="120dp"
android:layout_height="match_parent"
android:background="#5FBBB9"
android:clickable="true" >
<TextView
android:id="@+id/delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="解绑"
android:textColor="@color/floralwhite" />
</RelativeLayout>
</merge>
最后在Activity里面去用CustomSwipeListView这个自定义View就好了,因为它继承自ListView,所以直接用Adapter绑定数据.