Android實現(xiàn)ListView左右滑動刪除和編輯
有時候,為了實現(xiàn)項目中的需求,完成設(shè)計好的用戶交互體驗,不的不把這些View重新改造成自己想要的效果。
Android原生的ListView是不支持左右滑動的,但是看到微信電話本上,聯(lián)系人可以左右滑動進行操作的,就通過自己的設(shè)想和思路,并加以實現(xiàn)了。
思路:
1.獲取到手指放到屏幕時的x,y位置,并判斷點擊的處于ListView的那個position。
2.判斷滑動的方向,如果是上下方向,touch事件就交給ListView處理;如果是左右方向,就禁止ListView進行滑動。
3.根據(jù)手指的移動,來移動選中的View。
4.滑動結(jié)束后,把View歸位。
5.通過傳入的監(jiān)聽器,告訴用戶是左滑動還是右滑動。
效果圖:


重新的ListView的代碼:
package com.example.wz.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import com.example.wz.R;
import com.example.wz.util.MyLog;
import com.example.wz.util.OpenLooper;
import com.example.wz.util.OpenLooper.LoopCallback;
public class MyListView extends ListView {
public MyLog log = new MyLog(this, true);
public float screenWidth;
public int mTouchSlop;
public float density;
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public float transleteSpeed = 2f;
public OpenLooper openLooper = null;
public LoopCallback loopCallback = null;
@SuppressWarnings("deprecation")
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.screenWidth = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();
this.mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
this.density = context.getResources().getDisplayMetrics().density;
this.openLooper = new OpenLooper();
this.openLooper.createOpenLooper();
this.loopCallback = new ListLoopCallback(this.openLooper);
this.openLooper.loopCallback = this.loopCallback;
}
public class BodyStatus {
public int None = 0, Down = 1, Move = 2, Up = 3, Homing = 4;
int state = None;
}
public BodyStatus bodyStatus = new BodyStatus();
public class RemoveDirection {
public int None = 0, Left = 1, Right = 2, Homing_Left = 3, Homing_Right = 4;
public int state = None;
}
public RemoveDirection removeDirection = new RemoveDirection();
public class ListLoopCallback extends LoopCallback {
public ListLoopCallback(OpenLooper openLooper) {
openLooper.super();
}
@Override
public void loop(double ellapsedMillis) {
if (bodyStatus.state == bodyStatus.Homing) {
goHoming((float) ellapsedMillis);
}
}
}
public void goHoming(float delta) {
float distance = (float) delta * transleteSpeed;
if (removeDirection.state == removeDirection.Left) {
float currentX = itemView.getScrollX() + distance;
if (currentX > screenWidth) {
distance = distance - (currentX - screenWidth);
}
itemView.scrollBy((int) (distance), itemView.getScrollY());
if (itemView.getScrollX() > screenWidth / 2 + 40 * screenWidth / 1080) {
t2.setTranslationX(itemView.getScrollX() - screenWidth / 2 - 40 * 3f);
} else {
t2.setTranslationX(40 * 3f);
}
} else if (removeDirection.state == removeDirection.Right) {
float currentX = itemView.getScrollX() - distance;
if (currentX < -screenWidth) {
distance = distance - (Math.abs(currentX) - screenWidth);
}
itemView.scrollBy((int) (-distance), itemView.getScrollY());
if (itemView.getScrollX() < -(screenWidth / 2 + 40 * 3f * 2)) {
t1.setTranslationX(-(Math.abs(itemView.getScrollX()) - screenWidth / 2 - 40 * 3f));
} else {
t1.setTranslationX(-40 * 3f);
}
} else if (removeDirection.state == removeDirection.Homing_Left) {
float currentX = itemView.getScrollX() - distance;
if (currentX < 0) {
distance = distance - (Math.abs(currentX));
}
itemView.scrollBy((int) (-distance), itemView.getScrollY());
} else if (removeDirection.state == removeDirection.Homing_Right) {
float currentX = itemView.getScrollX() + distance;
if (currentX > 0) {
distance = distance - currentX;
}
itemView.scrollBy((int) (distance), itemView.getScrollY());
}
if (itemView.getScrollX() == 0 || itemView.getScrollX() >= screenWidth || itemView.getScrollX() <= -screenWidth) {
openLooper.stop();
if (itemView.getScrollX() >= screenWidth) {
mRemoveListener.removeItem(removeDirection, position);
} else if (itemView.getScrollX() <= -screenWidth) {
mRemoveListener.removeItem(removeDirection, position);
}
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
itemView.scrollTo(0, itemView.getScrollY());
bodyStatus.state = bodyStatus.None;
}
}, 300);
}
}
public int touch_down_x;
public int touch_down_y;
public View itemView;
public TextView t1;
public TextView t2;
public int SNAP_VELOCITY = 800;
public int position;
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
// addVelocityTracker(event);
if (bodyStatus.state != bodyStatus.None) {
return super.dispatchTouchEvent(event);
}
this.touch_down_x = (int) event.getX();
this.touch_down_y = (int) event.getY();
position = pointToPosition(touch_down_x, touch_down_y);
if (position == AdapterView.INVALID_POSITION) {
return super.dispatchTouchEvent(event);
}
itemView = getChildAt(position - getFirstVisiblePosition());
t2 = (TextView) itemView.findViewById(R.id.t2);
t1 = (TextView) itemView.findViewById(R.id.t1);
} else if (action == MotionEvent.ACTION_MOVE) {
if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY || (Math.abs(event.getX() - touch_down_x) > mTouchSlop && Math.abs(event.getY() - touch_down_y) < mTouchSlop)) {
isSlide = true;
}
} else if (action == MotionEvent.ACTION_UP) {
int velocityX = getScrollVelocity();
if (Math.abs(velocityX) > SNAP_VELOCITY) {
if (velocityX > SNAP_VELOCITY) {
bodyStatus.state = bodyStatus.Homing;
removeDirection.state = removeDirection.Right;
openLooper.start();
} else {
bodyStatus.state = bodyStatus.Homing;
removeDirection.state = removeDirection.Left;
openLooper.start();
}
} else {
if (itemView.getScrollX() >= screenWidth / 2) {
bodyStatus.state = bodyStatus.Homing;
removeDirection.state = removeDirection.Left;
openLooper.start();
} else if (itemView.getScrollX() <= -screenWidth / 2) {
bodyStatus.state = bodyStatus.Homing;
removeDirection.state = removeDirection.Right;
openLooper.start();
} else {
if (itemView.getScrollX() < 0) {
removeDirection.state = removeDirection.Homing_Right;
} else {
removeDirection.state = removeDirection.Homing_Left;
}
bodyStatus.state = bodyStatus.Homing;
openLooper.start();
}
}
recycleVelocityTracker();
isSlide = false;
}
return super.dispatchTouchEvent(event);
}
public boolean isSlide = false;
@SuppressLint("Recycle")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isSlide && position != AdapterView.INVALID_POSITION) {
requestDisallowInterceptTouchEvent(true);
addVelocityTracker(event);
int x = (int) event.getX();
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
} else if (action == MotionEvent.ACTION_MOVE) {
MotionEvent cancelEvent = MotionEvent.obtain(event);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (event.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
onTouchEvent(cancelEvent);
int deltaX = touch_down_x - x;
touch_down_x = x;
if (itemView.getScrollX() > screenWidth / 2 + 40 * 3f * 2) {
t2.setTranslationX(itemView.getScrollX() - screenWidth / 2 - 40 * 3f);
} else {
t2.setTranslationX(40 * 3f);
}
if (itemView.getScrollX() < -(screenWidth / 2 + 40 * 3f * 2)) {
t1.setTranslationX(-(Math.abs(itemView.getScrollX()) - screenWidth / 2 - 40 * 3f));
} else {
t1.setTranslationX(-40 * 3f);
}
itemView.scrollBy(deltaX, 0);
return true;
}
}
return super.onTouchEvent(event);
}
public RemoveListener mRemoveListener;
public void setRemoveListener(RemoveListener removeListener) {
this.mRemoveListener = removeListener;
}
public interface RemoveListener {
public void removeItem(RemoveDirection direction, int position);
}
public VelocityTracker velocityTracker;
public void addVelocityTracker(MotionEvent event) {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
}
velocityTracker.addMovement(event);
}
public int getScrollVelocity() {
if (velocityTracker != null) {
velocityTracker.computeCurrentVelocity(1000);
int velocity = (int) velocityTracker.getXVelocity();
return velocity;
}
return 0;
}
public void recycleVelocityTracker() {
if (velocityTracker != null) {
velocityTracker.recycle();
velocityTracker = null;
}
}
}
測試ListView的Activity代碼:
package com.example.wz;
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.example.wz.view.MyListView;
import com.example.wz.view.MyListView.RemoveDirection;
import com.example.wz.view.MyListView.RemoveListener;
public class TestListActivity extends Activity {
LayoutInflater mInflater;
@Override
protected void onCreate(Bundle savedInstanceState) {
displayMetrics = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testlist);
mInflater = getLayoutInflater();
listView = (MyListView) findViewById(R.id.slideCutListView);
showAddressDialog();
}
public DisplayMetrics displayMetrics;
MyListView listView;
public ArrayList<String> items;
public void showAddressDialog() {
items = new ArrayList<String>() {
{
add("item...1");
add("item...2");
add("item...3");
add("item...4");
add("item...5");
add("item...6");
add("item...7");
add("item...8");
add("item...9");
add("item...10");
add("item...11");
add("item...12");
add("item...13");
add("item...14");
add("item...15");
add("item...16");
add("item...17");
add("item...18");
add("item...19");
add("item...20");
}
};
NearbyRelationAdapter nearbyRelationAdapter = new NearbyRelationAdapter();
listView.setAdapter(nearbyRelationAdapter);
listView.setRemoveListener(new RemoveListener() {
@Override
public void removeItem(RemoveDirection direction, int position) {
if (direction.state == direction.Left) {
Log.e("A", "left" + "-" + position);
} else if (direction.state == direction.Right) {
Log.e("A", "right" + "-" + position);
}
}
});
}
public class NearbyRelationAdapter extends BaseAdapter {
@Override
public int getCount() {
return items.size();
}
@Override
public Object getItem(int posotion) {
return items.get(posotion);
}
@Override
public long getItemId(int posotion) {
return posotion;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder = null;
if (convertView == null) {
holder = new Holder();
convertView = mInflater.inflate(R.layout.view_menu_item1, null);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.o1 = convertView.findViewById(R.id.o1);
holder.o1.setTranslationX(-displayMetrics.widthPixels);
holder.o2 = convertView.findViewById(R.id.o2);
holder.o2.setTranslationX(displayMetrics.widthPixels);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
holder.title.setText(items.get(position));
convertView.scrollTo(0, convertView.getScrollY());
return convertView;
}
class Holder {
TextView title;
View o1, o2;
}
}
}
ListView布局文件:
<?xml version="1.0" encoding="utf-8"?> <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="match_parent" android:background="@android:color/darker_gray" > <com.example.wz.view.MyListView android:id="@+id/slideCutListView" android:layout_width="match_parent" android:layout_height="match_parent" android:cacheColorHint="@android:color/transparent" android:listSelector="@android:color/transparent" > </com.example.wz.view.MyListView> <TextView android:layout_width="1dp" android:layout_height="match_parent" android:layout_centerHorizontal="true" android:background="#0099cd" /> </RelativeLayout>
ListView 的Item布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#fff" > <RelativeLayout android:id="@+id/o1" android:layout_width="match_parent" android:layout_height="60dp" android:background="#ff0000" > <TextView android:id="@+id/t1" android:layout_width="80dp" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:gravity="center" android:padding="10dp" android:text="刪除" android:textColor="#99000000" android:textSize="18sp" /> </RelativeLayout> <RelativeLayout android:id="@+id/o2" android:layout_width="match_parent" android:layout_height="60dp" android:background="#660099cd" > <TextView android:id="@+id/t2" android:layout_width="80dp" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:gravity="center" android:padding="10dp" android:text="編輯" android:textColor="#99000000" android:textSize="18sp" /> </RelativeLayout> <RelativeLayout android:id="@+id/r1" android:layout_width="match_parent" android:layout_height="60dp" android:background="#fff" > <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:gravity="center_vertical" android:textColor="#99000000" android:textSize="18sp" /> </RelativeLayout> </RelativeLayout>
代碼中用到的其他的類,在上一篇文章中有: http://www.dbjr.com.cn/article/83822.htm
轉(zhuǎn)載來自:http://blog.csdn.net/qxs965266509
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助。
- android RecyclerView側(cè)滑菜單,滑動刪除,長按拖拽,下拉刷新上拉加載
- android RecyclerView實現(xiàn)條目Item拖拽排序與滑動刪除
- Android中RecyclerView實現(xiàn)滑動刪除與拖拽功能
- Android ListView實現(xiàn)上拉加載下拉刷新和滑動刪除功能
- Android仿微信列表滑動刪除 如何實現(xiàn)滑動列表SwipeListView
- Android ListView滑動刪除操作(SwipeListView)
- Android使用SwipeListView實現(xiàn)類似QQ的滑動刪除效果
- Android程序開發(fā)之ListView 與PopupWindow實現(xiàn)從左向右滑動刪除功能
- Android RecyclerView滑動刪除和拖動排序
- android自定義View滑動刪除效果
相關(guān)文章
Android PreferenceActivity與PreferenceFragment詳解及簡單實例
這篇文章主要介紹了Android PreferenceActivity與PreferenceFragment詳解及簡單實例的相關(guān)資料,需要的朋友可以參考下2016-12-12
Android audio音頻流數(shù)據(jù)異常問題解決分析
這篇文章主要為大家介紹了Android audio音頻流數(shù)據(jù)異常問題解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-08-08
Flutter持久化存儲之數(shù)據(jù)庫存儲(sqflite)詳解
這篇文章主要給大家介紹了關(guān)于Flutter持久化存儲之數(shù)據(jù)庫存儲的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者使用Flutter具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2019-03-03
Kotlin擴展函數(shù)及實現(xiàn)機制的深入探索
擴展函數(shù)與擴展屬性的神奇之處在于,可以在不修改原來類的條件下,使用函數(shù)和屬性,表現(xiàn)得就像是屬于這個類的一樣。下面這篇文章主要給大家介紹了關(guān)于Kotlin擴展函數(shù)及實現(xiàn)機制的相關(guān)資料,需要的朋友可以參考下2018-06-06
Android StatusBar 透明化方法(不同的版本適配)
本篇文章主要介紹了Android StatusBar 透明化方法(不同的版本適配),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01
Android中實現(xiàn)下載URL地址的網(wǎng)絡(luò)資源的實例分享
這篇文章主要介紹了Android中實現(xiàn)下載URL地址的網(wǎng)絡(luò)資源的實例,其中還有一個進行多線程下載的Java代碼示例,非常典型,需要的朋友可以參考下2016-04-04
Ubuntu 14.04下創(chuàng)建Genymotion安卓虛擬機的步驟詳解
Android 模擬器一直以速度奇慢無比著稱,基本慢到不可用。本文介紹我一直在用的 Genymotion,速度不亞于真機。而且功能齊全,使用簡單。下面這篇文章主要介紹了Ubuntu 14.04下創(chuàng)建Genymotion虛擬機的步驟,需要的朋友可以參考下。2017-03-03

