首先,关于在Android上绘图的一句话
在最基本的层面上,Android 上的可视元素(如按钮、文本和其他 UI 或自定义元素)通常是通过调用 Canvas API 绘制的,如 drawLine()()、drawText()、drawBitmap() 等drawText()。除非在自定义视图中绘制对象,否则代码可能不会直接调用这些方法,但在绘制应用程序中的 UI 组件时会代表您调用这些方法。
这些绘图命令中的大多数都向渲染系统提供三条信息。您可以将这些视为绘制的内容、位置和方式信息,操作本身的位置(要绘制的“基元”),对象的放置位置(位置和大小)在哪里,以及绘图属性集如何。正是这些属性让我们今天感到担忧,因为这就是模糊的用武之地。
绘图属性由Paint 对象提供,该对象具有可由调用方(您的应用程序或代表您的 UI 系统)更改的默认属性。大多数 Paint API 都相对简单明了,比如 setColor() 表示应该绘制基元的颜色,setColor() 表示对象是应该填充还是“setStyle()描边”(用于对象的轮廓),以及我不会在这里讨论的名副其实的大量文本属性。
还可以将更强大和更复杂的属性分配给 Paint 对象。其中包括ColorFilter子类(如我个人最喜欢的ColorMatrixColorFilter,它应该获得最长和最重复的ClassRerepeatitiveNameEver的奖项),用于更改基元和着色器的颜色。ColorFilter着色器包括各种渐变对象和位图,并提供源颜色,绘图操作从中采样以提供所绘制几何图形的结果颜色。例如,使用着色器可以填充线性或圆形渐变的矩形,或者使用位图中的值来填充或描边该矩形。(PS:Android 13 中有一个新的“着色器”API,它允许你超越这些效果;请继续关注本系列的下一篇文章以获取更多信息)。
渲染效果
RenderEffect 将RenderEffect一个或多个着色器捆绑在一起,并将它们应用于整体视图或 RenderNode(视图的底层渲染机制),通过让渲染器将这些RenderEffect效果应用于整个ViewViews来简化操作。View您可以使用单个渲染效果或将多个渲染效果链接在一起以应用多个RenderEffect。
在 API 级别 31 中引入 RenderEffect 时,它提供了将现有属性效果(如 ColorFilter 、Bitmap和Shader)收集到效果中以及将它们链接的方法,工厂方法如下:
static RenderEffect createBitmapEffect(Bitmap bitmap)
static RenderEffect createColorFilterEffect(ColorFilter colorFilter)
static RenderEffect createShaderEffect(Shader shader)
static RenderEffect createChainEffect(RenderEffect outer,
RenderEffect inner)
RenderEffect
也在此过程中引入了一种全新的绘图效果:Blur。.
Blurred Vision
除了封装现有 Paint 属性的对象之外,RenderEffect 还引入了一种新的效果,可以轻松模糊 View 或 RenderNode 内容:
static RenderEffect createBlurEffect(float radiusX, float radiusY,
Shader.TileMode edgeTreatment)
static RenderEffect createBlurEffect(float radiusX, float radiusY,
RenderEffect inputEffect,
Shader.TileMode edgeTreatment)
使用这些方法,您现在可以轻松地在View上创建模糊效果(或者,使用上面的第二个重载,另一个 RenderEffect),以便在渲染时模糊整个内容。可以将其视为通过过滤器发送视图的原始内容,该过滤器在此过程中模糊了视图。这基本上就是正在发生的事情,尽管它实现此目的的实际方法是将内容渲染到屏幕外,应用模糊,然后将模糊的结果复制到原始目的地。
radius参数确定模糊的大小(源输入中每个像素之外在每个方向上组合的像素数),而TileMode确定模糊边缘发生的情况。最后一个参数是必需的,因为模糊对正在计算的像素之外的像素起作用,因此它需要知道当这些其他像素位于输入内容之外时该怎么做。
创建模糊后,您可以通过调用以下命令将其设置为View:
View.setRenderEffect(renderEffect RenderEffect);
也可以在 RenderNode 上设置它:
RenderNode.setRenderEffect(renderEffect RenderEffect)
Demo
单击其中一张图片会放大它并显示该照片的标题。如果我们能模糊背景,这样当我们试图专注于前景图片及其标题时,其余图片就不会产生太多的视觉噪音,那不是很好吗?
我在应用程序中添加了一个 SeekBar 以允许动态更改模糊。这不是我在完成的应用程序中需要的东西(只需选择一个有效的模糊并坚持下去;用户不会想要修改这种东西,所以保持 UI 简单)。但我想最初使用它来处理不同的模糊,并展示如何使用不同的参数重新创建它们。seekBar 传入一个介于 0 到 50 之间的值(SeekBar UI 组件上的最小/最大值)。
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar, progress: Int,
fromUser: Boolean) {
updateEffect(progress.toFloat())
}
// start/stop overrides stubbed out...
})
updateEffect() 使用模糊半径的progress值(我对 x 和 y 使用相同的值)。重要说明:值 0 用于指示应删除模糊,这是通过将 RenderEffect 设置为 null 来完成的。事实证明,要求 0 半径模糊(数学上等同于不模糊)会崩溃。0 显然不是系统在请求模糊效果时期望的值。这是很少记录的(我们正在修复它...),所以我想你可能想知道,以防你在家里尝试这个。我知道我想知道当我的初始代码在尝试处理该值时崩溃时发生了什么。
fun updateEffect(progress: Float) {
if (progress > 0) {
val blur = RenderEffect.createBlurEffect(
progress, progress, Shader.TileMode.CLAMP)
pictureGrid.setRenderEffect(blur)
} else {
pictureGrid.setRenderEffect(null)
}
}
updateEffect() 使用半径的progress值创建渲染效果(或将其清空以将其删除),然后将其设置在图片布局模糊:
版权归原作者 不会写代码的猴子 所有, 如有侵权,请联系我们删除。