最近在网上学到一篇类似QQ消息左滑删除的demo,完善了下代码,感觉还不错,特此分享一波:

CustomSwipeListView.java 是个继承自ListView的类,里面调用了自定义View 类SwipeItemView.java的一个收缩的方法。其实QQ消息删除这个动画可以有很多种方法实现,这里我们介绍的方法是:常规ListView的每个Item展示方式不变,只是在Adapter类里面去绑定该显示的数据和删除等字样。具体详见如下:

自定义的ListView:

package com.ryg.slideview;

import android.annotation.SuppressLint;
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 CustomSwipeListView extends ListView {

private static final String TAG = "ListViewCompat";

public static SwipeItemView mFocusedItemView;

private int mPosition;

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

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

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

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

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

@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
int x = (int) event.getX();
int y = (int) event.getY();
// 我们想知道当前点击了哪一行
int position = pointToPosition(x, y);
Log.e(TAG, "postion=" + position);
if (position != INVALID_POSITION) {
int firstPos = getFirstVisiblePosition();
mFocusedItemView = (SwipeItemView) getChildAt(position - firstPos);
}
}
default:
break;
}

if (mFocusedItemView != null) {
mFocusedItemView.onRequireTouchEvent(event);

}

return super.onTouchEvent(event);
}

}

自定义的布局:

package com.ryg.slideview;

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

public class SwipeItemView extends LinearLayout {

private static final String TAG = "SlideView";

private Context mContext;

private LinearLayout mViewContent;

private RelativeLayout mHolder;

private Scroller mScroller;

private OnSlideListener mOnSlideListener;

private int mHolderWidth;

private int mLastX = 0;

private int mLastY = 0;

private static final int TAN = 2;

private boolean isHorizontalMove = true;// 判断是否横滑

public interface OnSlideListener {
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;

/**
* @param view
* current SlideView
* @param status
* SLIDE_STATUS_ON or SLIDE_STATUS_OFF
*/
public void onSlide(View view, int status);
}

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

public SwipeItemView(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);

mHolder = (RelativeLayout) findViewById(R.id.holder);
mHolder.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
mHolderWidth = mHolder.getMeasuredWidth();
mHolder.setLayoutParams(new LinearLayout.LayoutParams(mHolderWidth, LayoutParams.MATCH_PARENT));
}

public void setButtonText(CharSequence text) {
((TextView) findViewById(R.id.delete)).setText(text);
}

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

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: {
isHorizontalMove = true;// 按下默认为true,当移上下则为false
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) {
isHorizontalMove = false;
break;
}

if (!isHorizontalMove)
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.15 > 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;
mScroller.startScroll(scrollX, 0, delta, 0, Math.abs(delta) * 2);
invalidate();
}

@Override
public void computeScroll() {//由父视图调用,用于通知子视图在必要时更新 mScrollX 和 mScrollY 的值。 该操作主要用于子视图使用 Scroller 进行动画滚动时
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}

public boolean getisHorizontalMove() {
return isHorizontalMove;
}

}

Adapter:

package com.ryg.slideview;

import java.util.List;

import android.annotation.SuppressLint;
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.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.ryg.slideview.SwipeItemView.OnSlideListener;

public class HistoryListViewAdapter extends BaseAdapter {

private LayoutInflater mInflater;

private List<HistoryListItemObject> mMessageItems;

private Context context;

private SwipeItemView mLastSlideViewWithStatusOn;

public HistoryListViewAdapter(Context context, List<HistoryListItemObject> mMessageItems) {
mInflater = LayoutInflater.from(context);
this.mMessageItems = mMessageItems;
this.context = context;
}

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

@SuppressLint("InflateParams")
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
SwipeItemView slideView = (SwipeItemView) convertView;
if (slideView == null) {
View itemView = mInflater.inflate(R.layout.history_listview_items, null);
slideView = new SwipeItemView(context);
slideView.setContentView(itemView);
holder = new ViewHolder(slideView);
slideView.setOnSlideListener(new OnSlideListener() {
@Override
public void onSlide(View view, int status) {
// TODO Auto-generated method stub
if (mLastSlideViewWithStatusOn != null && mLastSlideViewWithStatusOn != view) {
mLastSlideViewWithStatusOn.shrink();
}
if (status == SLIDE_STATUS_ON) {
mLastSlideViewWithStatusOn = (SwipeItemView) view;
}
}
});
slideView.setTag(holder);
}
else {
holder = (ViewHolder) slideView.getTag();
}
HistoryListItemObject item = mMessageItems.get(position);
if (CustomSwipeListView.mFocusedItemView != null) {
CustomSwipeListView.mFocusedItemView.shrink();
}

holder.icon.setImageResource(item.getIconRes());
holder.title.setText(item.getTitle());
holder.msg.setText(item.getMsg());
holder.deleteHolder.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
mMessageItems.remove(position);
Toast.makeText(context, String.valueOf(position), Toast.LENGTH_SHORT).show();
notifyDataSetChanged();
}
});

return slideView;
}

private static class ViewHolder {
public ImageView icon;

public TextView title;

public TextView msg;

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);
deleteHolder = (ViewGroup) view.findViewById(R.id.holder);
}
}
}

布局:

没有删除布局的Item布局:

<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="wrap_content"
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="160dp"
android:layout_height="50dp"
android:layout_marginRight="5dp"
android:scaleType="fitStart"
android:src="@drawable/wechat_icon" />

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

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

</RelativeLayout>

Merge成最终的Item的布局:

<?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:background="#5FBBB9"
android:clickable="true" >

<TextView
android:id="@+id/delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="解绑"
android:textColor="@color/floralwhite" />

</RelativeLayout>

</merge>

最后在Activity里面去用CustomSwipeListView这个自定义View就好了,因为它继承自ListView,所以直接用Adapter绑定数据.

类似QQ消息左滑删除的Demo的更多相关文章

  1. 使用zepto实现QQ消息左滑删除效果

    有这样一个需求: 1. 有一个列表,将每一个列表项左滑动出现删除按钮: 2. 右滑动隐藏删除按钮: 3. 点击这个删除按钮删除该列表项. 完成以后的效果: 这是微信网页端的页面,使用的是 zepto ...

  2. android QQ消息左滑动删除实例(优化版SwipeListViewEX)

    仿 QQ消息左滑动删除item消息实例 源代码参考:http://blog.csdn.net/gaolei1201/article/details/42677951 自己作了一些调整,全部代码下载地址 ...

  3. 仿QQ列表左滑删除

    一直想写个仿QQ通讯列表左滑删除的效果,今天终于忙里偷闲,简单一个. 大概思路是这样的: 通过 ontouchstartontouchmoveontouchend 结合css3的平移. 不多说,直接上 ...

  4. 类似qq的左滑菜单栏简单实现

    代码托管到了Github https://github.com/cyuanyang/YYSlideView 主演实现代码: 1.滑动的viewController的初始化主要view -(instan ...

  5. Android开发学习之路-PopupWindow和仿QQ左滑删除

    这周作业,要做一个类似QQ的左滑删除效果的ListView,因为不想给每个item都放一个按钮,所以决定用PopupWindow,这里记录一下 先放一下效果图: 先说明一下这里面的问题: ①没有做到像 ...

  6. 模仿QQ左滑删除

    需求: 1.左滑删除 2.向左滑动距离超过一半的时候让它自动滑开,向右滑动超过一半的时候自动隐藏 3.一次只允许滑开一个item 还有,根本不需要自定义view来实现,谨防入坑 布局: <?xm ...

  7. 微信小程序独家秘笈之左滑删除

    代码地址如下:http://www.demodashi.com/demo/14056.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...

  8. [转]ANDROID仿IOS微信滑动删除_SWIPELISTVIEW左滑删除例子

    转载:http://dwtedx.sinaapp.com/itshare_290.html 本例子实现了滑动删除ListView的Itemdemo的效果.大家都知道.这种创意是来源于IOS的.左滑删除 ...

  9. wex5 实战 苹果左滑删除与长按编辑

    用了多年苹果,习惯了苹果的左滑删除与长按编辑,特别是短信什么的,很多安卓界面也采用了类似方式. 我的想法突如其来,用wex5也设计一个这样的功能,可以吗? 那句广告词,没有什么不可能. 呵呵. 一   ...

随机推荐

  1. 寒假训练3解题报告 CodeForces #148

    CodeForces 148B 一道简单模拟,判断龙能够抓到公主几次,如果公主和龙同时到达公主的城堡,不算龙抓住她,因为路程除以速度可能会产生浮点数,所以这里考虑一下精度问题 #include < ...

  2. 国内程序员的十大疑问之一:为什么老外不愿意用MyBatis?

    老外用MyBatis吗 昨天我在我在知乎看到了一张比较Hibernate和MyBatis使用情况的图,顺手发了条朋友圈: Hibernate vs MyBatis ,谁能告诉我什么样的国情导致了这么大 ...

  3. 【BZOJ4583】购物(组合计数)

    题意:商店出售3种颜色的球,分别为红.绿.蓝. 城市里有n个商店,第i个商店在第First_i天开始营业,连续营业Red_i+Green_i+Blue_i天,每个商店每天只能出售一种颜色的球. 每天最 ...

  4. 2018牛客网暑期ACM多校训练营(第一场)D图同构,J

    链接:https://www.nowcoder.com/acm/contest/139/D来源:牛客网 同构图:假设G=(V,E)和G1=(V1,E1)是两个图,如果存在一个双射m:V→V1,使得对所 ...

  5. How many ways?? 矩阵快速幂 邻接矩阵意义

    春天到了, HDU校园里开满了花, 姹紫嫣红, 非常美丽. 葱头是个爱花的人, 看着校花校草竞相开放, 漫步校园, 心情也变得舒畅. 为了多看看这迷人的校园, 葱头决定, 每次上课都走不同的路线去教室 ...

  6. SQL SERVER代理作业删除失败问题

    在SQL Server 2005上遇到了先删除已运行维护计划后,再删除代理中由其产生的作业时,提示删除失败.   DELETE 语句与 REFERENCE 约束"FK_subplan_job ...

  7. 【进击后端】Ubuntu 命令行 安装nginx

    一.安装nginx apt-get install nginx 安装路径为:/etc/nginx/conf.d 二.配置nginx,在conf.d目录下新建test.conf 新建文件的命令是vi t ...

  8. I/O---BufferedInputStream及相关类介绍

    关于BufferedInputStream 是java提供的具有缓存作用的字节输入流.与之对应的还有BufferedOutStream 和 BufferedRead 和BufferedWriter 这 ...

  9. Ubuntu 16.04设置rc.local开机启动命令/脚本的方法(通过update-rc.d管理Ubuntu开机启动程序/服务)

    注意:rc.local脚本里面启动的用户默认为root权限. 一.rc.local脚本 rc.local脚本是一个Ubuntu开机后会自动执行的脚本,我们可以在该脚本内添加命令行指令.该脚本位于/et ...

  10. Ubuntu 16.04安装QtCharts时报错:'qtConfig' is not a recognized test function.

    错误: 'qtConfig' is not a recognized test function. 解决方法: 其实5.9分支的版本有问题,转成5.7分支即可. git clone https://g ...