现在,越来越多的App里面使用了模糊效果,一些背景图片会根据手指上下移动的距离来进行不同程度的模糊,感觉甚为惊奇,毕竟大家都知道,在Android平台上进行模糊渲染是一个相当耗CPU也相当耗时的操作,一旦处理不好,卡顿是在所难免的。虽然我并不知道雅虎天气是怎么做出这种效果的,但是简单的模仿一下的话,还是能做到的。

一般来说,考虑到效率,渲染一张图片最好的方法是使用OpenGL,其次是使用C/C++,使用Java代码是最慢的。但是Android推出RenderScript之后,我们就有了新的选择,测试表明,使用RenderScript的渲染效率和使用C/C++不相上下,但是使用RenderScript却比使用JNI简单地多!同时,Android团队提供了RenderScript的支持库,使得在低版本的Android平台上也能使用。

不过在使用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 rs = RenderScript.create(context);

        ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));

        Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
        Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);

        blurScript.setRadius(BLUR_RADIUS);

        blurScript.setInput(tmpIn);

        blurScript.forEach(tmpOut);

        tmpOut.copyTo(outputBitmap);

        return outputBitmap;
    }
}

完成上面的代码后,需要在app的gradle文件中添加如下的支持:

defaultConfig {
    ......
    renderscriptTargetApi 19
    renderscriptSupportModeEnabled true
}

android ios动态模糊效果 安卓动态模糊软件_android ios动态模糊效果


android ios动态模糊效果 安卓动态模糊软件_ide_02

将图片模糊后,接下来要考虑的是怎么实现动态模糊效,有一点需要注意的是,即使我们使用了RenderScript这种高效的渲染方式,但是在实际测试中,渲染一张500*700分辨率的PNG格式图片,在我的Pro 6手机上,仍然需要50ms左右的时间(渲染最大模糊度的情况下),显然如果使用上面的代码进行实时渲染的话,会造成界面严重的卡顿。

既然实时渲染这条路走不通,那么就需要我们另辟蹊径了,我这里可以提供一种方法:先将图片进行最大程度的模糊处理,再将原图放置在模糊后的图片上面,通过不断改变原图的透明度(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();

        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);

        mTempBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.child);
        mFinalBitmap = BlurBitmap.blur(this, mTempBitmap);

        mBluredImage.setImageBitmap(mFinalBitmap);
        mOriginImg.setImageBitmap(mTempBitmap);
    }

    /**
     * 处理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"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.mzxy.mtxx.imageblurdemo.MainActivity">

    <ImageView
        android:id="@+id/activity_main_blured_img"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/child" />

    <ImageView
        android:id="@+id/activity_main_origin_img"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:orientation="vertical" />


    <SeekBar
        android:id="@+id/activity_main_seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/activity_main_progress_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="0"
        android:textSize="24sp" />

</RelativeLayout>

最终效果:

android ios动态模糊效果 安卓动态模糊软件_android_03


android ios动态模糊效果 安卓动态模糊软件_图片_04

哈哈,是不是感觉很好用很神奇!