当前位置: 移动技术网 > IT编程>移动开发>Android > Android自定义ListView实现仿QQ可拖拽列表功能

Android自定义ListView实现仿QQ可拖拽列表功能

2019年07月24日  | 移动技术网IT编程  | 我要评论

坏男人别叫我宝贝,春闺记事 19楼,吾爱小说论坛

我们大致的思路,其实是这样子的,也是我的设想,我们可以先去实现一个简单的listview的数据,但是他的adapter,我们可以用系统封装好的,然后传递进去一个实体类,最后自定义一个listview去操作,所以我们先把准备的工作做好,比如?

list_item.xml

<?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="match_parent">
<imageview
android:id="@+id/iv_logo"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignparentleft="true"
android:layout_centerinparent="true"
android:layout_marginleft="10dp"/>
<textview
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerinparent="true"
android:layout_marginleft="10dp"
android:layout_torightof="@id/iv_logo"/>
</relativelayout>

这就只有一个头像和一句话了,然后我们把实体类也给写完了

dragbean

package com.liuguilin.draglistviewsample.entity;
/*
* 项目名: draglistviewsample 
* 包名: com.liuguilin.draglistviewsample.entity
* 文件名: dragbean
* 创建者: lgl
* 创建时间: 2016/8/29 22:49
* 描述: 实体类
*/
public class dragbean {
private int ivid;
private string text;
public dragbean() {
}
public dragbean(int ivid, string text) {
this.ivid = ivid;
this.text = text;
}
public int getivid() {
return ivid;
}
public string gettext() {
return text;
}
}

ok,其实很简单,id是图片,然后是文本,这样我们就可以来实现一个adapter了,这里我用的是arrayadapter这样能让我们插入和删除很轻松

dragadapter

package com.liuguilin.draglistviewsample.adapter;
/*
* 项目名: draglistviewsample 
* 包名: com.liuguilin.draglistviewsample.adapter
* 文件名: dragadapter
* 创建者: lgl
* 创建时间: 2016/8/29 22:41
* 描述: 拖拽列表的数据源
*/
import android.content.context;
import android.view.view;
import android.view.viewgroup;
import android.widget.arrayadapter;
import android.widget.imageview;
import android.widget.textview;
import com.liuguilin.draglistviewsample.r;
import com.liuguilin.draglistviewsample.entity.dragbean;
import java.util.list;
public class dragadapter extends arrayadapter<dragbean> {
/**
* 构造方法
*
* @param context
* @param mlist
*/
public dragadapter(context context, list<dragbean> mlist) {
super(context, 0, mlist);
}
/**
* 实现view
*
* @param position
* @param convertview
* @param parent
* @return
*/
@override
public view getview(int position, view convertview, viewgroup parent) {
view view;
viewholder viewholder;
if (convertview == null) {
view = view.inflate(getcontext(), r.layout.list_item, null);
viewholder = new viewholder();
viewholder.imageview = (imageview) view
.findviewbyid(r.id.iv_logo);
viewholder.textview = (textview) view.findviewbyid(r.id.textview);
view.settag(viewholder);
} else {
view = convertview;
viewholder = (viewholder) view.gettag();
}
viewholder.imageview.setimageresource(getitem(position).getivid());
viewholder.textview.settext(getitem(position).gettext());
return view;
}
/**
* 缓存
*/
static class viewholder {
imageview imageview;
textview textview;
}
}

好的,其实到这里,他就是一个最普通的listview了,我们给他填充点数据

mainactivity

package com.liuguilin.draglistviewsample;
import android.support.v7.app.appcompatactivity;
import android.os.bundle;
import com.liuguilin.draglistviewsample.adapter.dragadapter;
import com.liuguilin.draglistviewsample.entity.dragbean;
import com.liuguilin.draglistviewsample.view.draglistview;
import java.util.arraylist;
import java.util.list;
public class mainactivity extends appcompatactivity {
//列表
private draglistview mlistview;
//数据
private list<dragbean> mlist = new arraylist<>();
//数据源
private dragadapter adapter;
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
initview();
}
/**
* 初始化view
*/
private void initview() {
mlistview = (draglistview) findviewbyid(r.id.mlistview);
//新增數據
for (int i = 0; i < 30; i++) {
dragbean bean = new dragbean(r.mipmap.ic_launcher, "刘某人程序员" + i);
mlist.add(bean);
}
//初始化数据源
adapter = new dragadapter(this,mlist);
mlistview.setadapter(adapter);
}
}

现在可以看看实际的效果了

这里写图片描述

现在我们可以重写我们的listview了

我们首先拦截他的事件

/**
* 获取触点所在条目的位置
* 获取选中条目的图片
* 事件的拦截机制
*
* @param ev
* @return
*/
@override
public boolean onintercepttouchevent(motionevent ev) {
//识别动作
switch (ev.getaction()) {
case motionevent.action_down:
//获取触点的坐标
int x = (int) ev.getx();
int y = (int) ev.gety();
//这样就可以计算我按到哪个条目上了
mstartposition = mendposition = pointtoposition(x, y);
//判断触点是否在logo的区域
viewgroup itemview = (viewgroup) getchildat(mstartposition - getfirstvisibleposition());
//记录手指在条目中的相对y坐标
dragpoint = y - itemview.gettop();
//listview在屏幕中的y坐标
dragoffset = (int) (ev.getrawy() - y);
//拖动的图标
view dragger = itemview.findviewbyid(r.id.iv_logo);
//判断触点是否在logo区域
if (dragger != null && x < dragger.getright() + 10) {
//定义listview的滚动条目
//上
upscroll = getheight() / 3;
//下
downscroll = getheight() * 2 / 3;
//获取选中的图片/截图
itemview.setdrawingcacheenabled(true);
//获取截图
bitmap bitmap = itemview.getdrawingcache();
//图片滚动
startdrag(bitmap, y);
}
break;
}
//还会传递事件到子view
return false;
}

获取到他的position之后我们直接截图,并且显示我们的window,这里做的事情就比较多了我们还要判断是否点击的是头像才去显示window

/**
* 图片在y轴,也就是上下可滚动
*
* @param bitmap
* @param y
*/
private void startdrag(bitmap bitmap, int y) {
//窗体仿照
wm = (windowmanager) getcontext().getsystemservice(context.window_service);
//设置窗体参数
lparams = new windowmanager.layoutparams();
//设置宽高
lparams.width = windowmanager.layoutparams.wrap_content;
lparams.height = windowmanager.layoutparams.wrap_content;
//属性
lparams.flags = windowmanager.layoutparams.flag_not_focusable
| windowmanager.layoutparams.flag_not_touchable
| windowmanager.layoutparams.flag_keep_screen_on;
//设置半透明
lparams.alpha = 0.8f;
//设置居中
lparams.gravity = gravity.top;
//设置xy
lparams.x = 0;
lparams.y = y-dragpoint + dragoffset;
//属性
lparams.format = pixelformat.translucent;
//设置动画
lparams.windowanimations = 0;
//图片
dragimageview = new imageview(getcontext());
//设置截图
dragimageview.setimagebitmap(bitmap);
//添加显示窗体
wm.addview(dragimageview, lparams);
}

好的,我们初始化一下window,光显示还不行呢,我们还要可以滑动,怎么监听?ontouchevent的action_move事件是可以做到的

/**
* 触摸事件
*
* @param ev
* @return
*/
@override
public boolean ontouchevent(motionevent ev) {
//错误的位置
if (dragimageview != null && mendposition != invalid_position) {
//在滑动事件中控制上下滑动
switch (ev.getaction()) {
case motionevent.action_move:
//直接获取到y坐标进行移动
int movey = (int) ev.gety();
dodrag(movey);
break;
//停止拖动成像
case motionevent.action_up:
int upy = (int) ev.gety();
stopdrag();
ondrag(upy);
break;
}
}
//拦截到事件
return true;
}

我们在移动的时候不断的去更新他的位置

/**
* 控制窗体移动
*
* @param movey
*/
private void dodrag(int movey) {
if (dragimageview != null) {
lparams.y = movey - dragpoint + dragoffset;
wm.updateviewlayout(dragimageview, lparams);
}
//判断移动到分割线 返回-1
int templine = pointtoposition(0, movey);
//我们处理他
if (templine != invalid_position) {
//只要你不移动到分割线 我才处理
mendposition = templine;
}
//拖拽时滚动、滚动速度
int scrollspeed = 0;
//上滚
if (movey < upscroll) {
scrollspeed = 10;
//下滚
} else if (movey > downscroll) {
scrollspeed = -10;
}
//开始滚动
if (scrollspeed != 0) {
//计算条目的y坐标
int dragitemy = getchildat(mendposition - getfirstvisibleposition()).gettop();
//当前速度
int dy = dragitemy + scrollspeed;
//设置
setselectionfromtop(mendposition, dy);
}
}

当你移动完成之后,我就可以停止你的window体了

/**
* 停止的位置
*/
private void stopdrag() {
//直接移除窗体
if (dragimageview != null) {
wm.removeview(dragimageview);
dragimageview = null;
}
}

这样,我就直接拼接你的position达到一个拖拽的效果

/**
* 最终开始成像
*
* @param upy
*/
private void ondrag(int upy) {
//分割线的处理
//判断移动到分割线 返回-1
int templine = pointtoposition(0, upy);
//我们处理他
if (templine != invalid_position) {
//只要你不移动到分割线 我才处理
mendposition = templine;
}
/**
* 你在最上方就直接落在第一个最下方就直接最下面一个
*/
//上边界处理
if (upy < getchildat(1).gettop()) {
mendposition = 1;
//下边界处理
} else if (upy > getchildat(getchildcount() - 1).gettop()) {
mendposition = getadapter().getcount() - 1;
}
//开始更新item顺序
if (mendposition > 0 && mendposition < getadapter().getcount()) {
dragadapter adapter = (dragadapter) getadapter();
//删除原来的条目
adapter.remove(adapter.getitem(mstartposition));
//更新
adapter.insert(adapter.getitem(mstartposition), mendposition);
}
}

全部代码贴上

draglistview

package com.liuguilin.draglistviewsample.view;
/*
* 项目名: draglistviewsample 
* 包名: com.liuguilin.draglistviewsample.view
* 文件名: draglistview
* 创建者: lgl
* 创建时间: 2016/8/29 20:50
* 描述: 自定义高仿qq列表可拖拽的listview
*/
import android.content.context;
import android.graphics.bitmap;
import android.graphics.pixelformat;
import android.util.attributeset;
import android.view.gravity;
import android.view.motionevent;
import android.view.view;
import android.view.viewgroup;
import android.view.windowmanager;
import android.widget.imageview;
import android.widget.listview;
import com.liuguilin.draglistviewsample.r;
import com.liuguilin.draglistviewsample.adapter.dragadapter;
public class draglistview extends listview {
//按下选中的position
private int mstartposition;
//需要达到的position
private int mendposition;
//手指在条目中的相对y坐标
private int dragpoint;
//listview在屏幕中的y坐标
private int dragoffset;
//上
private int upscroll;
//下
private int downscroll;
//窗体
private windowmanager wm;
//显示的截图
private imageview dragimageview;
//窗体参数
private windowmanager.layoutparams lparams;
//构造方法
public draglistview(context context, attributeset attrs) {
super(context, attrs);
}
/**
* 获取触点所在条目的位置
* 获取选中条目的图片
* 事件的拦截机制
*
* @param ev
* @return
*/
@override
public boolean onintercepttouchevent(motionevent ev) {
//识别动作
switch (ev.getaction()) {
case motionevent.action_down:
//获取触点的坐标
int x = (int) ev.getx();
int y = (int) ev.gety();
//这样就可以计算我按到哪个条目上了
mstartposition = mendposition = pointtoposition(x, y);
//判断触点是否在logo的区域
viewgroup itemview = (viewgroup) getchildat(mstartposition - getfirstvisibleposition());
//记录手指在条目中的相对y坐标
dragpoint = y - itemview.gettop();
//listview在屏幕中的y坐标
dragoffset = (int) (ev.getrawy() - y);
//拖动的图标
view dragger = itemview.findviewbyid(r.id.iv_logo);
//判断触点是否在logo区域
if (dragger != null && x < dragger.getright() + 10) {
//定义listview的滚动条目
//上
upscroll = getheight() / 3;
//下
downscroll = getheight() * 2 / 3;
//获取选中的图片/截图
itemview.setdrawingcacheenabled(true);
//获取截图
bitmap bitmap = itemview.getdrawingcache();
//图片滚动
startdrag(bitmap, y);
}
break;
}
//还会传递事件到子view
return false;
}
/**
* 图片在y轴,也就是上下可滚动
*
* @param bitmap
* @param y
*/
private void startdrag(bitmap bitmap, int y) {
//窗体仿照
wm = (windowmanager) getcontext().getsystemservice(context.window_service);
//设置窗体参数
lparams = new windowmanager.layoutparams();
//设置宽高
lparams.width = windowmanager.layoutparams.wrap_content;
lparams.height = windowmanager.layoutparams.wrap_content;
//属性
lparams.flags = windowmanager.layoutparams.flag_not_focusable
| windowmanager.layoutparams.flag_not_touchable
| windowmanager.layoutparams.flag_keep_screen_on;
//设置半透明
lparams.alpha = 0.8f;
//设置居中
lparams.gravity = gravity.top;
//设置xy
lparams.x = 0;
lparams.y = y-dragpoint + dragoffset;
//属性
lparams.format = pixelformat.translucent;
//设置动画
lparams.windowanimations = 0;
//图片
dragimageview = new imageview(getcontext());
//设置截图
dragimageview.setimagebitmap(bitmap);
//添加显示窗体
wm.addview(dragimageview, lparams);
}
/**
* 触摸事件
*
* @param ev
* @return
*/
@override
public boolean ontouchevent(motionevent ev) {
//错误的位置
if (dragimageview != null && mendposition != invalid_position) {
//在滑动事件中控制上下滑动
switch (ev.getaction()) {
case motionevent.action_move:
//直接获取到y坐标进行移动
int movey = (int) ev.gety();
dodrag(movey);
break;
//停止拖动成像
case motionevent.action_up:
int upy = (int) ev.gety();
stopdrag();
ondrag(upy);
break;
}
}
//拦截到事件
return true;
}
/**
* 最终开始成像
*
* @param upy
*/
private void ondrag(int upy) {
//分割线的处理
//判断移动到分割线 返回-1
int templine = pointtoposition(0, upy);
//我们处理他
if (templine != invalid_position) {
//只要你不移动到分割线 我才处理
mendposition = templine;
}
/**
* 你在最上方就直接落在第一个最下方就直接最下面一个
*/
//上边界处理
if (upy < getchildat(1).gettop()) {
mendposition = 1;
//下边界处理
} else if (upy > getchildat(getchildcount() - 1).gettop()) {
mendposition = getadapter().getcount() - 1;
}
//开始更新item顺序
if (mendposition > 0 && mendposition < getadapter().getcount()) {
dragadapter adapter = (dragadapter) getadapter();
//删除原来的条目
adapter.remove(adapter.getitem(mstartposition));
//更新
adapter.insert(adapter.getitem(mstartposition), mendposition);
}
}
/**
* 停止的位置
*/
private void stopdrag() {
//直接移除窗体
if (dragimageview != null) {
wm.removeview(dragimageview);
dragimageview = null;
}
}
/**
* 控制窗体移动
*
* @param movey
*/
private void dodrag(int movey) {
if (dragimageview != null) {
lparams.y = movey - dragpoint + dragoffset;
wm.updateviewlayout(dragimageview, lparams);
}
//判断移动到分割线 返回-1
int templine = pointtoposition(0, movey);
//我们处理他
if (templine != invalid_position) {
//只要你不移动到分割线 我才处理
mendposition = templine;
}
//拖拽时滚动、滚动速度
int scrollspeed = 0;
//上滚
if (movey < upscroll) {
scrollspeed = 10;
//下滚
} else if (movey > downscroll) {
scrollspeed = -10;
}
//开始滚动
if (scrollspeed != 0) {
//计算条目的y坐标
int dragitemy = getchildat(mendposition - getfirstvisibleposition()).gettop();
//当前速度
int dy = dragitemy + scrollspeed;
//设置
setselectionfromtop(mendposition, dy);
}
}
}

然后我们引用

layout_main.xml

<?xml version="1.0" encoding="utf-8"?>
<linearlayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<com.liuguilin.draglistviewsample.view.draglistview
android:id="@+id/mlistview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</linearlayout>

对了,别忘记了添加窗体的权限哦

<!--窗口权限-->
<uses-permission android:name="android.permission.system_alert_window"/>

做完这一系列事情之后,就觉得这个设想其实是对的,然后我们可以尝试性的运行

这里写图片描述

以上所述是小编给大家介绍的android自定义listview实现仿qq可拖拽列表功能,希望对大家有所帮助

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网