今天要总结的是listview的左滑显示删除按钮,点击删除。这个方法是我上个项目所做的时候应用的。主要的思路就是写两个xml文件,然后动态地添加到父布局中,当然需要在重写listview,判断是左右滑动还是上下滑动,在onTouch方法中判断是否消费本次传递事件,并在合并的View中写了Swipe方法,判断滑出的部分是否超过多余部分的二分之一进行滑入和滑出的操作,用于接收listview传递过来的事件,进行多余部分的显示与否。总体思路比较清晰,但是由于本人刚刚入门。。。还是写地略难过。。。
主要核心代码SwipeItemLayout.java
package com.example.testdemo;
import android.support.v4.widget.ScrollerCompat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.FrameLayout;
public class SwipeItemLayout extends FrameLayout {
// listview的item
private View contentView = null;
// 这个是左滑后多出的部分
private View menuView = null;
// 这个是动画的速度控制器
private Interpolator closeInterpolator = null;
private Interpolator openInterpolator = null;
// 控制控件滑动的,会平滑滑动,一个开一个关
private ScrollerCompat mOpenScroller;
private ScrollerCompat mCloseScroller;
// 手指点击的初始位置
private int mDownX;
private int state = STATE_CLOSE;
// 当前item的状态,open和close两种
private static final int STATE_CLOSE = 0;
private static final int STATE_OPEN = 1;
// 构造函数
public SwipeItemLayout(View contentView, View menuView,
Interpolator closeInterpolator, Interpolator openInterpolator) {
super(contentView.getContext());
this.contentView = contentView;
this.menuView = menuView;
this.closeInterpolator = closeInterpolator;
this.openInterpolator = openInterpolator;
init();// 初始化视图
}
private void init() {
setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
// 构造动画加速器
if (closeInterpolator != null) {
mCloseScroller = ScrollerCompat.create(getContext(),
closeInterpolator);
} else {
mCloseScroller = ScrollerCompat.create(getContext());
}
if (openInterpolator != null) {
mOpenScroller = ScrollerCompat.create(getContext(),
openInterpolator);
} else {
mOpenScroller = ScrollerCompat.create(getContext());
}
// 这里是设置各自的宽和高
contentView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
menuView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
// 将这两个布局都add到这个view中
addView(contentView);
addView(menuView);
}
// 预留接口,在listview中的ontouch事件传递到这里来处理
public boolean onSwipe(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 获取手指点击的初始位置
mDownX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
// 手指移动的距离
int dis = (int) (mDownX - event.getX());
if (state == STATE_OPEN) {
dis += menuView.getWidth();// 手指移动距离+多出来部分的宽度
}
swipe(dis);
break;
// 这里其实是一个判断,当用户滑了menuView的一半的时候,自动滑出来,否则滑进去
case MotionEvent.ACTION_UP:
if ((mDownX - event.getX()) > (menuView.getWidth() / 2)) {
// 平滑的滑出
smoothOpenMenu();
} else {
// 平滑的滑进
smoothCloseMenu();
return true;// 把这次的touch事件给lisview继续处理
}
break;
}
return true;// 默认是消费这次操作
}
// 判断是否滑出的状态
public boolean isOpen() {
return state == STATE_OPEN;
}
// 执行滑动操作,dis参数是滑动的距离
private void swipe(int dis) {
if (dis > menuView.getWidth()) {
dis = menuView.getWidth();
} else if (dis < 0) {
dis = 0;
}
// layout的四个参数分别是(l,t,r,b),类似于绝对布局。相对于绝对布局子view的位置
contentView.layout(-dis, contentView.getTop(), contentView.getWidth()
- dis, getMeasuredHeight());
menuView.layout(contentView.getWidth() - dis, menuView.getTop(),
contentView.getWidth() + menuView.getWidth() - dis,
menuView.getBottom());
}
// computeScroll在父控件执行drawChild时,会调用这个方法。做刷新操作
@Override
public void computeScroll() {
if (state == STATE_OPEN) {
if (mOpenScroller.computeScrollOffset()) {
swipe(mOpenScroller.getCurrX());
postInvalidate();
}
} else {
if (mCloseScroller.computeScrollOffset()) {
swipe(- mCloseScroller.getCurrX());
postInvalidate();
}
}
}
// 关闭隐藏图标
public void smoothCloseMenu() {
state = STATE_CLOSE;
//contentView的左边相对于父控件的距离contentView.getLeft()
mCloseScroller.startScroll(0, 0, -contentView.getLeft(), 0, 350);
postInvalidate();
}
// 打开隐藏的图标
public void smoothOpenMenu() {
//这里的-contentView.getLeft()与menuView.getWidth()相等
state = STATE_OPEN;
mOpenScroller.startScroll(-contentView.getLeft(), 0,
menuView.getWidth(), 0, 350);
Log.e("1111111111", contentView.getLeft()+"uuuuuu"+menuView.getWidth());
postInvalidate();
}
public void closeMenu() {
if (mCloseScroller.computeScrollOffset()) {
mCloseScroller.abortAnimation();
}
if (state == STATE_OPEN) {
state = STATE_CLOSE;
swipe(0);
}
}
public void openMenu() {
if (state == STATE_CLOSE) {
state = STATE_OPEN;
swipe(menuView.getWidth());
}
}
public View getContentView() {
return contentView;
}
public View getMenuView() {
return menuView;
}
// 这个方法 其实就是获取menuView的宽和高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
menuView.measure(MeasureSpec
.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec
.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
}
// 这个方法就把控件的相对布局显示出来了
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
contentView.layout(0, 0, getMeasuredWidth(),
contentView.getMeasuredHeight());
menuView.layout(getMeasuredWidth(), 0,
getMeasuredWidth() + menuView.getMeasuredWidth(),
contentView.getMeasuredHeight());
}
}
自定义的SwipeListView
package com.example.testdemo;
import android.support.v4.widget.ScrollerCompat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.FrameLayout;
public class SwipeItemLayout extends FrameLayout {
// listview的item
private View contentView = null;
// 这个是左滑后多出的部分
private View menuView = null;
// 这个是动画的速度控制器
private Interpolator closeInterpolator = null;
private Interpolator openInterpolator = null;
// 控制控件滑动的,会平滑滑动,一个开一个关
private ScrollerCompat mOpenScroller;
private ScrollerCompat mCloseScroller;
// 手指点击的初始位置
private int mDownX;
private int state = STATE_CLOSE;
// 当前item的状态,open和close两种
private static final int STATE_CLOSE = 0;
private static final int STATE_OPEN = 1;
// 构造函数
public SwipeItemLayout(View contentView, View menuView,
Interpolator closeInterpolator, Interpolator openInterpolator) {
super(contentView.getContext());
this.contentView = contentView;
this.menuView = menuView;
this.closeInterpolator = closeInterpolator;
this.openInterpolator = openInterpolator;
init();// 初始化视图
}
private void init() {
setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
// 构造动画加速器
if (closeInterpolator != null) {
mCloseScroller = ScrollerCompat.create(getContext(),
closeInterpolator);
} else {
mCloseScroller = ScrollerCompat.create(getContext());
}
if (openInterpolator != null) {
mOpenScroller = ScrollerCompat.create(getContext(),
openInterpolator);
} else {
mOpenScroller = ScrollerCompat.create(getContext());
}
// 这里是设置各自的宽和高
contentView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
menuView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
// 将这两个布局都add到这个view中
addView(contentView);
addView(menuView);
}
// 预留接口,在listview中的ontouch事件传递到这里来处理
public boolean onSwipe(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 获取手指点击的初始位置
mDownX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
// 手指移动的距离
int dis = (int) (mDownX - event.getX());
if (state == STATE_OPEN) {
dis += menuView.getWidth();// 手指移动距离+多出来部分的宽度
}
swipe(dis);
break;
// 这里其实是一个判断,当用户滑了menuView的一半的时候,自动滑出来,否则滑进去
case MotionEvent.ACTION_UP:
if ((mDownX - event.getX()) > (menuView.getWidth() / 2)) {
// 平滑的滑出
smoothOpenMenu();
} else {
// 平滑的滑进
smoothCloseMenu();
return true;// 把这次的touch事件给lisview继续处理
}
break;
}
return true;// 默认是消费这次操作
}
// 判断是否滑出的状态
public boolean isOpen() {
return state == STATE_OPEN;
}
// 执行滑动操作,dis参数是滑动的距离
private void swipe(int dis) {
if (dis > menuView.getWidth()) {
dis = menuView.getWidth();
} else if (dis < 0) {
dis = 0;
}
// layout的四个参数分别是(l,t,r,b),类似于绝对布局。相对于绝对布局子view的位置
contentView.layout(-dis, contentView.getTop(), contentView.getWidth()
- dis, getMeasuredHeight());
menuView.layout(contentView.getWidth() - dis, menuView.getTop(),
contentView.getWidth() + menuView.getWidth() - dis,
menuView.getBottom());
}
// computeScroll在父控件执行drawChild时,会调用这个方法。做刷新操作
@Override
public void computeScroll() {
if (state == STATE_OPEN) {
if (mOpenScroller.computeScrollOffset()) {
swipe(mOpenScroller.getCurrX());
postInvalidate();
}
} else {
if (mCloseScroller.computeScrollOffset()) {
swipe(- mCloseScroller.getCurrX());
postInvalidate();
}
}
}
// 关闭隐藏图标
public void smoothCloseMenu() {
state = STATE_CLOSE;
//contentView的左边相对于父控件的距离contentView.getLeft()
mCloseScroller.startScroll(0, 0, -contentView.getLeft(), 0, 350);
postInvalidate();
}
// 打开隐藏的图标
public void smoothOpenMenu() {
//这里的-contentView.getLeft()与menuView.getWidth()相等
state = STATE_OPEN;
mOpenScroller.startScroll(-contentView.getLeft(), 0,
menuView.getWidth(), 0, 350);
Log.e("1111111111", contentView.getLeft()+"uuuuuu"+menuView.getWidth());
postInvalidate();
}
public void closeMenu() {
if (mCloseScroller.computeScrollOffset()) {
mCloseScroller.abortAnimation();
}
if (state == STATE_OPEN) {
state = STATE_CLOSE;
swipe(0);
}
}
public void openMenu() {
if (state == STATE_CLOSE) {
state = STATE_OPEN;
swipe(menuView.getWidth());
}
}
public View getContentView() {
return contentView;
}
public View getMenuView() {
return menuView;
}
// 这个方法 其实就是获取menuView的宽和高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
menuView.measure(MeasureSpec
.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec
.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
}
// 这个方法就把控件的相对布局显示出来了
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
contentView.layout(0, 0, getMeasuredWidth(),
contentView.getMeasuredHeight());
menuView.layout(getMeasuredWidth(), 0,
getMeasuredWidth() + menuView.getMeasuredWidth(),
contentView.getMeasuredHeight());
}
}
SwipeAdapterMessage中的代码
package com.example.testdemo;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
public class SwipeAdapterMessage extends BaseAdapter {
private Context mContext = null;
private List<UserMessage> mUserMessage = null;
private int mResource;
private int mMerge;
public SwipeAdapterMessage(Context context, List<UserMessage> data,
int layoutId, int mergeId) {
this.mContext = context;
this.mUserMessage = data;
this.mResource = layoutId;
this.mMerge = mergeId;
}
public void setData(List<UserMessage> mUserMessageItems) {
mUserMessage = mUserMessageItems;
}
@Override
public int getCount() {
return mUserMessage.size();
}
@Override
public UserMessage getItem(int position) {
return mUserMessage.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder viewholder = null;
if (convertView == null) {
viewholder = new ViewHolder();
// 主视图item
View viewMain = LayoutInflater.from(mContext).inflate(mResource,
null);
// 滑动出来的删除视图
View viewDel = LayoutInflater.from(mContext).inflate(mMerge, null);
viewholder.btnDel = (TextView) viewDel.findViewById(R.id.delete);
viewholder.childId = (TextView) viewMain
.findViewById(R.id.messageid);
viewholder.txtContent = (TextView) viewMain
.findViewById(R.id.message_content);
viewholder.txtDate = (TextView) viewMain
.findViewById(R.id.message_date);
convertView = new SwipeItemLayout(viewMain, viewDel,null , null);
convertView.setTag(viewholder);
} else {
viewholder = (ViewHolder) convertView.getTag();
}
UserMessage Item = getItem(position);
viewholder.childId.setText(String.valueOf(Item.getId()));
viewholder.txtContent.setText(Item.getMessageContent());
viewholder.txtDate.setText(Item.getMyDate());
viewholder.btnDel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, "删除成功!", Toast.LENGTH_SHORT).show();
mUserMessage.remove(position); // 删除视图上对应的位置item
notifyDataSetChanged(); // 通知数据改变了,会重新刷新适配器中的数据
}
});
return convertView;
}
// view容器
public class ViewHolder {
TextView btnDel = null;
TextView txtContent = null;
TextView txtDate = null;
TextView childId = null;
}
}
最后附上本项目的下载地址:http://download.csdn.net/detail/z_zt_t/9556266
总结:我觉得首先要完成这个功能需要对事件的分发机制有着长足的了解,并不断地去理解实验然后跳坑出坑。。。我觉得写博客不能因为已经有大牛写了精美绝伦的博客就直接转载,哪怕写的支零破碎也要留下自己的一知半解,不为大家能看到,哪怕只作为自己的一个笔记片段,也是足够的了。这也是写博客的尴尬,也是转载的不深刻不理解留下的诟病。。。不将就