如何实现类似WhatsApp的展开式Collapsing Toolbar图片模糊效果?
实现WhatsApp风格的Collapsing Toolbar标题遮罩效果
我懂你这种困扰!浅色背景图上的白色标题确实完全看不清,给整张图加tint又毁了原图的质感,WhatsApp那种只在标题区域做模糊/渐变遮罩的思路确实聪明多了。下面给你两种实用的实现方式,按需选择:
方法一:添加渐变遮罩层(简单易实现)
这种方式用渐变层覆盖在图片底部的标题区域,既不影响图片整体观感,又能让白色标题清晰显示,是最省心的方案:
- 修改你的CollapsingToolbarLayout布局,在加载图片的
ImageView上方添加一个遮罩View,放在底部对应标题的高度位置:
<com.google.android.material.appbar.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar" android:layout_width="match_parent" android:layout_height="match_parent" app:contentScrim="@color/colorPrimary" app:layout_scrollFlags="scroll|exitUntilCollapsed" app:title="你的标题"> <!-- Picasso加载的图片 --> <ImageView android:id="@+id/toolbar_image" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop"/> <!-- 渐变遮罩层 --> <View android:layout_width="match_parent" android:layout_height="120dp" <!-- 对应标题区域的高度,可按需调整 --> android:layout_gravity="bottom" android:background="@drawable/gradient_mask"/> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin"/> </com.google.android.material.appbar.CollapsingToolbarLayout>
- 在
res/drawable下创建gradient_mask.xml文件,定义从透明到半透明黑色的垂直渐变:
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <gradient android:startColor="@android:color/transparent" android:endColor="#80000000" <!-- 半透明黑色,透明度可调整 --> android:angle="90"/> </shape>
方法二:实现模糊背景层(贴近WhatsApp原生效果)
如果想要完全复刻WhatsApp的模糊效果,可以对图片的底部标题区域进行模糊处理,步骤如下:
- 首先在项目的
build.gradle(Module级别)中添加RenderScript依赖,用于模糊处理:
implementation "androidx.renderscript:renderscript:16.0.1"
- 修改布局,把遮罩View换成
ImageView,用来显示模糊后的图片区域:
<ImageView android:id="@+id/mask_view" android:layout_width="match_parent" android:layout_height="120dp" android:layout_gravity="bottom" android:scaleType="centerCrop"/>
- 在代码中监听Picasso的图片加载完成事件,裁剪图片底部区域并做模糊处理:
ImageView toolbarImage = findViewById(R.id.toolbar_image); ImageView maskView = findViewById(R.id.mask_view); Picasso.get() .load("你的网络图片URL") .into(toolbarImage, new Callback() { @Override public void onSuccess() { // 获取加载完成的图片Bitmap Bitmap originalBitmap = ((BitmapDrawable) toolbarImage.getDrawable()).getBitmap(); // 计算标题区域的高度(这里用120dp转成像素) int titleHeight = (int) getResources().getDimension(R.dimen.title_height); // 裁剪图片底部的标题区域 Bitmap croppedBitmap = Bitmap.createBitmap( originalBitmap, 0, originalBitmap.getHeight() - titleHeight, originalBitmap.getWidth(), titleHeight ); // 对裁剪后的区域进行模糊处理 Bitmap blurredBitmap = blurBitmap(croppedBitmap, YourActivity.this); // 设置到遮罩ImageView上 maskView.setImageBitmap(blurredBitmap); } @Override public void onError(Exception e) { // 处理图片加载失败的情况 } });
- 添加模糊处理的工具方法:
private Bitmap blurBitmap(Bitmap bitmap, Context context) { RenderScript rs = RenderScript.create(context); // 创建和原图尺寸一致的空白Bitmap用于存储模糊结果 Bitmap blurredBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); Allocation input = Allocation.createFromBitmap(rs, bitmap); Allocation output = Allocation.createFromBitmap(rs, blurredBitmap); // 初始化模糊脚本 ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); blurScript.setInput(input); blurScript.setRadius(25f); // 模糊程度,数值越大越模糊,建议15-30之间 blurScript.forEach(output); // 将模糊结果复制到目标Bitmap output.copyTo(blurredBitmap); rs.destroy(); return blurredBitmap; }
额外优化:根据折叠状态动态调整遮罩
可以监听AppBar的折叠状态,在Toolbar折叠时隐藏遮罩(此时标题在contentScrim上,不需要遮罩),展开时显示:
AppBarLayout appBarLayout = findViewById(R.id.app_bar); appBarLayout.addOnOffsetChangedListener((appBar, verticalOffset) -> { // 计算折叠百分比 float collapsePercentage = (float) Math.abs(verticalOffset) / appBar.getTotalScrollRange(); // 折叠时降低遮罩透明度,展开时恢复 maskView.setAlpha(1 - collapsePercentage); });
内容的提问来源于stack exchange,提问作者Kiran Suvarna
相关产品推荐
相关产品推荐

