前面使用Qt 样式表实现滚动条,在实际工作中,发现存在一些瑕疵,例如如果在主窗口中绘制背景,则有可能给滚动条染色,还有如果想实现特殊的效果,则必须使用自定义风格,即从QStyle的子类派生出新的类型。以下从QProxyStyl派生出新的风格来实现自定义滚动(Qt4、Qt5均存在QCommonStyle、QProxyStyle,其余的风格在Qt5中已经不存在,不过原理一致)。
首先QScrollbar重绘时直接调用
drawComplexControl( ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget ) const
由此开始我们的自定绘制滚动条:
void MyStyle::drawComplexControl( ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget ) const { switch (control) { case CC_ScrollBar: drawScrollbar(control, option, painter, widget); return; default: break; } __super::drawComplexControl(control, option, painter, widget); }
绘制代码的实现(模拟QQ)
void MyStyle::drawScrollbar( ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget ) const { //QQ中滚动条的效果是:如果不在滚动条上,默认上下箭头不显示,AddPage, SubPage也不显示。 //如果在滚动条上,如果不在箭头所在的区域,则显示normal,否则显示hot,按下显示down。 //这里按这个标准实现。 const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option); if(!scrollbar) return; bool maxedOut = (scrollbar->maximum == scrollbar->minimum); if(maxedOut) return; painter->save(); painter->setRenderHint(QPainter::Antialiasing); QRect r = option->rect; const State state = option->state; const SubControls sub = option->subControls; State flags = option->state; if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow()) flags |= State_MouseOver; QRect drawRect; if (maxedOut) flags &= ~State_Enabled; const bool pressed = flags & State_Sunken; const bool mouseOver = flags & State_MouseOver; const bool isHorz = flags & State_Horizontal; const bool isRTL = option->direction == Qt::RightToLeft; QColor bkFillColor = Qt::white; QColor bkSliderNormal = Qt::lightGray; QColor bkSliderHover = Qt::gray; QColor bkSliderPress = Qt::darkGray; //清除所有子控件背景 //上下(左右)箭头可能和沟槽的背景不一样 drawRect= subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget); painter->fillRect(drawRect, bkFillColor); drawRect= subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget); painter->fillRect(drawRect, bkFillColor); //沟槽的颜色 drawRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarGroove, widget); painter->fillRect(drawRect, Qt::green); //在mouse over状态下,才绘制上下左右箭头。 //scrollbar->activeSubControls用于指定当前鼠标所在的子控件 const QStyle::SubControls sc = scrollbar->activeSubControls; if(!mouseOver) { //只绘制Slider,其余清除 drawRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); QPainterPath painterPath; painterPath.addRoundedRect(drawRect.left(), drawRect.top(), drawRect.width(), drawRect.height(), 4, 4); painter->fillPath(painterPath, bkSliderNormal); } else { //合并绘制AddPage SubPage drawRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarGroove, widget); painter->fillRect(drawRect, QColor(192, 192, 192, 127)); if (sub & SC_ScrollBarAddLine) //画加号,应该区分水平和垂直 { drawRect= subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget); QPixmap pixmap; if(isHorz) { if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowright_down.png"); else if(sc & SC_ScrollBarAddLine) pixmap.load(":/ScrollBar/scrollbar_arrowright_highlight.png"); else pixmap.load(":/ScrollBar/scrollbar_arrowright_normal.png"); } else { if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowdown_down.png"); else if(sc & SC_ScrollBarAddLine) pixmap.load(":/ScrollBar/scrollbar_arrowdown_highlight.png"); else pixmap.load(":/ScrollBar/scrollbar_arrowdown_normal.png"); } painter->drawPixmap(drawRect, pixmap); } if (sub & SC_ScrollBarSubLine) { drawRect= subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget); QPixmap pixmap; if(isHorz) { if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowleft_down.png"); else if(sc & SC_ScrollBarSubLine) pixmap.load(":/ScrollBar/scrollbar_arrowleft_highlight.png"); else pixmap.load(":/ScrollBar/scrollbar_arrowleft_normal.png"); } else { if(pressed) pixmap.load(":/ScrollBar/scrollbar_arrowup_down.png"); else if(sc & SC_ScrollBarSubLine) pixmap.load(":/ScrollBar/scrollbar_arrowup_highlight.png"); else pixmap.load(":/ScrollBar/scrollbar_arrowup_normal.png"); } painter->drawPixmap(drawRect, pixmap); } if (sub & SC_ScrollBarSlider) { drawRect = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); QPainterPath painterPath; painterPath.addRoundedRect(drawRect.left(), drawRect.top(), drawRect.width(), drawRect.height(), 4, 4); if(sc & SC_ScrollBarSlider) { painter->fillPath(painterPath, bkSliderPress); } else { painter->fillPath(painterPath, bkSliderHover); } } } painter->restore(); }
效果如下:
左为鼠标不在滚动条区域,右边为鼠标在滚动条区域
这里使用绿色做沟槽的背景,方便调试。