相信大家都发现了越来越多的 App 里面使用了模糊效果,比如雅虎天气的界面,虽然我并不知道雅虎天气是怎么做出这种效果的,但是简单的模仿一下的话,还是能做到的。下面一起来学习学习。
Android 是一种基于 Linux 的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由 Google 公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用 "安卓" 或 "安致"。
前言
雅虎天气的界面上滑的时候背景图片会跟着移动,最重要的是背景图片会根据手指上下移动的距离来进行不同程度的模糊,感觉甚为惊奇,毕竟大家都知道,在 Android 平台上进行模糊渲染是一个相当耗 CPU 也相当耗时的操作,一旦处理不好,卡顿是在所难免的。
一般来说,考虑到效率,渲染一张图片最好的方法是使用
,其次是使用 C++/C,使用 Java 代码是最慢的。但是 Android 推出
- OpenGL
之后,我们就有了新的选择,测试表明,使用
- RenderScript
的渲染效率和使用 C/C++ 不相上下,但是使用
- RenderScript
却比使用
- RenderScript
简单地多!同时,Android 团队提供了
- JNI
的支持库,使得在低版本的 Android 平台上也能使用。
- RenderScript
不过在使用
之前,对于模糊一张图片,需要注意的是,我们应该尽量不要使用原尺寸分辨率的图片,最好将图片缩小比例,这小渲染的效率要高一些。
- RenderScript
动态模糊的实现
如何使用
来模糊一张图片呢?废话不多说,先上核心代码:
- RenderScript
- public class BlurBitmap {
- /**
- * 图片缩放比例
- */
- private static final float BITMAP_SCALE = 0.4f;
- /**
- * 最大模糊度(在0.0到25.0之间)
- */
- private static final float BLUR_RADIUS = 25f;
- /**
- * 模糊图片的具体方法
- *
- * @param context 上下文对象
- * @param image 需要模糊的图片
- * @return 模糊处理后的图片
- */
- public static Bitmap blur(Context context, Bitmap image) {
- // 计算图片缩小后的长宽
- int width = Math.round(image.getWidth() * BITMAP_SCALE);
- int height = Math.round(image.getHeight() * BITMAP_SCALE);
- // 将缩小后的图片做为预渲染的图片。
- Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
- // 创建一张渲染后的输出图片。
- Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
- // 创建RenderScript内核对象
- RenderScript rs = RenderScript.create(context);
- // 创建一个模糊效果的RenderScript的工具对象
- ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
- // 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间。
- // 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去。
- Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
- Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
- // 设置渲染的模糊程度, 25f是最大模糊度
- blurScript.setRadius(BLUR_RADIUS);
- // 设置blurScript对象的输入内存
- blurScript.setInput(tmpIn);
- // 将输出数据保存到输出内存中
- blurScript.forEach(tmpOut);
- // 将数据填充到Allocation中
- tmpOut.copyTo(outputBitmap);
- return outputBitmap;
- }
- }
完成上面的代码后,需要在 app 的 gradle 文件中添加如下的支持:
- defaultConfig {
- ......
- renderscriptTargetApi 19
- renderscriptSupportModeEnabled true
- }
代码做了简单的注释以帮助理解,如果需要详细了解,可以查阅官方文档
然后,我们可以看一下模糊前和模糊后的效果对比:
将图片模糊后,接下来要考虑的是怎么实现动态模糊效,有一点需要注意的是,即使我们使用了
这种高效的渲染方式,但是在实际测试中,渲染一张 500*700 分辨率的 PNG 格式图片,在我的 Pro 6 手机上,仍然需要 50ms 左右的时间,显然如果使用上面的代码进行实时渲染的话,会造成界面严重的卡顿。
- RenderScript
既然实时渲染这条路走不通,那么就需要我们另辟蹊径了,我这里可以提供一种方法:先将图片进行最大程度的模糊处理,再将原图放置在模糊后的图片上面,通过不断改变原图的透明度 (Alpha 值)来实现动态模糊效果。
简单的代码如下:
- public class MainActivity extends AppCompatActivity {
- /**
- * 原始图片控件
- */
- private ImageView mOriginImg;
- /**
- * 模糊后的图片控件
- */
- private ImageView mBluredImage;
- /**
- * 进度条SeekBar
- */
- private SeekBar mSeekBar;
- /**
- * 显示进度的文字
- */
- private TextView mProgressTv;
- /**
- * 透明度
- */
- private int mAlpha;
- /**
- * 原始图片
- */
- private Bitmap mTempBitmap;
- /**
- * 模糊后的图片
- */
- private Bitmap mFinalBitmap;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- // 初始化视图
- initViews();
- // 获取图片
- mTempBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dayu);
- mFinalBitmap = BlurBitmap.blur(this, mTempBitmap);
- // 填充模糊后的图像和原图
- mBluredImage.setImageBitmap(mFinalBitmap);
- mOriginImg.setImageBitmap(mTempBitmap);
- // 处理seekbar滑动事件
- setSeekBar();
- }
- /**
- * 初始化视图
- */
- private void initViews() {
- mBluredImage = (ImageView) findViewById(R.id.activity_main_blured_img);
- mOriginImg = (ImageView) findViewById(R.id.activity_main_origin_img);
- mSeekBar = (SeekBar) findViewById(R.id.activity_main_seekbar);
- mProgressTv = (TextView) findViewById(R.id.activity_main_progress_tv);
- }
- /**
- * 处理seekbar滑动事件
- */
- private void setSeekBar() {
- mSeekBar.setMax(100);
- mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- mAlpha = progress;
- mOriginImg.setAlpha((int) (255 - mAlpha * 2.55));
- mProgressTv.setText(String.valueOf(mAlpha));
- }
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- }
- }
xml 布局文件代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:id="@+id/activity_main"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_weight="1"
- android:layout_height="0dp">
- <ImageView
- android:id="@+id/activity_main_blured_img"
- android:scaleType="centerCrop"
- android:src="@drawable/dayu"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
- <ImageView
- android:id="@+id/activity_main_origin_img"
- android:scaleType="centerCrop"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
- </FrameLayout>
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="80dp">
- <SeekBar
- android:layout_marginTop="@dimen/activity_vertical_margin"
- android:id="@+id/activity_main_seekbar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"/>
- <TextView
- android:id="@+id/activity_main_progress_tv"
- android:text="0"
- android:textSize="24sp"
- android:layout_gravity="center"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
- </LinearLayout>
效果如下:
怎么样?是不是很简单的样子?只需要调用模糊处理方法,并在 SeekBar 的滑动监听里面调用原图像的
方法,来实现动态模糊效果。
- setAlpha()
你以为这样就完了?不不不,我们的目的并不是这么单纯,哦,不对,并不是这么简单。还记得文章开头的时候说了吗?我们的终极目的是要简单地模仿一下雅虎天气的界面效果。
仿雅虎天气界面
有了上面的基础,就可以很容易地模仿雅虎天气的界面效果。简单来说,在上面制作出的效果基础上,有以下两点需要注意的地方:
需要要监听滑动事件,然后再将背景图片调用
方法,将图片向上平移一段距离。 要向上平移图片,还需要手动增加图片的高度,不然图片向上平移后,底部就会有留白。设置图片高度的核心代码如下:
- setTop()
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- Point point = new Point();
- display.getSize(point);
- // 获取到ImageView的高度
- int height = point.y;
- ViewGroup.LayoutParams params = imageView.getLayoutParams();
- params.width = ViewGroup.LayoutParams.MATCH_PARENT;
- // 将ImageView的高度增加100
- params.height = height + 100;
- // 应用更改设置
- imageView.requestLayout();
完成上面两点的内容后,基本就可以模仿出雅虎天气的首页了。
结合第一个例子的 demo,效果如下:
总结
以上就是本文的全部内容了,实现后的效果是不是很赞呢?感兴趣的朋友快快自己动手操作起来吧,希望本文对大家开发 Android 能有所帮助。
来源: http://www.phperz.com/article/17/0314/290851.html