Android-实现View模糊效果

首先,关于在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值创建渲染效果(或将其清空以将其删除),然后将其设置在图片布局模糊:

 

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
实现高斯模糊背景的对话框,可以使用 Android 的 DialogFragment 和 RenderScript 实现。以下是大致的步骤: 1. 在布局文件中定义对话框的 UI,注意要为对话框添加一个半透明的背景。 2. 创建一个继承自 DialogFragment 的类,并在其中实现 onCreateDialog() 方法。 3. 在 onCreateDialog() 方法中,使用 RenderScript 创建一个高斯模糊的 Bitmap,并将其设置为对话框背景。 4. 在对话框显示时,使用 DialogFragment.show() 方法显示对话框。 以下是示例代码: ```java public class BlurDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // 创建一个对话框 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); LayoutInflater inflater = getActivity().getLayoutInflater(); View view = inflater.inflate(R.layout.dialog_layout, null); builder.setView(view); // 创建一个 RenderScript 对象 RenderScript rs = RenderScript.create(getActivity()); // 加载图片资源 Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.background); // 创建一个高斯模糊的 Bitmap Bitmap blurredBitmap = image.copy(Bitmap.Config.ARGB_8888, true); Allocation input = Allocation.createFromBitmap(rs, image); Allocation output = Allocation.createFromBitmap(rs, blurredBitmap); ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); script.setInput(input); script.setRadius(25.f); script.forEach(output); output.copyTo(blurredBitmap); // 将高斯模糊的 Bitmap 设置为对话框背景 view.setBackground(new BitmapDrawable(getResources(), blurredBitmap)); return builder.create(); } } ``` 要实现毛玻璃效果的对话框,可以使用 Android 的 DialogFragment 和自定义控件实现。以下是大致的步骤: 1. 在布局文件中定义对话框的 UI,包括一个用于显示毛玻璃效果的自定义控件。 2. 创建一个继承自 DialogFragment 的类,并在其中实现 onCreateDialog() 方法。 3. 在 onCreateDialog() 方法中,创建一个自定义控件的实例,并将其添加到对话框的 UI 中。 4. 在对话框显示时,使用 DialogFragment.show() 方法显示对话框。 以下是示例代码: ```java public class GlassDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // 创建一个对话框 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); LayoutInflater inflater = getActivity().getLayoutInflater(); View view = inflater.inflate(R.layout.dialog_layout, null); builder.setView(view); // 创建一个自定义控件 GlassView glassView = new GlassView(getActivity()); ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); glassView.setLayoutParams(layoutParams); // 将自定义控件添加到对话框的 UI 中 FrameLayout frameLayout = view.findViewById(R.id.frame_layout); frameLayout.addView(glassView); return builder.create(); } } ``` 其中 GlassView 是一个自定义控件,用于显示毛玻璃效果。以下是示例代码: ```java public class GlassView extends View { private Paint paint = new Paint(); private Bitmap bitmap; public GlassView(Context context) { super(context); init(); } public GlassView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public GlassView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { // 加载图片资源 bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.background); // 设置画笔透明度 paint.setAlpha(100); } @Override protected void onDraw(Canvas canvas) { // 创建一个缩小的 Bitmap,用于实现毛玻璃效果 Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth() / 10, bitmap.getHeight() / 10, false); // 将缩小的 Bitmap 放大,实现模糊效果 Bitmap blurredBitmap = Bitmap.createScaledBitmap(scaledBitmap, getWidth(), getHeight(), false); // 绘制模糊的 Bitmap canvas.drawBitmap(blurredBitmap, 0, 0, paint); } } ``` 注意:以上代码只是示例代码,实际上还需要根据具体情况进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会写代码的猴子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值