开发中经常遇到拖拽交换图片列表中的图片位置,决定写个简单通用的demo以便不时之需。我的项目是Egret引擎做的,因为demo是项目中写好简化出来的,所以demo也是Egret实现的,如果是其他语言,具体代码不同,但思路上应该大同小异。
先上实现效果:
功能描述:
对显示的图片列表(这里用的是2*4的图片列表),用户可以通过长按单个图片,然后拖拽与其他图片交换位置,并且其他图片位置顺位移动。当然用户也可以通过点击单个图片对图片编辑。
实现思路:
1.长按与点击的区分。可以启动计时器,判断当前点击是否是长按(比如触摸达到一秒以上即认为是长按,否则为一般点击)。
2.拖拽与交换位置。获取当前拖拽的图片的位置,判断是否达到与另一图片交换位置的临界条件,如果达到交换条件,则交换图片位置,并更新其他图片位置。
具体实现关键代码:
代码也比较简单,主要包含两个文件,一个是AvatarList.ts,为头像列表类;另一个是单个头像item:AvatarListItem。实现逻辑都在AvatarList.ts中。
对每个头像item设置点击监听,分别是开始触摸,图片移动和结束触摸:
avatarListItem.addEventListener(egret.TouchEvent.TOUCH_BEGIN,this.avatarItemTouchBeginHandler, this);//开始点击头像
avatarListItem.addEventListener(egret.TouchEvent.TOUCH_MOVE,this.avatarItemTouchMoveHandler, this);//拖拽头像
avatarListItem.addEventListener(egret.TouchEvent.TOUCH_END, this.avatarItemTouchEndHandler, this);//结束点击头像
开始触摸响应方法里要记录触摸点与图片锚点的坐标差,并启动倒计时以判断是否是长按:
//开始点击头像
private avatarItemTouchBeginHandler(e:egret.TouchEvent){
//记录点击位置与图片锚点的坐标差
let avatarListItem:AvatarListItem = e.currentTarget;
let point = this.gpAvatarList.globalToLocal(e.stageX, e.stageY);
this._distance.x = point.x - avatarListItem.x;
this._distance.y = point.y - avatarListItem.y;
this.avatarTouchTimer.start();
this.avatarTouchTimer.addEventListener(egret.TimerEvent.TIMER,this.timerCountEnd,this);
this.avatarTouchFlg = 1;//开始点击
}
接下来是图片移动的响应方法,这里要判断是否是长按拖拽,触摸时间不到1000ms则不能拖拽:
//拖拽头像
private avatarItemTouchMoveHandler(e:egret.TouchEvent){
if(this.avatarTouchFlg == 2){//如果是长按则可以移动,否则不能移动
let avatarListItem:AvatarListItem = e.currentTarget;
this.gpAvatarList.setChildIndex(avatarListItem, this.gpAvatarList.numChildren - 1);//将该图片置顶
// 从舞台(全局)坐标转换为显示对象的(本地)坐标
let point = this.gpAvatarList.globalToLocal(e.stageX, e.stageY);
//移动图片到点击位置
avatarListItem.x = point.x - this._distance.x;
avatarListItem.y = point.y - this._distance.y;
//刷新图片位置
this.refreshAvatarList(avatarListItem,point);
}
}
再触摸结束时,判断是长按还是普通点击,并分别处理:
//结束点击头像
private avatarItemTouchEndHandler(e:egret.TouchEvent){
if(this.avatarTouchFlg == 1){//点击
//TODO 打开编辑头像小弹框
}else if(this.avatarTouchFlg == 2){//长按,拖动结束
//快速拖动可能会出现错位情况,所以刷新校正全部图片位置
for(let i=0; i
对长按还是普通点击的判断标识的设置,是在倒计时结束的响应方法里,这里设置触摸不小于1000ms即为长按,否则为普通点击:
//倒计时结束
private timerCountEnd(e:Event){
if(this.avatarTouchFlg == 1){//如果点击了并且没有结束点击,则表示是长按
this.avatarTouchFlg = 2;//长按
}
//重置计时器
this.avatarTouchTimer.reset();
this.avatarTouchTimer.removeEventListener(egret.TimerEvent.TIMER,this.timerCountEnd,this);
}
刷新(交换)头像位置的方法也比较简单,我这里设置的是交换临界点是两张图片单向重叠达到图片宽度(或高度)的一半,就交换位置,并刷新其他图片位置。方法如下,具体代码就不贴了,后边我会附上完整demo:
//刷新头像位置
private refreshAvatarList(targetItem:AvatarListItem, currentPosition:egret.Point)
关键代码就是以上部分,其他的看注释应该都能明白。完整demo已经上传到个人资源,如果需要可以自行下载。
(注:不知道为什么,csdn的代码粘贴板不支持TS的代码格式,粘贴进去缩进格式简直不能看,只能一行一行手动调整,所以大段代码我没有贴进去,大家可以下载完整demo查看。还请见谅!)