很多时候,设计要求当按钮的状态变化时(比如 hover、按下,禁用),按钮的背景、文字、图标能够改变颜色,以反馈按钮的当前状态。如下图:
改变文字、背景的颜色很容易,但是改变图标颜色就比较麻烦了。早期的方案,是提供一整套不同颜色图片,分别设置给不同的状态。
比如 iOS 上的 UIButton:
- func setImage(UIImage?, for: UIControl.State)
- // Sets the image to use for the specified state.
再比如 Android 上的 Drawable selector:
- "1.0" encoding="utf-8"?>
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:drawable="@drawable/button_1_selected" android:state_pressed="true"/>
-
- <item android:drawable="@drawable/button_1_normal" android:state_focused="true"/>
-
- <item android:drawable="@drawable/button_1_normal"/>
- selector>
所有一旦要新加或者改变一个图标,都要设计提供一组图片文件,开发一个个的加到代码项目中,再添加一组设置图片的代码,是特别的麻烦。
后来,一些平台提供了相关的解决方案,就是 tintColor。有了 tintColor, 只要一张图片就可以了,通过代码就可以改变图标的颜色。
这里我们主要针对 Qt 框架,对其他平台的 tintColor 有兴趣的话,可以查看下面给出的文档:
ImageView | Android Developers
在 Qt 中,目前并不支持 tintColor,所有还需要我们自己造轮子。
不过我们只打算在 Qml 中支持,如果你使用 QWidget,也可以参考我们的思路自己动手实现一个。
先看一下最终给使用者的控件:
- import QtQuick 2.12
- import QtQuick.Controls 2.5
-
- Image {
-
- property url originSource
- property string tintColor
-
- source: {
- if (Qt.colorEqual(tintColor, "transparent"))
- return originSource
- return "image://EffectSync/tint/")
- + encodeURIComponent(originSource)
- + "?tintColor=" + tintColor
- }
- }
-
使用者只需要设置 originSource 和 tintColor 就可以了。
上面的关键是 "image://" 开头的 url,是通过 ImageProvider 实现的。
如何实现 QQuickImageProvider,在我的另一篇文章中有进过,所以不再重复了。
在 QQuickImageProvider 中,拿到原始图片 image 后,我们直接就地修改 image 的像素,将所有颜色,替换为 tintColor 的 r、g、b 值:
- auto size = image.bytesPerLine();
- auto a = tintColor_.alpha();
- auto r = tintColor_.red() * a / 255;
- auto g = tintColor_.green() * a / 255;
- auto b = tintColor_.blue() * a / 255;
- for (int i = 0; i < image.height(); ++i) {
- auto bytes = image.scanLine(i);
- auto end = bytes + size;
- while (bytes < end) {
- if (bytes[3]) {
- a = bytes[3];
- if (a == 255) {
- bytes[0] = b;
- bytes[1] = g;
- bytes[2] = r;
- } else {
- bytes[0] = b * a / 255;
- bytes[1] = g * a / 255;
- bytes[2] = r * a / 255;
- }
- }
- bytes += 4;
- }
- }
注意,QImage 图片的像素一般是 premultiplied ,所以要乘以原始像素的 alpha 值。但是如果原始像素 alpha 为 0,可以不修改改像素,优化性能。