Android开发学习之路-PopupWindow和仿QQ左滑删除
这周作业,要做一个类似QQ的左滑删除效果的ListView,因为不想给每个item都放一个按钮,所以决定用PopupWindow,这里记录一下
先放一下效果图:
先说明一下这里面的问题:
①没有做到像QQ那样可以允许item跟随手指移动,虽然PopupWindow有update方法让我们动态移动,但是在屏幕外移动会没有动画效果,直接弹进来
②仔细观察可以发现,item的滑动和删除按钮的滑动是分开的,无法保证它们会一起播放,QQ的动画可以
再说说大概的思路,因为我们没有让item都带上Button,所以用到了PopupWindow来做删除按钮,我们可以通过自定义ListView来监听手势,如果判断了是左滑动,则让PopupWindow动态显示出来,在让item做一个平移的效果,当两者的持续时间一致的时候,效果就出来了。接下来,如果用户点击了PopupWindow以外的区域,PopupWindow会消失,回调dismiss方法,我们再把item移回原位。
介绍一下PopupWindow,它允许我们在屏幕上的任意位置弹出一个提示框,提示框的布局我们可以自己定义,我们上面的删除按钮,其实就是一个PopupWindow。
如果我们要使用PopupWindow,可以像下面这样,定义一个自定义布局的PopupWindow
View view = LayoutInflater.from(context).inflate(R.layout.item_delete, null);
PopupWindow mPopupWindow = new PopupWindow(view, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, true);
如果我们要显示这个PopupWindow,可以调用showAtLocation或者showAsDropDown,前者是直接把PopupWindow显示在某个特定位置,后者是在某个位置从上向下弹出,这里我们调用第一个方法,有兴趣的可以尝试下第二个,参数什么的可以不用管,不懂的看看API就知道了,之后再调用update方法更新位置信息
mPopupWindow.showAtLocation(itemView, Gravity.LEFT | Gravity.TOP, locations[0]+ itemView.getWidth() - popWidth, locations[1]);
mPopupWindow.update();
至于对PopupWindow设置背景什么的,需要的时候看看API就知道了!这里不详说。
至于效果的实现,我们一步一步的来。
①自定义一个ListView,详细的做法,已经在下面做了注释
public class SlidableListView extends ListView {
private static final String TAG = "SlidableListView";
private int touchSlop; private View itemView; // 每个Item的View
private int mCurrentDownPosition; // 当前点击的位置,用来找出点击的Item
private PopupWindow mPopupWindow; // 删除按钮
private int popHeight, popWidth; // 删除按钮的高和宽
private boolean isSliding = false; // 手指是否在向左滑动
private int downX, downY, moveX, moveY; // 点击的X和Y,以及移动的X和Y
private OnItemDeleteListener onItemDeleteListener; // 点击删除按钮时候的回调接口 public SlidableListView(Context context) {
super(context);
} // 在布局中使用自定义控件调用的构造函数,这里进行一些必要的初始化
public SlidableListView(Context context, AttributeSet attrs) {
super(context, attrs);
// 初始化删除按钮,记得让按钮可以接收焦点
View view = LayoutInflater.from(context).inflate(R.layout.item_delete, null);
mPopupWindow = new PopupWindow(view, WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT, true);
// 获取删除按钮的宽和高
view.measure(0, 0);
popWidth = view.getMeasuredWidth();
popHeight = view.getMeasuredHeight();
// 获取滑动的最小距离
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
} public SlidableListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
} // 先拦截touch事件,根据不同情况进行分发
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
int x = (int) ev.getX(), y = (int) ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
downX = x;
downY = y;
// 如果删除按钮正在显示,那么不处理这个触摸事件,并把按钮隐藏起来
if (mPopupWindow.isShowing()) {
mPopupWindow.dismiss();
return false; // 阻止事件传递
}
mCurrentDownPosition = pointToPosition(downX, downY);
// 获取触摸的item
itemView = getChildAt(mCurrentDownPosition - getFirstVisiblePosition());
break;
case MotionEvent.ACTION_MOVE:
moveX = x;
moveY = y;
int deltaX = moveX - downX;
int deltaY = moveY - downY;
// 当判断为左滑的时候,把事件交给OnTouchEvent来处理
if (moveX < downX && Math.abs(deltaX) > touchSlop && Math.abs(deltaY) <
touchSlop) {
isSliding = true;
}
break;
}
return super.dispatchTouchEvent(ev);
} @Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
// 判断是否已经有左右滑动
if (isSliding) {
switch (action) {
case MotionEvent.ACTION_UP:
isSliding = false;
break;
case MotionEvent.ACTION_MOVE:
// 获取item在屏幕中的位置信息
int[] locations = new int[2];
itemView.getLocationOnScreen(locations);
// 给删除按钮设置动画,这里自定了出现从右到左平移,隐藏从左到右
mPopupWindow.setAnimationStyle(R.style.PopupWindowAnimations);
// 设置删除按钮的位置,这里设置在item的最右端
mPopupWindow.showAtLocation(itemView, Gravity.LEFT | Gravity.TOP, locations[0]
+ itemView.getWidth() - popWidth, locations[1]);
mPopupWindow.update();
// 给item播放平移动画,配合删除按钮
ObjectAnimator.ofFloat(itemView, "translationX", 0, -popWidth).setDuration
(200).start();
// 获取删除按钮的实例,这里是一个TextView
TextView delete = (TextView) mPopupWindow.getContentView().findViewById(R.id
.delete);
// 设置监听,如果删除按钮消失(点击按钮外的地方,按下back键)回调
mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
// 把item移回原位置
ObjectAnimator.ofFloat(itemView, "translationX", -popWidth, 0)
.setDuration(200).start();
}
});
// 给删除按钮设置点击事件
delete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 获取点击的item位置,并调用回调接口方法发送出去,隐藏删除按钮
int position = SlidableListView.this.getPositionForView(itemView);
onItemDeleteListener.onItemDelete(position);
mPopupWindow.dismiss();
}
});
break;
}
// 如果有在滑动,消化事件
return true;
}
return super.onTouchEvent(ev);
} public void setOnItemDeleteListener(OnItemDeleteListener onItemDeleteListener) {
this.onItemDeleteListener = onItemDeleteListener;
} // 回调删除事件的接口
public interface OnItemDeleteListener {
void onItemDelete(int position);
}
}
②自定义消失和进入的动画,因为系统中没有我们想要的平移进入
style.xml:
<style name="PopupWindowAnimations" parent="@android:style/Animation">
<item name="android:windowEnterAnimation">@anim/delete_enter</item>
<item name="android:windowExitAnimation">@anim/delete_exit</item>
</style>
anim/delete_enter.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromXDelta="200"
android:toXDelta="0"
android:fromYDelta="0"
android:toYDelta="0"
android:duration="200"/>
</set>
anim/delete_exit.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="200"
android:fromYDelta="0"
android:toYDelta="0"
android:duration="200"/>
</set>
③自定义Adapter,这里不给出了,都懂
④在布局中使用自定义的ListView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"> <sixthweek.fndroid.com.sixthweek.SlidableListView
android:id="@+id/slidableListView"
android:layout_width="match_parent"
android:layout_height="match_parent" /> </LinearLayout>
⑤在Activity中使用adapter传入数据即可
public class SlidableListViewActivity extends AppCompatActivity {
private SlidableListView slidableListView;
private MyAdapter adapter;
private List<Map<String, String>> data; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_slidablelistview);
slidableListView = (SlidableListView) findViewById(R.id.slidableListView);
data = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Map<String, String> map = new HashMap();
map.put("name", "QQ" + i);
data.add(map);
}
adapter = new MyAdapter(this, data);
slidableListView.setAdapter(adapter);
// 实现回调接口,处理逻辑
slidableListView.setOnItemDeleteListener(new SlidableListView.OnItemDeleteListener() {
@Override
public void onItemDelete(int position) {
data.remove(position);
adapter.notifyDataSetChanged();
}
});
}
}
Android开发学习之路-PopupWindow和仿QQ左滑删除的更多相关文章
- Vue 仿QQ左滑删除功能(非原创)
非原创,摘选来源:http://www.jb51.net/article/136221.htm. 废话不多说,相当实用,先记录. Html代码: <div class="contain ...
- Android开发学习之路-RecyclerView滑动删除和拖动排序
Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...
- Android开发学习之路--基于vitamio的视频播放器(二)
终于把该忙的事情都忙得差不多了,接下来又可以开始good good study,day day up了.在Android开发学习之路–基于vitamio的视频播放器(一)中,主要讲了播放器的界面的 ...
- Android开发学习之路--Android Studio cmake编译ffmpeg
最新的android studio2.2引入了cmake可以很好地实现ndk的编写.这里使用最新的方式,对于以前的android下的ndk编译什么的可以参考之前的文章:Android开发学习之路– ...
- Android开发学习之路--网络编程之xml、json
一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的.常用的就是xml和json了.在此先要搭建个简单的服务器吧,首先呢下载 ...
- Android开发学习之路--Activity之初体验
环境也搭建好了,android系统也基本了解了,那么接下来就可以开始学习android开发了,相信这么学下去肯定可以把android开发学习好的,再加上时而再温故下linux下的知识,看看androi ...
- Android开发学习之路--Android系统架构初探
环境搭建好了,最简单的app也运行过了,那么app到底是怎么运行在手机上的,手机又到底怎么能运行这些应用,一堆的电子元器件最后可以运行这么美妙的界面,在此还是需要好好研究研究.这里从芯片及硬件模块-& ...
- Android开发学习之路--MAC下Android Studio开发环境搭建
自从毕业开始到现在还没有系统地学习android应用的开发,之前一直都是做些底层的驱动,以及linux上的c开发.虽然写过几个简单的app,也对android4.0.3的源代码做过部分的分析,也算入门 ...
- Android开发学习之路-记一次CSDN公开课
今天的CSDN公开课Android事件处理重难点快速掌握中老师讲到一个概念我觉得不正确. 原话是这样的:点击事件可以通过事件监听和回调两种方法实现. 我一听到之后我的表情是这样的: 这跟我学的看的都不 ...
随机推荐
- Qt中 QString 和int, char等的“相互”转换
转载:http://blog.csdn.net/ei__nino/article/details/7297791 Qt中 int ,float ,double转换为QString 有两种方法 1.使用 ...
- JavaScript高级程序设计-(3) 变量、作用域和内存问题
传递参数 ECMAScript所有函数参数都是按值传递的,即使对象在函数内部修改了参数的值,原始的引用任然不变,局部对象在函数执行完毕后被销毁
- vs2013中一: 无法查找或打开 PDB 文件解决办法 二:解决This function or variable may be unsafe
一 链接地址: https://www.baidu.com/s?wd=%E6%97%A0%E6%B3%95%E6%9F%A5%E6%89%BE%E6%88%96%E6%89%93%E5%BC%80 ...
- 关于UnsupportedClassVersionError的错误处理
错误:Java.lang.UnsupportedClassVersionError: Bad version number in .class file 造成这种错误的原因是你的支持Tomcat运行的 ...
- 前端CSS规范整理_转载、、、
一.文件规范 1.文件均归档至约定的目录中. 具体要求通过豆瓣的CSS规范进行讲解: 所有的CSS分为两大类:通用类和业务类.通用的CSS文件,放在如下目录中: 基本样式库 /css/core 通用U ...
- Python读取文件内容并将内容插入到SSDB中
import os import linecache import time from SSDB import SSDB ssdb = SSDB('127.0.0.1', 8888) print(&q ...
- Android 社交类APP 豆瓣同城Lite(安全,无广告)
随着科技的发展,人们的生活越来越变的单调,有时间也不知道如何打发.使用豆瓣同城手机客户端能帮助你发现身边正在进行的各种有趣的活动,你可以凭自己的兴趣来报名喜欢的活动,结实新的朋友,让自己的业余生活变得 ...
- BZOJ2506: calc
Description 给一个长度为n的非负整数序列A1,A2,…,An.现有m个询问,每次询问给出l,r,p,k,问满足l<=i<=r且Ai mod p = k的值 ...
- c#List移除列表中的元素
对于一个List<T>对象来说移除其中的元素是常用的功能.自己总结了一下,列出自己所知的几种方法. class Program { static void Main(string[] ar ...
- 【Beta】Daily Scrum Meeting总结
团队博客目录:FTD团队博客目录 一.项目预期计划和现实进展 更换网络请求框架为okHttp 完成 补充和完善服务器的API 完成(可与web端互连) 补充和完善app与服务器交互的类和方法 完成 完 ...