android实现QQ微信侧滑删除效果

作者:mr丶yang 时间:2021-07-11 15:58:11 

最近由于项目需求,需要做一个listview中的item策划删除的效果,与是查找资料和参考了一些相关的博客,终于完美实现了策划删除的效果。

先看一下效果图(研究了半天竟然没研究出来真机上gif图怎么做,大家将就看一下吧)。

android实现QQ微信侧滑删除效果     

侧滑效果图

android实现QQ微信侧滑删除效果

点击删除后的截图

android实现QQ微信侧滑删除效果

点击删除后,listview中的第一个“微信团队”被删除。

接下来看代码部分,很多注释都在代码中,直接上代码。

首先自定义个SlideView继承LinearLayout。


import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.TextView;

/**  
* SlideView 继承自LinearLayout  
*/  
public class SlideView extends LinearLayout {  

private static final String TAG = "SlideView";  

private Context mContext;  

// 用来放置所有view的容器  
 private LinearLayout mViewContent;  

// 用来放置内置view的容器,比如删除 按钮  
 private RelativeLayout mHolder;  

// 弹性滑动对象,提供弹性滑动效果  
 private Scroller mScroller;  

// 滑动回调接口,用来向上层通知滑动事件  
 private OnSlideListener mOnSlideListener;  

// 内置容器的宽度 单位:dp  
 private int mHolderWidth = 120;  

// 分别记录上次滑动的坐标  
 private int mLastX = 0;  
 private int mLastY = 0;  

// 用来控制滑动角度,仅当角度a满足如下条件才进行滑动:tan a = deltaX / deltaY > 2  
 private static final int TAN = 2;  

public interface OnSlideListener {  
   // SlideView的三种状态:开始滑动,打开,关闭  
   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;  

public void onSlide(View view, int status);  
 }  

public SlideView(Context context) {  
   super(context);  
   initView();  
 }  

public SlideView(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);  
   mHolderWidth = Math.round(TypedValue.applyDimension(  
       TypedValue.COMPLEX_UNIT_DIP, mHolderWidth, getResources()  
           .getDisplayMetrics()));  
 }  

// 设置按钮的内容,也可以设置图标啥的,我没写  
 public void setButtonText(CharSequence text) {  
   ((TextView) findViewById(R.id.delete)).setText(text);  
 }  

// 将view加入到ViewContent中  
 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);  
   }  
 }  

// 根据MotionEvent来进行滑动,这个方法的作用相当于onTouchEvent  
 // 如果你不需要处理滑动冲突,可以直接重命名,照样能正常工作  
 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: {  
     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) {  
       // 滑动不满足条件,不做横向滑动  
       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.75 > 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;  
   // 以三倍时长滑向destX,效果就是慢慢滑动  
   mScroller.startScroll(scrollX, 0, delta, 0, Math.abs(delta) * 3);  
   invalidate();  
 }  

@Override  
 public void computeScroll() {  
   if (mScroller.computeScrollOffset()) {  
     scrollTo(mScroller.getCurrX(), mScroller.getCurrY());  
     postInvalidate();  
   }  
 }  

}

自定义SlideView所关联的xml.(主要就是布局侧滑显出来的删除按钮)


<?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:clickable="true"
   android:background="@drawable/holder_bg">

<TextView
     android:id="@+id/delete"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:drawableLeft="@drawable/del_icon_normal"
     android:layout_centerInParent="true"
     android:gravity="center"
     android:textColor="@color/floralwhite"
     android:text="删除" />
 </RelativeLayout>

</merge>

自定义ListViewCompat继承Listview。


import com.ryg.slideview.MainActivity.MessageItem;

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 ListViewCompat extends ListView {

private static final String TAG = "ListViewCompat";

private SlideView mFocusedItemView;

public ListViewCompat(Context context) {
   super(context);
 }

public ListViewCompat(Context context, AttributeSet attrs) {
   super(context, attrs);
 }

public ListViewCompat(Context context, AttributeSet attrs, int defStyle) {
   super(context, attrs, defStyle);
 }

public void shrinkListItem(int position) {
   View item = getChildAt(position);

if (item != null) {
     try {
       ((SlideView) item).shrink();
     } catch (ClassCastException e) {
       e.printStackTrace();
     }
   }
 }

@Override  
 public boolean onTouchEvent(MotionEvent event) {  
   switch (event.getAction()) {  
   case MotionEvent.ACTION_DOWN: {  
     int x = (int) event.getX();  
     int y = (int) event.getY();  
     //根据坐标获取item所在的行  
     int position = pointToPosition(x, y);  
     Log.e(TAG, "postion=" + position);  
     if (position != INVALID_POSITION) {  
       //得到当前点击行的数据从而取出当前行的item。  
       //可能有人怀疑,为什么要这么干?为什么不用getChildAt(position)?  
       //因为ListView会进行缓存,如果你不这么干,有些行的view你是得不到的。  
       MessageItem data = (MessageItem) getItemAtPosition(position);  
       mFocusedItemView = data.slideView;  
       Log.e(TAG, "FocusedItemView=" + mFocusedItemView);  
     }  
   }  
   default:  
     break;  
   }  

//向当前点击的view发送滑动事件请求,其实就是向SlideView发请求  
   if (mFocusedItemView != null) {  
     mFocusedItemView.onRequireTouchEvent(event);  
   }  

return super.onTouchEvent(event);  
 }  

}

接下来就是主界面的activity了


import java.util.ArrayList;
import java.util.List;

import com.ryg.slideview.SlideView.OnSlideListener;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity implements OnItemClickListener,  
OnClickListener, OnSlideListener {  

private static final String TAG = "MainActivity";  

private ListViewCompat mListView;  

private List<MessageItem> mMessageItems = new ArrayList<MainActivity.MessageItem>();  

private SlideAdapter mSlideAdapter;  

// 上次处于打开状态的SlideView  
private SlideView mLastSlideViewWithStatusOn;  

@Override  
protected void onCreate(Bundle savedInstanceState) {  
super.onCreate(savedInstanceState);  
setContentView(R.layout.activity_main);  
initView();  
}  

private void initView() {  
mListView = (ListViewCompat) findViewById(R.id.list);  

for (int i = 0; i < 20; i++) {  
 MessageItem item = new MessageItem();  
 if (i % 2 == 0) {  
   item.iconRes = R.drawable.default_qq_avatar;  
   item.title = "腾讯新闻";  
   item.msg = "天津大 * :河北大 * ";  
   item.time = "晚上18:00";  
 } else {  
   item.iconRes = R.drawable.wechat_icon;  
   item.title = "微信团队";  
   item.msg = "欢迎你使用微信";  
   item.time = "10月01日";  
 }  
 mMessageItems.add(item);  
}  
mSlideAdapter = new SlideAdapter();  
mListView.setAdapter(mSlideAdapter);  
mListView.setOnItemClickListener(this);  
}  

//listview 的适配器
private class SlideAdapter extends BaseAdapter {  

private LayoutInflater mInflater;  

SlideAdapter() {  
 super();  
 mInflater = getLayoutInflater();  
}  

@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;  
}  

@Override  
public View getView(int position, View convertView, ViewGroup parent) {  
 ViewHolder holder;  
 SlideView slideView = (SlideView) convertView;  
 if (slideView == null) {  
   // 这里是我们的item  
   View itemView = mInflater.inflate(R.layout.list_item, null);  

slideView = new SlideView(MainActivity.this);  
   // 这里把item加入到slideView  
   slideView.setContentView(itemView);  
   // 下面是做一些数据缓存  
   holder = new ViewHolder(slideView);  
   slideView.setOnSlideListener(MainActivity.this);  
   slideView.setTag(holder);  
 } else {  
   holder = (ViewHolder) slideView.getTag();  
 }  
 MessageItem item = mMessageItems.get(position);  
 item.slideView = slideView;  
 item.slideView.shrink();  

holder.icon.setImageResource(item.iconRes);  
 holder.title.setText(item.title);  
 holder.msg.setText(item.msg);  
 holder.time.setText(item.time);  
 holder.deleteHolder.setOnClickListener(MainActivity.this);  

return slideView;  
}  

}  

public class MessageItem {  
public int iconRes;  
public String title;  
public String msg;  
public String time;  
public SlideView slideView;  
}  

private static class ViewHolder {  
public ImageView icon;  
public TextView title;  
public TextView msg;  
public TextView time;  
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);  
 time = (TextView) view.findViewById(R.id.time);  
 deleteHolder = (ViewGroup) view.findViewById(R.id.holder);  
}  
}  

@Override  
public void onItemClick(AdapterView<?> parent, View view, int position,  
 long id) {  
// 这里处理ListItem的点击事件  
Log.e(TAG, "onItemClick position=" + position);  
}  

@Override  
public void onSlide(View view, int status) {  
// 如果当前存在已经打开的SlideView,那么将其关闭  
if (mLastSlideViewWithStatusOn != null  
   && mLastSlideViewWithStatusOn != view) {  
 mLastSlideViewWithStatusOn.shrink();  
}  
// 记录本次处于打开状态的view  
if (status == SLIDE_STATUS_ON) {  
 mLastSlideViewWithStatusOn = (SlideView) view;  
}  
}  

@Override  
public void onClick(View v) {  
// 这里处理删除按钮的点击事件,可以删除对话  
if (v.getId() == R.id.holder) {  
 int position = mListView.getPositionForView(v);  
 if (position != ListView.INVALID_POSITION) {  
   mMessageItems.remove(position);  
   mSlideAdapter.notifyDataSetChanged();  
 }  
 Log.e(TAG, "onClick v=" + v);  
}  
}

主界面中activity用到的xml


<LinearLayout 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" >

<com.ryg.slideview.ListViewCompat
   android:id="@+id/list"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="#fff4f7f9"
   android:cacheColorHint="#00000000"
   android:divider="#dddbdb"
   android:dividerHeight="1.0px"
   android:drawSelectorOnTop="false"
   android:listSelector="@android:color/transparent"
   android:scrollbars="none" />

</LinearLayout>

listview适配器的布局。


<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="58dp"
 android:background="@drawable/list_item_bg"
 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="50dp"
   android:layout_height="50dp"
   android:layout_marginRight="5dp"
   android:src="@drawable/wechat_icon" />

<TextView
   android:id="@+id/title"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_toRightOf="@id/icon"
   android:textColor="@color/black"
   android:textSize="15sp" />

<TextView
   android:id="@+id/msg"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_alignBottom="@id/icon"
   android:layout_alignLeft="@id/title"
   android:textColor="@color/grey" />

<TextView
   android:id="@+id/time"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_alignParentRight="true"
   android:layout_alignTop="@id/title"
   android:textColor="@color/grey" />

</RelativeLayout>

附drawable目录下的xml文件
holder_bg.xml


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:drawable="@color/dimgrey" android:state_pressed="true"></item>
 <item android:drawable="@color/dimgrey" android:state_focused="true"></item>
 <item android:drawable="@color/dimgrey" android:state_selected="true"></item>
 <item android:drawable="@color/gray" android:state_enabled="true"></item>
 <item android:drawable="@color/gray" android:state_enabled="false"></item>

</selector>

list_item_bg.xml.


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:drawable="@drawable/item_point_bg" android:state_pressed="true"></item>
 <item android:drawable="@drawable/item_point_bg" android:state_focused="true"></item>
 <item android:drawable="@drawable/item_point_bg" android:state_selected="true"></item>
 <item android:drawable="@android:color/transparent" android:state_enabled="true"></item>
 <item android:drawable="@android:color/transparent" android:state_enabled="false"></item>

</selector>

还有一些对color颜色的定义,这里就不贴了,大家可以直接换成颜色的代码就可以。

来源:http://blog.csdn.net/qq_33776306/article/details/52596055

标签:android,侧滑删除
0
投稿

猜你喜欢

  • Spring3 整合MyBatis3 配置多数据源动态选择SqlSessionFactory详细教程

    2023-03-21 21:22:27
  • android studio 项目 :UI设计高精度实现简单计算器

    2022-07-14 02:09:29
  • Java实现超市会员管理系统

    2021-10-01 08:39:28
  • springmvc接收json串,转换为实体类List方法

    2023-07-28 12:40:00
  • 启用springboot security后登录web页面需要用户名和密码的解决方法

    2022-01-29 06:12:07
  • java线程池:获取运行线程数并控制线程启动速度的方法

    2022-06-30 23:22:57
  • java文件重命名(文件批量重命名)实例程序代码分享

    2023-07-20 06:45:06
  • C#中读写INI配置文件的方法

    2021-10-29 19:57:37
  • Java运行时数据区划分原理解析

    2023-07-13 22:05:48
  • JSON.toJSONString()空字段不忽略修改的问题

    2023-06-16 03:12:37
  • Java实现的基于socket通信的实例代码

    2021-12-30 19:06:50
  • java根据网络地址保存图片的方法

    2021-09-01 18:37:02
  • Android实现简易秒表功能

    2021-08-29 11:06:58
  • Java语言之LinkedList和链表的实现方法

    2023-12-19 20:18:59
  • Android convinientbanner顶部广告轮播控件使用详解

    2021-08-20 13:09:09
  • .NET实现父窗体关闭而不影响子窗体的方法

    2022-10-06 20:56:04
  • spring cloud gateway网关路由分配代码实例解析

    2021-06-09 02:54:08
  • C#多线程系列之线程通知

    2023-09-13 17:08:03
  • 解析C#多线程编程中异步多线程的实现及线程池的使用

    2021-10-23 02:36:23
  • 简单了解Java多态向上转型相关原理

    2023-10-11 16:11:01
  • asp之家 软件编程 m.aspxhome.com