Juce实现Table自定义

article/2025/6/8 20:26:37

Juce实现Table自定义

一.总体展示概及概述

在项目中Juce中TableList往往无法满足用户需求,头部和背景及背景颜色设置以及在Cell中添加自定义按钮,所以需要自己实现自定义TabelList,该示例是展示实现自定义TableList,实现自定义标题头及Item,程序员在使用过程中可以自己修改颜色皮肤,按需求实现相关功能。总体展示示例如下:

请添加图片描述

二.实现步骤

1.实现自定义TableListModel

实现TableListModel的背景绘制,绘制单元格,及重新实现单元格生成的控件函数,绘制单元格边线。

#pragma once#include <JuceHeader.h>
#include "TabelLookAndFeel.h"
#include "TableLabel.h"
#include "TabelOperCell.h"
#include <vector>
using namespace juce;class CustomTableModel :public juce::TableListBoxModel,public Label::Listener,public TabelOperComponent::Listener
{
public:struct TableItemInfo{std::string str_time_;std::string str_name_;bool b_checked = false;TableItemInfo(const std::string& str_time, const std::string& str_name,bool bChecked=false):str_time_(str_time), str_name_(str_name),b_checked(bChecked){}};public:CustomTableModel(std::vector<TableItemInfo>& items):vec_table_item_(items){}~CustomTableModel(){}int getNumRows() override{return vec_table_item_.size();}void paintRowBackground(juce::Graphics& g, int rowNumber, int width, int height, bool rowIsSelected) override{// 绘制行背景颜色juce::Colour bgColour(2,75,96);// = juce::Colour(8, 82.120);/* rowNumber % 2 ? juce::Colours::white//: juce::Colour(0xfff8f8f8);*/if (rowIsSelected)bgColour = Colour(127,191,127);g.fillAll(bgColour);// 绘制行底部分隔线//g.setColour(Colour(52,110,127));//g.drawLine(0, height - 1, width, height - 1);}void paintCell(juce::Graphics& g, int rowNumber, int columnId, int width, int height, bool rowIsSelected) override{//if (columnId != 1) // 非复选框列{// 设置文本样式g.setFont(juce::Font(14.0f));//g.setColour(rowIsSelected ? juce::Colours::black : juce::Colours::darkgrey);g.setColour(Colours::white);if (vec_table_item_.size() > rowNumber){if (columnId == 1){g.drawText(std::to_string(rowNumber+1), juce::Rectangle<int>(0, 0, width, height),juce::Justification::centred, true);}//else if (columnId == 2)//    g.drawText(vec_table_item_[rowNumber].str_name_.c_str(), juce::Rectangle<int>(0, 0, width, height),//        juce::Justification::centred, true);else if (columnId == 3)g.drawText(vec_table_item_[rowNumber].str_time_.c_str(), juce::Rectangle<int>(0, 0, width, height),juce::Justification::centred, true);}}// 绘制方格线g.setColour(Colour(52, 110, 127));//g.drawRect(0, 0, width, height);//g.drawLine(0, 0, width, 0, 2); // 绘制上边线g.drawLine(0, 0, 0, height, 2); // 绘制左边线g.drawLine(0, height, width, height, 2);// 绘制下边线if (columnId == 4)g.drawLine(width, 0, width, height, 2); // 绘制右边线}juce::Component* refreshComponentForCell(int rowNumber, int columnId, bool isRowSelected,juce::Component* existingComponentToUpdate) override{if (columnId == 2) // 复选框列{TabelLabel* label = static_cast<TabelLabel*>(existingComponentToUpdate);if (label == nullptr){label = new TabelLabel();label->setRow(rowNumber);label->setText(vec_table_item_[rowNumber].str_name_.c_str(),dontSendNotification);label->setEditable(true, true, false);label->addListener(this);}if (rowNumber < data.size()){label->setText(data[rowNumber][columnId - 1], juce::dontSendNotification);}return label;}else if (columnId == 4){TabelOperComponent* oper = static_cast<TabelOperComponent*>(existingComponentToUpdate);if (oper == nullptr){oper = new TabelOperComponent();oper->setRow(rowNumber);}return oper;}return nullptr;}// 设置 TableListBox 指针void setTable(juce::TableListBox* tableListBox){table = tableListBox;}// 处理标签文本更改事件void labelTextChanged(juce::Label* label) override{TabelLabel* label_new = static_cast<TabelLabel*>(label);int row = label_new->getRow();}void itemClicked(TabelOperComponent* oper, int type) override{int row = oper->getRow();}
private:juce::Array<juce::StringArray> data;juce::TableListBox* table = nullptr;std::vector<TableItemInfo>& vec_table_item_;juce::Array<bool> toggleStates;//CustomToggleLookAndFeel customToggleLookAndFeel;JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CustomTableModel)
};
2.实现TableList的标题头

继承自LookAndFeel_V4 绘制Border,绘制列及绘制背景色

#pragma once
#include <JuceHeader.h>
using namespace juce;class CustomTableHeaderLookAndFeel : public juce::LookAndFeel_V4
{
public:void drawTableHeaderBackground(juce::Graphics& g, juce::TableHeaderComponent& header) override{// 绘制表头背景//auto color = ;auto color = Colours::black.fromFloatRGBA(0, 0, 0, 0.25);g.fillAll(color);}// 新增绘制表格边框的方法void drawTableBorder(juce::Graphics& g, juce::TableListBox& table){g.setColour(juce::Colour(52, 110, 127)); // 设置边框颜色juce::Rectangle<int> bounds = table.getLocalBounds();// 绘制上边框g.drawLine(bounds.getX(), bounds.getY(), bounds.getRight(), bounds.getY(), 1);// 绘制下边框//g.drawLine(bounds.getX(), bounds.getBottom(), bounds.getRight(), bounds.getBottom(), 1);// 绘制左边框//g.drawLine(bounds.getX(), bounds.getY(), bounds.getX(), bounds.getBottom(), 1);// 绘制右边框//g.drawLine(bounds.getRight(), bounds.getY(), bounds.getRight(), bounds.getBottom(), 1);}void drawTableHeaderColumn(juce::Graphics& g, juce::TableHeaderComponent& header,const juce::String& columnName, int columnId,int width, int height, bool isMouseOver,bool isMouseDown, int columnFlags) override{{// 其他列的文本绘制(与item文本样式一致)g.setFont(juce::Font(14.0f)); // 设置与item相同的字体大小g.setColour(juce::Colours::white);g.drawText(columnName, juce::Rectangle<int>(0, 0, width, height),juce::Justification::centred, true);}// 绘制列的边框线g.setColour(Colour(52, 110, 127)); // 设置边框线颜色//g.drawRect(0, 0, width, height, 1); // 绘制1px宽的边框线g.drawLine(0, 0, width, 0, 2); // 绘制上边线g.drawLine(0, 0, 0, height, 2); // 绘制左边线g.drawLine(0, height, width, height,2);if(columnId == 4)g.drawLine(width, 0, width, height, 2); // 绘制右边线}
};
3.在列中实现自定义button

在对应列中实现自定义component创建,如下所示:
请添加图片描述

对应自定义component的代码示例:简单实现了三个按钮,同学可以根据自己情况修改按钮,可以增加图标增加美观

class TabelOperComponent : public juce::Component,public juce::ToggleButton::Listener
{
public:static const int BTN_NUM = 3;class JUCE_API  Listener{public:/** Destructor. */virtual ~Listener() = default;virtual void itemClicked(TabelOperComponent* oper,int type) = 0;};void addListener(Listener* newListener) {const ScopedLock sl(listenerLock);listeners.addIfNotAlreadyThere(newListener);}void removeListener(Listener* listenerToRemove) {const ScopedLock sl(listenerLock);listeners.removeFirstMatchingValue(listenerToRemove);}TabelOperComponent();~TabelOperComponent() override {}void setRow(int row) { row_ = row; }int  getRow() { return row_; }void paint(juce::Graphics& g) override;void resized() override;virtual void buttonClicked(Button*) override;
private:int row_ = 0;CriticalSection listenerLock;Array<Listener*> listeners;juce::ToggleButton toggleButtons[BTN_NUM];JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TabelOperComponent)
};
#include "TabelOperCell.h"
TabelOperComponent::TabelOperComponent()
{for (int i = 0; i < 3; ++i){addAndMakeVisible(toggleButtons[i]);}
}
void TabelOperComponent::paint(juce::Graphics& g)
{//g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));auto area = getLocalBounds().toFloat();g.setColour(Colour(52, 110, 127));int width = area.getWidth();int height = area.getHeight();g.drawLine(0, 0, 0, height, 2); g.drawLine(width, 0, width, height, 2); g.drawLine(0, height, width, height, 2);
}void TabelOperComponent::resized()
{auto area = getLocalBounds();int width = (area.getWidth()-6)/3;for (auto& button : toggleButtons){button.setBounds(area.removeFromLeft(width).withSizeKeepingCentre(16,16));area.removeFromLeft(2);}
}void TabelOperComponent::buttonClicked(Button* ptr_btn)
{for (int i = 0; i < BTN_NUM; i++){if (ptr_btn == &toggleButtons[i]){ScopedLock lock(listenerLock);for (int j = listeners.size(); --j >= 0;)if (auto* l = listeners[j])l->itemClicked(this,i);}}
}
4.在MainCompoent中使用自定义TableList

使用Table和LookAndFeel并自定义了Item参数结构体,设置TableList的属性,并自定义添加Item条数。

请添加图片描述
请添加图片描述

请添加图片描述

示例代码如下:

#pragma once#include <JuceHeader.h>
#include "PresetTableComponent.h"
#include "TabelLookAndFeel.h"//==============================================================================
/*This component lives inside our window, and this is where you should put allyour controls and content.
*/
class MainComponent  : public juce::Component
{
public://==============================================================================MainComponent();~MainComponent() override;//==============================================================================void paint (juce::Graphics&) override;void resized() override;private://==============================================================================// Your private member variables go here...juce::TableListBox table_;CustomTableModel* table_model_ = nullptr;CustomTableHeaderLookAndFeel customLookAndFeel;std::vector<CustomTableModel::TableItemInfo> vec_table_item_;//TableDemoComponent table;JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
#include "MainComponent.h"//==============================================================================
MainComponent::MainComponent()
{//addAndMakeVisible(&table);table_.setLookAndFeel(&customLookAndFeel);table_.getHeader().addColumn("ID", 1, 10);table_.getHeader().addColumn("Name", 2, 10);table_.getHeader().addColumn("Time", 3, 10 * 4);table_.getHeader().addColumn("Operation", 4, 60);table_.getHeader().setSize(getWidth(), 32);table_.setColour(juce::TableListBox::backgroundColourId, Colour(2, 75, 96));table_.getViewport()->setScrollBarsShown(false, false);table_.getHeader().setPopupMenuActive(false);table_.getHeader().addMouseListener(this, false);table_.getHeader().setColumnVisible(7, false); // hide the "length" column until the user shows ittable_.setMultipleSelectionEnabled(true);addAndMakeVisible(&table_);setSize(500, 500);
}MainComponent::~MainComponent()
{
}//==============================================================================
void MainComponent::paint (juce::Graphics& g)
{// (Our component is opaque, so we must completely fill the background with a solid colour)g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));g.setFont (juce::Font (16.0f));g.setColour (juce::Colours::white);g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);
}void MainComponent::resized()
{auto area = getLocalBounds();if (table_model_){table_.setModel(nullptr);delete table_model_;table_model_ = nullptr;}vec_table_item_.clear();for (int i = 0; i < 10; i++){vec_table_item_.emplace_back("2025-04-17 13:56:00", "Name1");}if (!table_model_){table_model_ = new CustomTableModel(vec_table_item_);table_.setModel(table_model_);table_model_->setTable(&table_);}auto tab_area = area;int colum_with = tab_area.getWidth() / 8;table_.setRowHeight(32);table_.getHeader().setColumnWidth(1, tab_area.removeFromLeft(colum_with).getWidth()); table_.getHeader().setColumnWidth(2, tab_area.removeFromLeft(colum_with).getWidth());table_.getHeader().setColumnWidth(3, tab_area.removeFromLeft(4 * colum_with).getWidth());table_.getHeader().setColumnWidth(4, tab_area.getWidth());table_.getHeader().setSize(getWidth(), 32);table_.getHeader().getProperties().set("allSelected", false);table_.setBounds(area);
}

http://www.hkcw.cn/article/yrHXJWsElV.shtml

相关文章

VBA 64位API声明语句第010讲

跟我学VBA&#xff0c;我这里专注VBA, 授人以渔。我98年开始&#xff0c;从源码接触VBA已经20余年了&#xff0c;随着年龄的增长&#xff0c;越来越觉得有必要把这项技能传递给需要这项技术的职场人员。希望职场和数据打交道的朋友&#xff0c;都来学习VBA,利用VBA,起码可以提高…

【鸿蒙】HarmonyOS NEXT之如何正常加载地图组件

1、不支持模拟器&#xff0c;需要真机&#xff01; 2、Map地图需要在AGC上申请权限&#xff0c;需要在AGC上创建对应的项目 地址&#xff1a; AppGallery Connect 2.1 AGC中项目创建 2.1.1 添加项目 2.1.2 起个名字 2.1.3 添加应用&#xff1a; 2.1.4 选择HarmonyOS APP&…

精美的软件下载页面HTML源码:现代UI与动画效果的完美结合

精美的软件下载页面HTML源码&#xff1a;现代UI与动画效果的完美结合 在数字化产品推广中&#xff0c;一个设计精良的下载页面不仅能提升品牌专业度&#xff0c;还能显著提高用户转化率。本文介绍的精美软件下载页面HTML源码&#xff0c;通过现代化UI设计与丰富的动画效果&…

3. 简述node.js特性与底层原理

&#x1f63a;&#x1f63a;&#x1f63a; 一、Node.js 底层原理&#xff08;简化版&#xff09; Node.js 是一个 基于 Chrome V8 引擎构建的 JavaScript 运行时&#xff0c;底层核心由几部分组成&#xff1a; 组成部分简要说明 1.V8 引擎 将 JS 编译成机器码执行&#xff0…

【后端高阶面经:架构篇】51、搜索引擎架构与排序算法:面试关键知识点全解析

一、搜索引擎核心基石&#xff1a;倒排索引技术深度解析 &#xff08;一&#xff09;倒排索引的本质与构建流程 倒排索引&#xff08;Inverted Index&#xff09;是搜索引擎实现快速检索的核心数据结构&#xff0c;与传统数据库的正向索引&#xff08;文档→关键词&#xff0…

LayoutLM 模型文章总结

模型处理的文本图片样例&#xff1a; LayoutLM&#xff0c;一种简单而有效的文本和布局预训练方法&#xff0c;用于文档图像理解任务。BERT模型中输入的文本信息主要通过文本嵌入和位置嵌入来表示&#xff0c;LayoutLM 增加了两种输入嵌入&#xff1a; (1) 二维位置嵌入&…

低成本单节电池风扇解决方案WD8001

功能说明 1 、充电参数&#xff1a; 5V/500mA &#xff0c;满电 4.2V &#xff0c;充电指示灯为 LED4 &#xff0c;充电亮&#xff0c; 满电熄灭&#xff1b; 2 、工作电压&#xff1a; 2.7---4.2V,BAT 电压低于 2.7V &#xff0c;芯片禁止输出&#xff1b; 3 、工作说明&a…

6个月Python学习计划 Day 13 - 文件操作基础

第一周 Day 1 - Python 基础入门 & 开发环境搭建 Day 2 - 条件判断、用户输入、格式化输出 Day 3 - 循环语句 range 函数 Day 4 - 列表 & 元组基础 Day 5 - 字典&#xff08;dict&#xff09;与集合&#xff08;set&#xff09; Day 6 - 综合实战&#xff1a;学生信息…

解决IDEA插件使用Lombok找不到符号问题

https://juejin.cn/post/7013998800842784782 -Djps.track.ap.dependenciesfalse

应用智能化转型—MCP原理分析

当下AI风头正盛&#xff0c;许多行业都已经进入AI赋能的道路&#xff0c;无论是服务业、工业、还是软件行业。本篇文章我将以软件的智能化转型之MCP原理分析为主题讲解其具体实现方案 MCP我们都知道是一个当下非常火的模型上下文协议&#xff0c;它可以搭建出模型与业务之间的…

【R语言编程绘图-mlbench】

mlbench库简介 mlbench是一个用于机器学习的R语言扩展包&#xff0c;主要用于提供经典的基准数据集和工具&#xff0c;常用于算法测试、教学演示或研究场景。该库包含多个知名数据集&#xff0c;涵盖分类、回归、聚类等任务。 包含的主要数据集 BostonHousing 波士顿房价数据…

兼容老设备!EtherNet/IP转DeviceNet网关解决储能产线通讯难题

在新能源行业飞速发展的当下&#xff0c;工业自动化水平的高低直接影响着企业的生产效率与产品质量。JH-EIP-DVN疆鸿智能ETHERNET/IP和DEVICENET作为工业领域常用的通信协议&#xff0c;它们之间的转换应用在新能源生产线上发挥着关键作用。本文重点探讨ETHERNETIP从站转DEVICE…

实验设计与分析(第6版,Montgomery著,傅珏生译) 第10章拟合回归模型10.9节思考题10.12 R语言解题

本文是实验设计与分析&#xff08;第6版&#xff0c;Montgomery著&#xff0c;傅珏生译) 第10章拟合回归模型10.9节思考题10.12 R语言解题。主要涉及线性回归、回归的显著性、残差分析。 10-12 vial <- seq(1, 12, 1) Viscosity <- c(26,24,175,160,163,55,62,100,26,30…

【Ragflow】25.Ragflow-plus开发日志:excel文件解析新思路/公式解析适配

引言 RagflowPlus v0.3.0 版本中&#xff0c;增加了对excel文件的解析支持&#xff0c;但收到反馈&#xff0c;说效果并不佳。 以下测试文件内容来自群友反馈提供&#xff0c;数据已脱敏处理。 经系统解析后&#xff0c;分块效果如下&#xff1a; 可以看到&#xff0c;由于该…

SoloSpeech - 高质量语音处理模型,一键提取指定说话人音频并提升提取音频清晰度和质量 本地一键整合包下载

视频教程&#xff1a; 一个强大的语音分离和降噪软件 SoloSpeech 是由约翰霍普金斯大学、香港中文大学、南洋理工大学、清华大学及布拉格理工大学等多所高校共同主导开源的一个创新的语音处理项目&#xff0c;旨在解决在多人同时说话的环境中&#xff0c;准确提取并清晰呈现特定…

解锁Java多级缓存:性能飞升的秘密武器

一、引言 文末有彩蛋 在当今高并发、低延迟的应用场景中&#xff0c;传统的单级缓存策略往往难以满足性能需求。随着系统规模扩大&#xff0c;数据访问的瓶颈逐渐显现&#xff0c;如何高效管理缓存成为开发者面临的重大挑战。多级缓存架构应运而生&#xff0c;通过分层缓存设…

WinRAR 6.24 (64-bit) 的详细安装步骤(适用于 Windows 系统)

1. 下载安装文件 WinRAR下载链接&#xff1a;https://pan.quark.cn/s/7cc02bd4ebb5 2. 运行安装程序 双击下载的 WinRAR-6.24-final-x64.exe 文件。 若出现 用户账户控制&#xff08;UAC&#xff09; 弹窗&#xff0c;点击 “是” 允许安装。 3. 设置安装选项 ① 选择安装路…

YOLO12 改进|融入 Mamba 架构:插入混合模块Hybrid Module 像素和补丁双层面进行交互学习,提升小目标 多尺度

图像修复需平衡局部纹理还原与全局语义连贯。传统 CNN 受限于感受野&#xff0c;难以建模长程依赖&#xff1b;Transformer 虽能捕获全局交互&#xff0c;但二次计算复杂度使其在高分辨率场景效率低下&#xff0c;且分块处理易丢失细节。Mamba 作为高效序列模型&#xff0c;可线…

LangChain4j之AiService源码分析

这一节我们主要理解的逻辑为&#xff1a; 代理对象的创建流程代理对象的方法执行流程 代理对象的创建流程 创建代理对象是通过AiServices.create(Coder.JavaCoder.class, model)进行的&#xff0c;由于AiServices是一个抽象类&#xff0c;源码中有一个默认的子类DefaultAiSer…

多合一箱变保护测控装置,助力箱变实现“无人值守,少人值班”

箱式变压器&#xff08;简称“箱变”&#xff09;将传统变压器集中设计在箱式壳体中&#xff0c;因其结构紧凑、安装简单、运行稳定等优势被广泛应用于光伏及风电系统。但是&#xff0c;由于箱变安装位置偏远且分散、运行环境恶劣&#xff0c;箱内设备种类多、需要实时掌握运行…