您的位置:首页 > 其它

开源控件学习之-侧滑listview进行删除(一)(自定义listview)

2016-01-05 14:52 441 查看
最近工作的项目中需要用到listview的侧滑删除。

参考了网上的一些项目做出了更改,这里做出一篇笔记,希望对看到的人有一些帮助。

实现步骤一:定义自定义控件SlideListView

/**
* Created by Anthony on 16/1/4.
*/
public class SlideListView extends ListView {

/**
* 禁止侧滑模式
*/
public static int MOD_FORBID = 0;
/**
* 从左向右滑出菜单模式
*/
public static int MOD_LEFT = 1;
/**
* 从右向左滑出菜单模式
*/
public static int MOD_RIGHT = 2;
/**
* 左右均可以滑出菜单模式
*/
public static int MOD_BOTH = 3;
/**
* 当前的模式
*/
private int mode = MOD_FORBID;
/**
* 左侧菜单的长度
*/
private int leftLength = 0;
/**
* 右侧菜单的长度
*/
private int rightLength = 0;

/**
* 当前滑动的ListView position
*/
private int slidePosition;
/**
* 手指按下X的坐标
*/
private int downY;
/**
* 手指按下Y的坐标
*/
private int downX;
/**
* ListView的item
*/
private View itemView;
/**
* 滑动类
*/
private Scroller scroller;
/**
* 认为是用户滑动的最小距离
*/
private int mTouchSlop;

/**
* 判断是否可以侧向滑动
*/
private boolean canMove = false;
/**
* 标示是否完成侧滑
*/
private boolean isSlided = false;

public SlideListView(Context context) {
this(context, null);
}

public SlideListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public SlideListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
scroller = new Scroller(context);
//getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件,如viewpager就是用这个距离来判断用户是否翻页
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}

/**
* 初始化菜单的滑出模式
*
* @param mode
*/
public void initSlideMode(int mode) {
this.mode = mode;
}

/**
* 处理我们拖动ListView item的逻辑
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {

final int action = ev.getAction();
int lastX = (int) ev.getX();

switch (action) {
case MotionEvent.ACTION_DOWN:
System.out.println("touch-->" + "down");
//当前模式不允许滑动,则直接返回,交给ListView自身去处理
if (this.mode == MOD_FORBID) {
return super.onTouchEvent(ev);
}
// 如果处于侧滑完成状态,侧滑回去,并直接返回
if (isSlided) {
scrollBack();
return false;
}
// 假如scroller滚动还没有结束,我们直接返回
if (!scroller.isFinished()) {
return false;
}
downX = (int) ev.getX();
downY = (int) ev.getY();
//通过当前位置xy值获取当前在第几个item
slidePosition = pointToPosition(downX, downY);

// 无效的position, 不做任何处理
if (slidePosition == AdapterView.INVALID_POSITION) {
return super.onTouchEvent(ev);
}

// 获取我们点击的item view
itemView = getChildAt(slidePosition - getFirstVisiblePosition());

/*此处根据设置的滑动模式,自动获取左侧或右侧菜单的长度*/
if (this.mode == MOD_BOTH) {
this.leftLength = -itemView.getPaddingLeft();
this.rightLength = -itemView.getPaddingRight();
} else if (this.mode == MOD_LEFT) {
this.leftLength = -itemView.getPaddingLeft();
} else if (this.mode == MOD_RIGHT) {
this.rightLength = - itemView.getPaddingRight();
}
break;
case MotionEvent.ACTION_MOVE:
System.out.println("touch-->" + "move");
if (!canMove
&& slidePosition != AdapterView.INVALID_POSITION
&& (Math.abs(ev.getX() - downX) > mTouchSlop && Math.abs(ev
.getY() - downY) < mTouchSlop)) {
int offsetX = downX - lastX;
if (offsetX > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)) {
/*从右向左滑*/
canMove = true;
} else if (offsetX < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)) {
/*从左向右滑*/
canMove = true;
} else {
canMove = false;
}
/*此段代码是为了避免我们在侧向滑动时同时触发ListView的OnItemClickListener事件*/
MotionEvent cancelEvent = MotionEvent.obtain(ev);
cancelEvent
.setAction(MotionEvent.ACTION_CANCEL
| (ev.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
onTouchEvent(cancelEvent);
}
if (canMove) {
//设置此属性,可以在侧向滑动时,保持ListView不会上下滚动
requestDisallowInterceptTouchEvent(true);
// 手指拖动itemView滚动, deltaX大于0向左滚动,小于0向右滚
int deltaX = downX - lastX;
if (deltaX < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)) {
/*向左滑*/
itemView.scrollTo(deltaX, 0);
} else if (deltaX > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)) {
/*向右滑*/
itemView.scrollTo(deltaX, 0);
} else {
itemView.scrollTo(0, 0);
}
return true; // 拖动的时候ListView不滚动
}
case MotionEvent.ACTION_UP:
System.out.println("touch-->" + "up");
if (canMove) {
canMove = false;
scrollByDistanceX();
}
break;
}
// 否则直接交给ListView来处理onTouchEvent事件
return super.onTouchEvent(ev);
}

/**
* 根据手指滚动itemView的距离来判断是滚动到开始位置还是向左或者向右滚动
*/
private void scrollByDistanceX() {
/*当前模式不允许滑动,则直接返回*/
if (this.mode == MOD_FORBID) {
return;
}
if (itemView.getScrollX() > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)) {
/*从右向左滑*/
if (itemView.getScrollX() >= rightLength / 2) {
scrollLeft();
} else {
// 滚回到原始位置
scrollBack();
}
} else if (itemView.getScrollX() < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)) {
/*从左向右滑*/
if (itemView.getScrollX() <= -leftLength / 2) {
scrollRight();
} else {
// 滚回到原始位置
scrollBack();
}
} else {
// 滚回到原始位置
scrollBack();
}

}

/**
* 往右滑动,getScrollX()返回的是左边缘的距离,就是以View左边缘为原点到开始滑动的距离,所以向右边滑动为负值
*/
private void scrollRight() {
isSlided = true;
final int delta = (leftLength + itemView.getScrollX());
// 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
scroller.startScroll(itemView.getScrollX(), 0, -delta, 0,
Math.abs(delta));
postInvalidate(); // 刷新itemView
}

/**
* 向左滑动,根据上面我们知道向左滑动为正值
*/
private void scrollLeft() {
isSlided = true;
final int delta = (rightLength - itemView.getScrollX());
// 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
scroller.startScroll(itemView.getScrollX(), 0, delta, 0,
Math.abs(delta));
postInvalidate(); // 刷新itemView
}

/**
* 滑动会原来的位置
*/
private void scrollBack() {
isSlided = false;
scroller.startScroll(itemView.getScrollX(), 0, -itemView.getScrollX(),
0, Math.abs(itemView.getScrollX()));
postInvalidate(); // 刷新itemView
}

/**
* 通常是用mScroller记录/计算View滚动的位置,
* 再重写View的computeScroll(),完成实际的滚动。
*/
@Override
public void computeScroll() {
// 调用startScroll的时候scroller.computeScrollOffset()返回true,
if (scroller.computeScrollOffset()) {
// 让ListView item根据当前的滚动偏移量进行滚动
itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY());
postInvalidate();
}
}

/**
* 提供给外部调用,用以将侧滑出来的滑回去
*/
public void slideBack() {
this.scrollBack();
}

}
实现步骤二:整体activity的布局如下,也就是一个listview:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<com.test.view.SlideListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#dddbdb"
android:dividerHeight="1.0px"
android:drawSelectorOnTop="true"
android:listSelector="@drawable/list_item_selector"
android:scrollbars="none" />

</RelativeLayout>


实现步骤三:listItem的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="@color/bg_channel"
android:paddingRight="-90dp"
android:orientation="horizontal"
android:layout_height="@dimen/size100">
<RelativeLayout android:id="@+id/layout_content"
android:layout_height="@dimen/size100"
android:layout_centerVertical="true"
android:layout_width="fill_parent">

<TextView android:id="@+id/txt_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/size10"
android:ellipsize="end"
android:lines="1"
android:textColor="@color/vtibet_black"
android:textSize="16sp" />

</RelativeLayout>
<LinearLayout
android:id="@+id/layout_right"
android:layout_width="90dp"
android:layout_height="match_parent"
android:orientation="vertical"
>
<RelativeLayout
android:id="@+id/delete_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/tomato"
android:clickable="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="删除"
android:textColor="@color/white"
android:textSize="15sp"/>

</RelativeLayout>

</LinearLayout>

</LinearLayout>
对于listItem,我们主要通过的是PaddingRight来实现的把我们的id为layout_right的控件隐藏。这也是下拉刷新的实现原理之一,通过padding设置为负数来隐藏我们的一些控件。然后需要将我们的item上面的delete的布局的clickable置为true,不然你会发现无法点击item上面的删除键。

实现步骤四:在我们的activity中进行调用:

list_view = (SlideListView) view.findViewById(R.id.list_view);
list_view.initSlideMode(SlideListView.MOD_RIGHT);
list_view.setOnItemClickListener(this);
list_view.setOnItemLongClickListener(this);
......在自己的adapter的getView中:
<pre name="code" class="html">            final RelativeLayout delete_item = (RelativeLayout) convertView.findViewById(R.id.delete_item);

delete_item.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
delete(item.getId(), position);//删除数据
}
});
......


在步骤四中需要注意的是,我们需要initSlideMode,不然我在自定义的控件中会抛出空指针错误。

此时,有的人可能会在listview的adapter中添加我们item的点击事件,这样我们就不可以进行滑动了。这里牵涉到事件机制。

推荐关注的文章:
http://blog.csdn.net/liyuanjinglyj/article/details/48550489 http://blog.csdn.net/jiangwei0910410003/article/details/17504315 http://blog.csdn.net/guolin_blog/article/details/9097463/ http://blog.csdn.net/xiaanming/article/details/21696315注意事项:1 左、右侧滑出菜单的ListView(划出后选择进行操作,本项目主要是进行删除);2 使用请注意与ListView的Item的布局配合;3 该效果的实现是基于在Item的布局中通过设置PaddingLeft和PaddingRight来隐藏左右菜单的, 所以使用此ListView时,请务必在布局Item时使用PaddingLeft和PaddingRight。

好了。当做对这个问题处理的笔记,也希望对看到的人真心有帮助。

<pre style="background-color:#2b2b2b;color:#a9b7c6;font-family:'宋体';font-size:12.0pt;">



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