QML <4> Tableview 自定义表格显示 delegate FontAwesome Canvas
前言
基于QML TableView 的自定义表格显示,表格实现内容参考网易云音乐如下图所示:
参考实现功能自定义表格实现如下:
一、表格列设置
表格列为固定列数,代码如下;
TableViewColumn{role: "num_flag"; title: ""; width: 80; elideMode: Text.ElideRight;}
TableViewColumn{role: "name"; title: qsTr("标题"); width: 320; elideMode: Text.ElideRight;}
TableViewColumn{role: "time"; title: qsTr("时长"); width: 90;}
TableViewColumn{role: "singer"; title: qsTr("歌手"); width: 120;}
二、headerDelegate
表头使用delegate自定义绘制,代码如下:
Component {
id: header_delegate
Rectangle {
height: 40
color: "white"
border.width: 1
border.color: "gray"
Text {
id: headertext
anchors.centerIn: parent
text: styleData.value
color: "gray"
}
}
}
三、itemDelegate
itemDelegate为单元格绘制代理,根据列role 和Loader 返回不同delegate ,处理表格列之间的区分显示,为了Component 中可以访问styleData,故列delegate定义在Loader内部
tips:== 和===的区别
Component
{
id:item_delegate
Loader
{
id:item_loader
anchors.fill: parent
// visible: true
//== 、=== 区别
//https://blog.csdn.net/weixin_42420703/article/details/82531125
sourceComponent:
{
var role = styleData.role;
if (role === "num_flag")
return num_falg_component;
if (role === "name")
return name_component;
else if (role === "time")
return time_component;
else if (role === "singer")
return singercomponent;
else return empty_component;
}
1.排行delegate
第一列主要显示:行号,标记、上升、下降排名,如下图所示:
在这几个功能点实现上,使用Canvas 绘制,上下 趋势箭头使用Unicode字符,字符获取网址:
https://unicode-table.com/cn/blocks/
代码如下:
Component
{
id:num_falg_component
Rectangle
{
id:draw_rect
height: row_height
property var row:"1";
property var new_flag : false;
//false :down true :up
property var trend : true;
property var trend_num:11;
Canvas
{
id:draw
anchors.fill: parent
onPaint: {
var ctx = getContext("2d");
row = styleData.row+1;
if(row === 2 )
{
new_flag = true;
}
else if( row == 3 )
{
trend = false;
trend_num =6;
}
//行号
ctx.fillStyle = "gray";
ctx.lineWidth = 1;
ctx.font="12px Georgia";
ctx.fillText(row, 15,15,width/2);
//新
if(new_flag)
{
ctx.fillStyle = "green";
ctx.lineWidth = 3;
ctx.fillText("new", width/2,15,width/2);
}
else
{
var trend_text = String.fromCodePoint(0x2193);
var trend_color="blue";
if(trend)
{
trend_color = "red";
trend_text = String.fromCodePoint(0xA71B);
}
ctx.fillStyle = trend_color;
ctx.fillText(trend_text, width/2,15,width/3);
ctx.fillStyle = "red";
ctx.fillText(trend_num, width/2+10,15,width/3*2);
}
}
}
}
}
2.标题delegate
标题栏主要实现功能是只有前三行显示专辑图片,如图所示:
Component 代码如下:
Component
{
id:name_component
TableNameColumn
{
music_name_text: "As Long As You Love You"
image_show: styleData.row < 3 ? true:false
iamge_source: "qrc:/img/img/109951166294262909.jpg"
height: row_height
width:parent.width
}
}
自定义组件TableNameColumn 代码如下:
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.12
import "qrc:/scripts/Custom/ToolScripts.js" as JsTool
Rectangle
{
id:root
property string music_name_text: "";
property bool image_show: false;
property alias iamge_source:image.source
width: 300
height: 50
anchors.fill: parent
Image {
id: image
width: 45
height: 45
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 0
anchors.topMargin: 3
anchors.left: parent.left
anchors.top: parent.top
visible: image_show
}
Rectangle
{
id:music_name
width: 241
height: 45
anchors.left: image_show ?image.right :parent.left
anchors.top: parent.top
Button {
id: paly_canvas
background: Rectangle {
color: "white"
opacity: 0.2 //透明度
}
width: 18
height: 18
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
font.family: "FontAwesome"
text: "\uf01d"
}
Canvas
{
id:name_draw
x: 26
y: 0
width: 222
height: 45
anchors.verticalCenter: parent.verticalCenter
anchors.left: paly_canvas.right
onPaint:
{
var ctx = getContext("2d");
ctx.fillStyle = "rgba(54, 54, 54, 1)";
ctx.lineWidth = 2;
ctx.font="11px Georgia";
ctx.fillText(music_name_text,1, height/2,height);
}
}
}
}
3.时长delegate
除显示时长外,在鼠标所在当前行显示 添加 、 收藏、 分享、下载按钮如图所示:
使用StackView 实现,MouseArea 监听鼠标行为,图标使用FontAwesome,delegate 代码如下:
Component
{
id:time_component
TableTimeColumn
{
id:time_column
height: row_height
MouseArea
{
anchors.fill: parent
hoverEnabled :true
onEntered:
{
time_column.changeStackView(1)
}
onExited:
{
time_column.changeStackView(0)
}
}
}
}
自定义组件TableTimeColumn 代码如下:
import QtQuick 2.0
import QtQuick.Controls 2.14
import "qrc:/scripts/Custom/ToolScripts.js" as JsTool
Item
{
property int timesec: 60
StackView {
id: stack
initialItem: time_view
anchors.fill: parent
}
Component {
id: time_view
Canvas
{
id:time_text
anchors.centerIn: parent
onPaint: {
var ctx = getContext("2d");
JsTool.drawBackground(ctx,"white",width,height);
ctx.fillStyle = "rgba(54, 54, 54, 1)";
ctx.lineWidth = 2;
ctx.font="14px Georgia";
var str=JsTool.calTimeFromSecond(timesec);
JsTool.draw_text_show(ctx,width-15,height/2,1,str);
}
}
}
Component {
id: opt_view
Rectangle
{
anchors.centerIn: parent
Row
{
anchors.centerIn: parent
spacing:2
Button
{
id:plus
text: "\uf055"
font.family: "FontAwesome"
width:18
height: 18
background: Rectangle {
color: "white"
opacity: 0.2 //透明度
}
}
Button
{
id:btn_file
font.family: "FontAwesome"
text: "\uf0c7"
width:18
height: 18
background: Rectangle {
color: "white"
opacity: 0.2 //透明度
}
}
Button
{
font.family: "FontAwesome"
text: "\uf045"
width:18
height: 18
background: Rectangle {
color: "white"
opacity: 0.2 //透明度
}
}
Button
{
font.family: "FontAwesome"
text: "\uf019"
width:18
height: 18
background: Rectangle {
color: "white"
opacity: 0.2 //透明度
}
}
}
}
}
function changeStackView(index)
{
stack.pop()
if(index === 0 )
{
stack.push(time_view)
}
else
{
stack.push(opt_view)
}
}
}
4.歌手delegate
歌手列文字使用Canvas 绘制,文字长度超过宽度显示"…",如下图所示:
delegate 代码如下:
Component
{
id:singercomponent
TableSinglerColumn
{
height: row_height
id: text_singer
}
}
自定义组件TableSinglerColumn 代码如下:
import QtQuick 2.0
import "qrc:/scripts/Custom/ToolScripts.js" as JsTool
Rectangle
{
property string text: "未知sdfsdfkjdsfjdslkflkdsflkdsfjkdsfjdsk"
anchors.fill: parent
Canvas
{
anchors.fill: parent
onPaint: {
var ctx = getContext("2d");
ctx.fillStyle = "rgba(54, 54, 54, 1)";
ctx.lineWidth = 2;
ctx.font="14px Georgia";
var str= text
JsTool.draw_text_show(ctx,width-15,height/2,1,str);
}
}
}
四、行rowDelegate
排行前3显示专辑图片,故行高有变化,如下图所示:
TableView 的行高由rowDelegate 返回,代码如下:
property var image_row_height:40
property var row_height:30
rowDelegate: Rectangle
{
height: styleData.row < 3 ?image_row_height:row_height;
//border.color:"gray";
//color: styleData.alternate?"lightgray":"white"
}
总结
1 QML 使用FontAwesome
-
资源获取
FontAwesome 资源获取:http://www.fontawesome.com.cn/faicons/
下载后将ttf 文件添加到qrc文件中,
-
工程配置
main.cpp 中配置如下:
-
使用示例
text为 对应的十六进制数值,在css文件中可根据对应图标名称找到对应数值
Button使用图标代码如下:
Button
{
id:plus
text: "\uf055"
font.family: "FontAwesome"
width:18
height: 18
background: Rectangle {
color: "white"
opacity: 0.2 //透明度
}
}
2 function 函数封装
QML 中使用的function函数可单独写到*.js 文件中,在添加到qrc文件中,使用function时 在QML 文件中 import ,代码如下:
import "qrc:/scripts/Custom/ToolScripts.js" as JsTool
JsTool.draw_text_show(ctx,width-15,height/2,1,str);
- 工程中使用的function 函数如下:
//ctx 画布
// linewidth 行宽
// lineheight 行高
// maxline 最大行数
// str 显示字符串
function draw_text_show(ctx,linewidth,lineheight,maxline,str)
{
var strwidth = ctx.measureText(str).width;
if(strwidth < linewidth)
{
ctx.fillText(str, 5, lineheight);
}
else
{
var curwidth = 0;
var curline= 1;
var maskstr = '...';
var maskwidth = ctx.measureText(maskstr).width;
var curstr="";
var len = str.length;
for( var i = 0; i < str.length; ++i )
{
var fontWidth = ctx.measureText(str[i]).width;
var nextwidth = curwidth+fontWidth;
var draw_all = false;
//########
//####...
if(curline === maxline)
{
nextwidth += maskwidth;
if(nextwidth <= linewidth)
{
curwidth += fontWidth;
curstr += str[i];
if(i+1 < str.length)
{
continue;
}
}
else
{
curstr += maskstr;
draw_all = true;
}
}
else
{
if( nextwidth <= linewidth )
{
curwidth += fontWidth;
curstr += str[i]
continue;
}
}
ctx.fillText(curstr, 5, lineheight*curline);
curline++;
curwidth = 0;
curstr="";
if(draw_all)
{
break;
}
}
}
}
//根据秒数,计算时分
function calTimeFromSecond( timesecs)
{
var date= new Date(0,0,0,0,0,timesecs,0); //转换为Date对象
return Qt.formatDateTime(date, "mm:ss");
}
//播放按钮
function draw(ctx)
{
// 圆圈绘制
ctx.strokeStyle = "gray"
ctx.lineWidth = 1
ctx.beginPath()
ctx.arc(width/2,height/2,width/2,0,Math.PI*2)
ctx.stroke()
//三角形绘制
ctx.fillStyle = "gray"
ctx.strokeStyle = "gray"
ctx.beginPath()
ctx.moveTo(width/4, height/4)
ctx.lineTo(width/4, height/4*3)
ctx.lineTo(width/4*3+2, height/2)
ctx.closePath()
ctx.fill()
ctx.stroke()
}
//绘制背景
function drawBackground(ctx,color,width,height )
{
ctx.clearRect(0, 0, width, height)
ctx.strokeStyle = color;
ctx.fillStyle = color;
ctx.fillRect(0, 0,width, height)
ctx.stroke()
}