响应状态的按钮图标
很多时候,设计要求当按钮的状态变化时(比如 hover、按下,禁用),按钮的背景、文字、图标能够改变颜色,以反馈按钮的当前状态。如下图:
改变文字、背景的颜色很容易,但是改变图标颜色就比较麻烦了。早期的方案,是提供一整套不同颜色图片,分别设置给不同的状态。
比如 iOS 上的 UIButton:
func setImage(UIImage?, for: UIControl.State)
// Sets the image to use for the specified state.
再比如 Android 上的 Drawable selector:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- pressed -->
<item android:drawable="@drawable/button_1_selected" android:state_pressed="true"/>
<!-- focused -->
<item android:drawable="@drawable/button_1_normal" android:state_focused="true"/>
<!-- default -->
<item android:drawable="@drawable/button_1_normal"/>
</selector>
所有一旦要新加或者改变一个图标,都要设计提供一组图片文件,开发一个个的加到代码项目中,再添加一组设置图片的代码,是特别的麻烦。
后来,一些平台提供了相关的解决方案,就是 tintColor。有了 tintColor, 只要一张图片就可以了,通过代码就可以改变图标的颜色。
这里我们主要针对 Qt 框架,对其他平台的 tintColor 有兴趣的话,可以查看下面给出的文档:
ImageView | Android Developers
给 Qml Image 增加 tintColor
在 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 就可以了。
实现 QQuickImageProvider
上面的关键是 "image://" 开头的 url,是通过 ImageProvider 实现的。
如何实现 QQuickImageProvider,在我的另一篇文章中有进过,所以不再重复了。
实现 tintColor 效果
在 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,可以不修改改像素,优化性能。