最近在网上学到一篇类似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. [luoguP1041] 传染病控制(DFS)

    传送门 n <= 300 结果裸的dfs就直接过了.. 枚举每一层,枚举删除每一层的边,然后把删除的边所连接的子树全部删去 代码 #include <vector> #include ...

  2. Uva - 12230 Crossing Rivers (数学期望)

    你住在村庄A,每天需要过很多条河到另一个村庄B上班,B在A的右边,所有的河都在A,B之间,幸运的是每条船上都有自由移动的自动船, 因此只要到达河左岸然后等船过来,在右岸下船,上船之后船的速度不变.现在 ...

  3. AtCoder Grand Contest 011 E - Increasing Numbers(灵性乱搞)

    题意: 当一个整数高位数字总不小于低位数字,或者说写成字符串之后单调不下降,称之为上升数.求一个整数最少能表示为多少个上升数的和.(n<=1e500000) 分析: 考虑那些不下降的数字,一定可 ...

  4. ETL增量单表同步简述_根据dateTime增量

    ETL增量单表同步简述 1. 实现需求 当原数据库的表有新增.更新.删除操作时,将改动数据同步到目标库对应的数据表. 2. 设计思路 设计总体流程图如下: 步骤简单说明: 1.设置job的执行属性,如 ...

  5. 谈论javascript闭包

    闭包看似很简单,其实牵扯了很多东西,例如:上下文作用域(事件处理程序).内存占用.局部以及全局变量.回调函数以及编程模式等 首先我们谈论一个问题,为什么需要闭包? 1.var全局定义(全局污染)- 指 ...

  6. Android基础新手教程——4.3.1 BroadcastReceiver牛刀小试

    Android基础新手教程--4.3.1 BroadcastReceiver牛刀小试 标签(空格分隔): Android基础新手教程 本节引言 本节我们将来学习Android四大组件中的第三个:Bro ...

  7. 05-图1. List Components (25)

    05-图1. List Components (25) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue For a ...

  8. Eclipse 常用快捷键及使用技巧

    做 java 开发的,经常会用 Eclipse 或者 MyEclise 集成开发环境,一些实用的 Eclipse 快捷键和使用技巧,可以在平常开发中节约出很多时间提高工作效率,下面我就结合自己开发中的 ...

  9. java struts jxl 导入导出Excel(无模板)

    jar包: import javax.servlet.http.HttpServletResponse; import java.io.OutputStream; import java.io.Fil ...

  10. 特征列 属性值 获取 vowpal wabbit 生成DNN 的训练测试数据

    用户特征文件 userFeature.data 每 行 代 表 一 个 用 户 的 特 征 数 据, 格 式 为: “uid|features”,uid 和 features 用竖线“|”分隔.其中 ...