列表自定义排序设计与实现

一、概述

列表自定义排序,在QQ音乐、网易云音乐等APP中是一个很常见的功能,用户收藏歌曲和歌单里的歌曲都可以通过拖动的实现自定义排序。

刚接到这个需求,要实现这个功能的时候,惯性思维会觉得不难,不就加个排序字段吗,有多难。但是仔细考虑之后,其实当中会引发许多问题。

二、功能问题点

1、排序值设计以及排序更改后如何变化

假设我们在列表的数据中增加一个position字段,以添加顺序1、2、3、4、5往下排,那么我想把第4位数据移动至第1位,就需要把第4位的排序值4改为1,把1、2、3位的排序值分别改为2、3、4。如此,才能满足需求。

上述方式中,每移动一个排序,就需要对许多数据进行修改,对数据库的修改太频繁了。如果数据量不大的情况下,可以考虑这种方式,数据量一大,很明显会有性能问题。

2、数据分页查询问题

拖动排序,一般都是在一个单独的页面进行排序,此页面包含了排序列表的全部数据。以用户收藏的歌曲举例,如果用户收藏2000条数据,难道我们要一次性全部查询出来?这明显不合理。

这样做,不仅会对服务器造成压力,对用户网络流量及手机性能的消耗也是巨大的。

三、解决方案

针对上述问题,在查询了许多资料及结合我们项目的特点和需求之后,笔者整理出一套比较适合于我们的方式。具体如下。

1、使用中值排序法

中值排序法,顾名思义,便是采用取中值的方式。

首先让两个相邻的数据之间的排序值,有一定的间隔,默认是2^16 = 65536,如:65536 * 1 = 6553665536 * 2 = 13107265536 * 3 = 196608

然后移动排序时,比如要将第3个数据一直1和2的中间,就取1和2的中值,计算公式如下。

中值计算公式

    // 移动到两个元素中间(倒序排序时)
    double newPosition = next.position + (prev.position - next.position) / 2;
    // 移动到第一个(倒序排序时)
    double newPosition = nowFirst.position + 65536
    // 移动到最后一个(倒序排序时)
    double newPosition = nowLast.position / 2

将得到的newPosition赋值给第3个数据,便完成了此次排序移动。

整个过程中,我们只对第3个数据的position进行了修改,不需要频繁对列表中的其他不参与排序的数据进行修改。

但是这种做法,在用户多次移动之后,难免会出现小数的情况,对于这种情况,我们需要对position字段进行重排操作。

即一旦判断newPosition出现小数,则依据现在的position排序结果,对position的字段进行重新赋值(赋初始值),此操作,可以另外创建一个线程来执行。以免影响用户排序接口的请求速度。

此处的计算应该在后端进行,避免前端恶意制造小数点,造成每次请求都进行重排。

2、数据分页查询

数据分页的话,我们可以先将数据正常分页查询出来,假设数据有5页,用户已经浏览到3页,此时用户如果点击排序,再去将剩余页的数据全部查询出来。

在数据查询期间,可以给用户一个提示,提示用户数据正在加载,然后数据全部请求完成之后,将数据带入排序页面即可。

四、前后端交互流程

前端在用户进行完排序之后,将用户更改的排序值的相对位置信息传到后端即可,由后端计算出新的position位置。

举个例子,假设有数据如下。

排序示例

// 初始化数据(数据库数据)
let list = [
    {
        id: 1004,
        position: 262144
    },
    {
        id: 1003,
        position: 196608
    },
    {
        id: 1002,
        position: 131072
    },
    {
        id: 1001,
        position: 65536
    }
]

// 此时想要将1001数据的位置移动到第 2 位,只要告诉后端相对位置即可。移动到第 2 位之后,1001的下一个是1003, 所以移动接口请求的参数如下:
let payload = {
    target: 1001,
    next: 1003
}
// 就是告诉后端要移动的目标(target)是1001,移动之后,1001的下一个(next)是1003

// 后端在接收到数据之后,根据前端传入的next查询当前next记录相邻的上一个,然后进行上述的公式计算即可算出新的位置信息。

// 排序后数据(数据库数据)
let list = [
    {
        id: 1004,
        position: 262144
    },
    {
        id: 1001,
        // 196608 + (262144 - 196608) / 2 = 229376
        position: 229376
    },
    {
        id: 1003,
        position: 196608
    },
    {
        id: 1002,
        position: 131072
    }
]
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值