欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-03-07 15:02:51
我们大致的思路,其实是这样子的,也是我的设想,我们可以先去实现一个简单的listview的数据,但是他的adapter,我们可以用系统封装好的,然后传递进去一个实体类,最后...

我们大致的思路,其实是这样子的,也是我的设想,我们可以先去实现一个简单的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);
}
}

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

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

现在我们可以重写我们的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可拖拽列表功能

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