ToB企服应用市场:ToB评测及商务社交产业平台

标题: Qml 实现瀑布流布局 [打印本页]

作者: 忿忿的泥巴坨    时间: 2024-9-10 17:20
标题: Qml 实现瀑布流布局
【写在前面】

最近在刷掘金的时候看到一篇关于瀑布流布局的文章,然鹅他们的实现都是前端的那套,就想着 Qml 有没有类似实现。
效果百度了一圈也没有( T_T Qml 凉了凉了 ),于是,我按照自己理解,简单实现了一个 Qml 版的瀑布流布局。
关于瀑布流:
瀑布流布局(Waterfall Layout),也被称为瀑布式布局或多栏自适应布局,是一种网页布局技能,它允许内容以多列的情势显示,类似于瀑布一样从上到下游动。这种布局方式特殊适合于展示图片或卡片式内容,如图片库、新闻摘要、商品列表等。
瀑布流布局的特点包括:
【正文开始】

一个经典的瀑布流布局来自小红书:

而我们实现的 Qml 版效果图如下:

现在开始讲解思路:
首先考虑屏幕宽度,竖屏两列,横屏可以三列或者更多,应当根据宽度动态改变,然后便可以盘算出列宽:
width: (flickable.width - flickable.spacing) / flickable.column
因此,实在未知的仅有卡片高度:

如图所示,卡片高度由三部分组成:【封面图片高度】+【标题高度】+【卡片信息高度】。
height: coverRealHeight + titleHeight + infoHeight
现在有了宽高,接下来只要盘算出 位置 (x, y) 即可:
  1.     if (flickable.currentColumn == flickable.column) {
  2.         flickable.currentColumn = 0;
  3.         flickable.currentX = 0;
  4.         for (let i = 0; i < flickable.column; i++) {
  5.             flickable.currentY[i] += flickable.prevHeight[i];
  6.         }
  7.     }
  8.     x = flickable.currentX;
  9.     y = flickable.currentY[flickable.currentColumn];
  10.     flickable.prevHeight[flickable.currentColumn] = Math.round(height + flickable.spacing);
  11.     print(flickable.currentColumn, flickable.currentX, flickable.prevHeight, flickable.currentY);
  12.     flickable.currentX += coverRealWidth + flickable.spacing;
  13.     flickable.currentColumn++;
  14.     let max = 0;
  15.     for (let j = 0; j < flickable.column; j++) {
  16.         max = Math.max(flickable.prevHeight[j] + flickable.currentY[j]);
  17.     }
  18.     flickable.contentHeight = max;
  19.    
复制代码
x 坐标盘算思路是:从左往右依次增长一个卡片宽度,到达本行最后一个卡片时置零即可。
y 坐标盘算思路是:记录下本行卡片高度数组 prevHeight[column],到达本行最后一个卡片时盘算下行卡片 y 坐标数组 currentY[column],而首行则为 0。
至此,Rect (x, y, width, height) 全部已知,我们可以直接利用 Repeater 轻松实例化出来:
[code]Repeater {    id: repeater    model: ListModel {        id: listModel        Component.onCompleted: {            flickable.loadMore();        }    }    delegate: Rectangle {        id: rootItem        width: (flickable.width - flickable.spacing) / flickable.column        height: coverRealHeight + titleHeight + infoHeight        radius: 4        clip: true        property real aspectRatio: coverWidth / coverHeight        property real coverRealWidth: width        property real coverRealHeight: width / aspectRatio        property real titleWidth: width        property real titleHeight: titleText.height        property real infoWidth: width        property real infoHeight: 50        Component.onCompleted: {            if (flickable.currentColumn == flickable.column) {                flickable.currentColumn = 0;                flickable.currentX = 0;                for (let i = 0; i < flickable.column; i++) {                    flickable.currentY += flickable.prevHeight;                }            }            x = flickable.currentX;            y = flickable.currentY[flickable.currentColumn];            flickable.prevHeight[flickable.currentColumn] = Math.round(height + flickable.spacing);            print(flickable.currentColumn, flickable.currentX, flickable.prevHeight, flickable.currentY);            flickable.currentX += coverRealWidth + flickable.spacing;            flickable.currentColumn++;            let max = 0;            for (let j = 0; j < flickable.column; j++) {                max = Math.max(flickable.prevHeight[j] + flickable.currentY[j]);            }            flickable.contentHeight = max;        }        Column {            Item {                id: coverPort                width: coverRealWidth                height: coverRealHeight                Image {                    anchors.fill: parent                    anchors.topMargin: rootItem.radius                    source: cover                }            }            Item {                id: titlePort                width: titleWidth                height: titleText.height                Text {                    id: titleText                    width: parent.width                    wrapMode: Text.WrapAnywhere                    text: title                    font.family: "微软雅黑"                    font.pointSize: 14                }            }            Item {                id: infoPort                width: infoWidth                height: infoHeight                RowLayout {                    anchors.fill: parent                    CircularImage {                        id: head                        Layout.preferredWidth: parent.height - 5                        Layout.preferredHeight: parent.height - 5                        Layout.leftMargin: 5                        Layout.alignment: Qt.AlignVCenter                        source: "file:/C:/Users/mps95/Desktop/head.jpg"                    }                    Text {                        Layout.fillWidth: true                        Layout.fillHeight: true                        text: "用户" + user                        font.pointSize: 14                        verticalAlignment: Text.AlignVCenter                        elide: Text.ElideRight                    }                    Text {                        Layout.preferredWidth: 100                        Layout.preferredHeight: parent.height                        Layout.rightMargin: 5                        text: (like ? "
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4