需求:
顶部导航栏有若干选项,可能很多,顶部区域不能完全展示,比如10个选项,界面一次只能展示五个,那么要求把后面的选项隐藏起来,并且,当点击第四个第五个按钮的时候,自动滑动到中间位置,后面的也滑动出来。
看下效果图
分析
这里面有几个点:
- 顶部导航栏的按钮会很多,要求能够正常隐藏那些显示不了的按钮
- 顶部导航栏的按钮宽度要自适应,也就是根据按钮中的文字宽度来调整按钮宽度
- 点击按钮实现滑动效果,注意,靠两边的按钮不滑动(比如第一个、第二个本身就应该靠边,无法滑动)
- 滑动效果要流畅
实现方式
#pragma once
#include <QtWidgets/QWidget>
#include "ui_topnavbar.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <QScrollArea>
#include <QScrollBar>
#include <QLabel>
#include <QScroller>
#include <QDebug>
#include <QList>
#include <QFontMetrics>
#include <QFont>
#include <QFile>
#include <QPropertyAnimation>
class TopNavBar : public QWidget
{
Q_OBJECT
public:
TopNavBar(QWidget *parent = nullptr);
~TopNavBar();
private:
void scrollToButton(QPushButton* button);// 导航栏滑动
void slotTopButtonSmoothScroll(QPushButton* button); // 导航栏平滑滑动
void slotButtonClicked(bool clicked);
void createText(QList<QString>& buttonText);
void slotTestButtonClicked(bool clicked);
void loadStyleSheet();
QList<QString> buttonText;
QScrollArea* _scrollArea;
QList<QPushButton*> _topButtons;
// QPushButton* testButton = nullptr;
QLabel* iconLabel;
QLabel* textLabel;
};
#include "topnavbar.h"
#include <QStyle>
TopNavBar::TopNavBar(QWidget *parent)
: QWidget(parent)
{
this->setMinimumHeight(500);
this->setMinimumWidth(800);
loadStyleSheet();
createText(buttonText);
// 创建一个滚动区域
_scrollArea = new QScrollArea(this);
_scrollArea->setWidgetResizable(true);// 内部控件自动填充滚动区域
_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);// 关闭垂直滚动条显示
_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);// 关闭水平滚动条显示
_scrollArea->setFixedHeight(40);// 固定高度
_scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);// 扩展策略
QHBoxLayout* layoutButton = new QHBoxLayout;
layoutButton->setSpacing(0);
layoutButton->setContentsMargins(0, 0, 0, 0);
// 创建一个容器用于放置按钮
QWidget* buttonContainer = new QWidget(_scrollArea);
buttonContainer->setLayout(layoutButton);
// 循环创建按钮并添加到布局中
for (int i = 0; i < buttonText.size(); ++i) {
QPushButton* pbutton = new QPushButton(buttonText[i], buttonContainer);
// 通过样式文件 控制button中文字与边界的边距
pbutton->setStyleSheet("background:rgb(225,225,225);margin: 0px;padding: 10px 24px;");
// 宽度自适应 按钮宽度最小为128 要求内部文字上下边距为10px 左右边距为24px
// 如果内部文字过长 则根据内部文字宽度 自适应调整按钮宽度
// 获取按钮的字体
QFont font = pbutton->font();
// 创建 QFontMetrics 对象
QFontMetrics metrics(font);
// 测量文本的宽度
int textWidth = metrics.horizontalAdvance(pbutton->text());
// 打印文本宽度
qDebug() << "Text width in pixels:" << textWidth;
int buttonWidth = textWidth + 48 > 128 ? textWidth + 48 : 128;
pbutton->setMinimumWidth(buttonWidth);
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
pbutton->setSizePolicy(sizePolicy);
// 高度扩展
pbutton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// 添加布局
layoutButton->addWidget(pbutton);
// 加入按钮列表
_topButtons.push_back(pbutton);
// 连接信号槽,当按钮被点击时,滚动到该按钮的位置
connect(pbutton, &QPushButton::clicked, [this, pbutton]() {
slotTopButtonSmoothScroll(pbutton);
});
// 当按钮被点击后 切换其样式 并设置其他未被选择的按钮的样式
connect(pbutton, &QPushButton::clicked, this, &TopNavBar::slotButtonClicked);
}
// 将容器设置为滚动区域的子窗口
_scrollArea->setWidget(buttonContainer);
QLabel* testLabel = new QLabel;
testLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// 垂直布局
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(_scrollArea);
layout->addWidget(testLabel);
this->setLayout(layout);
}
TopNavBar::~TopNavBar()
{}
// 没加动画 直接移动
void TopNavBar::scrollToButton(QPushButton* button) {
// 计算滚动条应该滚动到的位置
int scrollPosition = button->pos().x() - (_scrollArea->width() - button->width()) / 2;
// qDebug() << "scrollPosition:" << scrollPosition;
if (scrollPosition < 0)
{
scrollPosition = 0;
}
QScrollBar* scrollBar = _scrollArea->horizontalScrollBar();
scrollBar->setValue(scrollPosition);
}
// 加了动画 平滑滑动
void TopNavBar::slotTopButtonSmoothScroll(QPushButton* button) {
int scrollPosition = button->pos().x() - (_scrollArea->width() - button->width()) / 2;
QScrollBar* scrollBarH = _scrollArea->horizontalScrollBar();
qDebug() << "----------------------------------" ;
qDebug() << "button->pos().x():" << button->pos().x();
qDebug() << "scrollArea->width():" << _scrollArea->width();
qDebug() << "button->width():" << button->width();
qDebug() << "scrollBarH->minimum():" << scrollBarH->minimum();
qDebug() << "scrollBarH->maximum():" << scrollBarH->maximum();
scrollPosition = qBound(scrollBarH->minimum(), scrollPosition, scrollBarH->maximum());
QPropertyAnimation* smoothAnimation = new QPropertyAnimation(scrollBarH, "value");
smoothAnimation->setDuration(300); // 设置动画持续时间
smoothAnimation->setStartValue(scrollBarH->value());
smoothAnimation->setEndValue(scrollPosition);
smoothAnimation->start();// 启动动画
}
void TopNavBar::slotButtonClicked(bool clicked)
{
QPushButton* pButton = dynamic_cast<QPushButton*>(sender());
if (!pButton)
{
return;
}
for (auto button : _topButtons)
{
if (button == pButton)
{
button->setStyleSheet("background:gray;margin: 0px;padding: 10px 24px;");
}
else
{
button->setStyleSheet("background:rgb(225,225,225);margin: 0px;padding: 10px 24px;");
}
}
}
void TopNavBar::createText(QList<QString>& buttonText)
{
buttonText.push_back("Factory Reset Factory Reset");
buttonText.push_back("Update");
buttonText.push_back("Projects");
buttonText.push_back("L");
buttonText.push_back("AC Back");
buttonText.push_back("Fan Mode");
}