iOS双滑块选择器
《SDRangeSliderView》
https://github.com/qddnovo/SDRangeSliderView
实现了通用性和便利性
今天是个好日子
最近项目需要进行价格区间选择,网上找到一个flutter带双滑块的进度条插件很好用,推荐给大家:
syncfusion_flutter_sliders: ^18.4.48-beta这个插件是国外一家知名插件公司提供的,功能强大,他们还有其他插件,比如图表,日历,等等,具体可在https://pub.flutter-io.cn/上搜索syncfusion,自行查看;今天说明一下双滑块的实现:
首先在yaml文件中导入本插件,然后在要用本插件的文件引用:
import 'package:syncfusion_flutter_sliders/sliders.dart';
然后使用代码:
SfRangeSlider( min: 0.0, max: 1000.0, values: _values, interval: 200, showTicks: true, showLabels: true, stepSize: 100, labelFormatterCallback: (dynamic actualValue, String formattedText) { return actualValue == 1000 ? '不限' : '¥$formattedText'; }, inactiveColor: Colors.grey[200], minorTicksPerInterval: 1, onChanged: (SfRangeValues values) { print("here is change!!!"); setState(() { _values = values; }); }, ),
其中属性:min是最小值,
max最大值,
values:是默认滑块所在位置格式为:
SfRangeValues _values = SfRangeValues(0.0, 800.0);interval是标签间隔,设为200 就是每个200显示一个标签,
showTicks是是否显示刻度,showLabels是是否显示标签,stepSize是最小滑动距离,labelFormatterCallback是对标签进行定制,上面的代码意思是标签值不是1000的话标签显示人民币符号加数字,如果是1000的话标签显示不限。inactiveColor:未选中的进度条颜色,或者说是进度条背景色。activeColor:选中范围的进度条颜色。minorTicksPerInterval:两个大刻度之间显示的小刻度数量。onChanged:变化监听。另外如果想要更多样式需要使用核心包:
syncfusion_flutter_core: ^18.4.48引用文件:
import 'package:syncfusion_flutter_core/theme.dart';代码:
SfRangeSliderTheme( data: SfRangeSliderThemeData( thumbColor: Colors.white, thumbRadius: 12, thumbStrokeWidth: 2, thumbStrokeColor: Colors.deepOrange), child: SfRangeSlider( min: 0.0, max: 1000.0, values: _values, interval: 200, showTicks: true, showLabels: true, stepSize: 100, labelFormatterCallback: (dynamic actualValue, String formattedText) { return actualValue == 1000 ? '不限' : '¥$formattedText'; }, inactiveColor: Colors.grey[200], minorTicksPerInterval: 1, onChanged: (SfRangeValues values) { print("here is change!!!"); setState(() { _values = values; }); }, ), ),
也就是在
SfRangeSlider外面套一层:SfRangeSliderTheme并在data中写入样式数据就可以了上面的代码主要修改滑块的样式,当然也可以修改其他样式。以下是实现的效果图:
RangeSlider 即双滑块slider,可以通过拖动两个滑块选择一个数值区间。实现步骤
(一)添加两个slider,并把背景以及fill设置为透明,并去除RaycastTarget
(二)在背景下添加个一个image,背景图为滑块划过后的填充图,添加的image为默认非填充图
解决问题
(一)双滑块需要在两个方向上更改图片大小,但是只要Pivot设定好了,更改尺寸只会同一规律更改。即无法实现随意更改某一滑块时,从一侧更改步骤(二)中创建的image。此时可以把image设置为宽度方向填充,通过更改anchor的min x与max x值来实现随意更改image宽度。
(二)数值锁定,即最大值滑块值比最小值滑块值小时,拖动失效,同理最小值滑块也要失效。实现方法,添加个一个类,继承slider重写set方法
下载地址
双滑块slider链接如下:https://download.csdn.net/download/qq_37833413/11872974使用时将Budget预制体拖到canvas下即可
转载于:https://www.cnblogs.com/xiaobajiu/p/8207326.html
在开发项目时,有需求要做双滑块的滑动条,本着能不造轮子就不造轮子的原则,去网上搜了一番,果然有Qt开源的拓展库,Qt Extension Library,有一个控件是QxtSpanSlider,实现了简单的双滑块,然而自定义了一些样式后,就各种问题。
于是又搜索相关的解决办法,有搜到一位博主的博客,url:https://blog.csdn.net/Ilson_/article/details/103960278,按照博主的方法,没有起左右,于是又花了C币去下载,还是不起作用。无奈之下,又不想自己写这个控件(我不会啊...),于是在源码基础上进行了一些猥琐的(聪明的)封装,最终实现了目的。
问题:
1、拖动一个滑块,另一个滑块轴的位置被span覆盖,而且两侧的颜色不特殊处理的话,颜色不对。sub-page和add-page要处理;
2、范围span的颜色控制不了,即使按照上面博主的修改方法也不行,当两侧的颜色有一定透明度时,也还是会把中间的span颜色蒙一层,UI走查的时候一眼就可以看出来,然后就该被深深低鄙视了,不就是改个颜色的事儿吗...
3、没有处理鼠标mousePressEvent,那么鼠标在某个位置按下的时候,滑块就不能定位到指定位置;
解决思路:
1、设置sub-page和add-page颜色一致,举例子:
QSlider#TwoSlider::sub-page:horizontal { background: rgba(0,0,0,0.1); height: 8px; border-radius: 4px; } QSlider#TwoSlider::add-page:horizontal { background: rgba(0,0,0,0.1); height: 8px; border-radius: 4px; }
2、代码里设置颜色(没成功...),具体修改位置如下:
void QxtSpanSliderPrivate::drawSpan(QStylePainter* painter, const QRect& rect) const { QStyleOptionSlider opt; initStyleOption(&opt); const QSlider* p = q_ptr; // area QRect groove = p->style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, p); if (opt.orientation == Qt::Horizontal) groove.adjust(0, 0, -1, 0); else groove.adjust(0, 0, 0, -1); // pen & brush /*painter->setPen(QPen(p->palette().color(QPalette::Dark).light(110), 0)); if (opt.orientation == Qt::Horizontal) setupPainter(painter, opt.orientation, groove.center().x(), groove.top(), groove.center().x(), groove.bottom()); else setupPainter(painter, opt.orientation, groove.left(), groove.center().y(), groove.right(), groove.center().y());*/ // 相交得到的矩形宽度、高度少了1pixel,需要加上 QRect rt = rect.intersected(groove); rt.adjust(0, -1, 1, 3); // 调用自写函数修改样式 setupPainter(painter, opt.orientation, rt); // draw groove painter->drawRect(rt); }
自己重载一个setupPainter函数
void QxtSpanSliderPrivate::setupPainter(QPainter* painter, Qt::Orientation orientation,QRect& rect) const { painter->setBrush(QBrush(QColor(255, 96, 0))); painter->setPen(Qt::transparent); }
这里面的颜色就是我想要的颜色。然而我这边的效果是被sub-page和add-page刷新覆盖了一层,如果sub-page和add-page是不带透明度的纯色,中间设置的就完全不起作用了。
而且,拖动一个滑块,另一个滑块坐标轴的位置就会被span覆盖掉,如果是同色还好,不同色的话就废了。
最后我想了一个猥琐的办法,增加三个单独的QWidget,覆盖在滑动条上,即两个滑块和中间span控件,并设置这个三个控件的显示不影响下面滑块控件的使用。 在滑动条刷新的时候,这三个控件也去更新位置和大小,效果还是相当完美的。
具体操作:
在滑块paintEvent里添加信号,方便我们封装的控件处理添加的三个控件
void QxtSpanSlider::paintEvent(QPaintEvent* event) { Q_UNUSED(event); QStylePainter painter(this); // groove & ticks QStyleOptionSlider opt; d_ptr->initStyleOption(&opt); opt.sliderValue = 0; opt.sliderPosition = 0; opt.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderTickmarks; painter.drawComplexControl(QStyle::CC_Slider, opt); // handle rects opt.sliderPosition = d_ptr->lowerPos; const QRect lr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); const int lrv = d_ptr->pick(lr.center()); opt.sliderPosition = d_ptr->upperPos; const QRect ur = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); const int urv = d_ptr->pick(ur.center()); emit updateHandle(lr,ur); ... ...
最后的信号即是我添加的,方便知道两个滑块的具体位置,然后在我封装的控件里,进行如下处理:
connect(m_Slider, &QxtSpanSlider::updateHandle, this, [this](const QRect &lRect, const QRect &uRect) { m_Span->move(lRect.x() + m_lWgdt->width(), 10); m_Span->setFixedWidth(uRect.x() - lRect.x()-m_lWgdt->width()); m_Span->raise(); for (auto &time : m_ValueVector) { time.circleWidget->raise(); } m_lWgdt->move(lRect.x(), lRect.y()); m_uWgdt->move(uRect.x(), uRect.y()); m_lWgdt->raise(); m_uWgdt->raise(); });
time.circleWidget无需理会,是我实现更复杂的功能需要显示和处理的。这样就解决了最头疼的第二个问题;
3、鼠标点击事件,这个就相对好处理多了。不过你得事先跟产品经理通个气,看看点击某个位置,两个滑块怎么处理,就比如在中间点一下,是小值变大,还是大值变小,blabla...
我最终实现的代码:
bool DoubleSlider::eventFilter(QObject *obj, QEvent *event) { if (obj == m_Slider) { if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); if (mouseEvent->button() == Qt::LeftButton) { double pos = mouseEvent->pos().x() / (double)m_Slider->width(); int value = (pos * (m_Slider->maximum() - m_Slider->minimum()) + m_Slider->minimum()) + 0.5; int lowerValue = m_Slider->lowerValue(); int upperValue = m_Slider->upperValue(); if (value < upperValue) { m_Slider->setLowerValue(value); } else if (value > upperValue) { m_Slider->setUpperValue(value); } } } } return QObject::eventFilter(obj, event); }
最后就完美的解决问题了,给大家看下最后的效果图吧:
代码有时间整理后上传。
原创不易,且行且珍惜: