爲ListView添加炫酷的Item中帶側滑的刪除,原理是利用item佈局中的padding(Left和Right)屬性爲負值,來把刪除的按鈕隱藏在屏幕外。然後通過自定義ListView重寫其中的OnTouchEvent通過手指座標點的計算來處理事件,實現itemView的滾動,達到滑動出現刪除菜單,本例中僅實現右向左滑出現刪除按鈕,大家可以根據自己需求,參照自定義ListView中事件處理做出左側滑,右側滑出現菜單,當然不僅僅侷限於刪除。更多靈活用法期待發覺。
首先上自定義的ListSlideView代碼,其中有有詳細註釋,我就不廢話了!(該ListView參考以前項目裏面的事件分發處理判斷,具體參考了哪位大神的寫法已無法考究,感謝最原先的作者的同時也請見諒,如見可聯繫博主,把參考出處加上)
ListSlideView:
/**
* ListSlideView.java
* 2015 下午3:00:24
*/
package com.example.slideviewtest;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Scroller;
/**
* @note 自定義的listView,集成自系統的ListView,對list的OnTouchEnvent事件分發進行了攔截處理(最關鍵的地方,
* 也是通過該方式實現了控制屏幕外佈局的拖拽) 需要配合Item的佈局文件來實現
* @author blank
* @time 下午3:00:24
* @version V1.0
*/
public class ListSlideView extends ListView {
/** 禁止側滑模式 */
public static int MODE_FORBID = 0;
/** 從右向左滑出菜單模式 */
public static int MODE_RIGHT = 1;
/** 當前的模式 */
private int mode = MODE_FORBID;
/** 右側菜單的長度 */
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 ListSlideView(Context context) {
this(context, null);
}
public ListSlideView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SlideMode);
mode = a.getInt(R.styleable.SlideMode_mode, 0);
}
public ListSlideView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SlideMode);
mode = a.getInt(R.styleable.SlideMode_mode, 0);
scroller = new Scroller(context);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
/**
* 處理我們拖動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");
/* 默認不處理當前View的事件,即沒有側滑菜單 */
if (this.mode == MODE_FORBID) {
return super.onTouchEvent(ev);
}
// 側滑狀態判斷
if (isSlided) {
scrollBack();
return false;
}
// 滾動是否結束
if (!scroller.isFinished()) {
return false;
}
downX = (int) ev.getX();
downY = (int) ev.getY();
slidePosition = pointToPosition(downX, downY);
// 無效的position
if (slidePosition == AdapterView.INVALID_POSITION) {
return super.onTouchEvent(ev);
}
itemView = getChildAt(slidePosition - getFirstVisiblePosition());
/* 右側菜單的長度 */
if (this.mode == MODE_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 == MODE_RIGHT) {
/* 從右向左滑 */
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);
// 根據X座標的差可以得到手指滑動方向,本例子可以根據自己的需要去靈活修改(左邊劃出菜單,右邊劃出菜單,或者左右均可)
int deltaX = downX - lastX;
if (deltaX > 0 && this.mode == MODE_RIGHT) {
/* X座標差大於0手指向右滑動 */
itemView.scrollTo(deltaX, 0);
} else {
itemView.scrollTo(0, 0);
}
return true;
}
case MotionEvent.ACTION_UP:
System.out.println("touch-->" + "up");
if (canMove) {
canMove = false;
scrollByDistanceX();
}
break;
}
return super.onTouchEvent(ev);
}
/**
* 根據手指滾動itemView的距離來判斷是滾動到開始位置還是向左或者向右滾動
*/
private void scrollByDistanceX() {
/* 當前模式不允許滑動,則直接返回 */
if (this.mode == MODE_FORBID) {
return;
}
if (itemView.getScrollX() > 0 && this.mode == MODE_RIGHT) {
/* 從右向左滑 */
if (itemView.getScrollX() >= rightLength / 2) {
scrollLeft();
} else {
// 滾回原始位置
scrollBack();
}
} else {
// 滾回原始位置
scrollBack();
}
}
/**
* 向左滑動
*/
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
}
@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();
}
}
爲了兼容以前的ListView添加了是否需要側滑的自定義控件屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SlideMode">
<attr name="mode">
<enum name="forbid" value="0"></enum>
<enum name="right" value="1"></enum>
</attr>
</declare-styleable>
</resources>
0代表不支持側滑,1代表支持右側滑,可以根據自己需要去加上左側滑
ItemView的佈局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingBottom="10dp"
android:paddingRight="-90dp"
android:paddingTop="10dp"
android:weightSum="1" >
<LinearLayout
android:layout_width="0dp"
android:layout_height="96dp"
android:layout_weight="0.35"
android:orientation="vertical" >
<ImageView
android:id="@+id/imgIcon"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="fitXY"
android:src="@drawable/images" />
<TextView
android:id="@+id/tvPrice"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="$128.5" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="96dp"
android:layout_weight="0.65"
android:orientation="vertical"
android:padding="5dp" >
<TextView
android:id="@+id/tvName"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="HP 14-P010NR 14 TOUCHSCREEN SLATEBOOK, NIVIDIA TE"
android:textStyle="bold" />
<RatingBar
android:id="@+id/ratingBar"
style="?android:attr/ratingBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="5" />
</LinearLayout>
<TextView
android:id="@+id/tvDelete"
android:layout_width="90dp"
android:layout_height="96dp"
android:layout_centerInParent="true"
android:layout_marginRight="-5dp"
android:background="@android:color/holo_red_light"
android:gravity="center"
android:text="Delete"
android:textColor="@android:color/white"
android:textSize="15sp" />
</LinearLayout>
爲了適配ListSlideView的適配器:
package com.example.slideviewtest;
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.TextView;
/**
* @author blank
* @time 下午4:01:07
* @version V1.0
*/
public class SlideViewAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater layoutInflr;
private int itemtype = 1;
private OnRemoveListener mRemoveListener;
public int getItemtype() {
return itemtype;
}
public void setItemtype(int itemtype) {
this.itemtype = itemtype;
}
public SlideViewAdapter( Context context) {
this.mContext = context;
this.layoutInflr = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setRemoveListener(OnRemoveListener removeListener) {
this.mRemoveListener = removeListener;
}
class Viewlayout {
private TextView mDelete;// 隱藏的側滑刪除按鈕
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return 10;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
Viewlayout icom_view;
if (convertView == null) {
icom_view = new Viewlayout();
convertView = layoutInflr.inflate(R.layout.item_product_listview,
null);
icom_view.mDelete = (TextView) convertView
.findViewById(R.id.tvDelete);
convertView.setTag(icom_view);
} else {
icom_view = (Viewlayout) convertView.getTag();
}
icom_view.mDelete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(mRemoveListener!=null){mRemoveListener.onRemoveItem(position);}
}
});
return convertView;
}
public interface OnRemoveListener {
void onRemoveItem(int position);
}
}
demo沒有做數據填充,把刪除的事件回調出去了!
來2張效果圖:
源碼地址已修改爲正確地址
源碼下載地址:源碼下載