Android ImageView模糊动画

时间:2014-07-18 13:22:01

标签: android animation imageview android-animation

我希望将模糊动画添加到ImageView,但设置持续时间。所以,例如,我希望图像随着时间的推移而模糊。

我已经有了模糊图像的方法,但我需要的是让它从模糊变为无模糊,比如2秒。

有人可以帮帮我吗?

编辑:这是我目前模糊图像的方法。

public Bitmap blur(Bitmap sentBitmap, int radius) {

    // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>

    Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);

    if (radius < 1) {
        return (null);
    }

    int w = bitmap.getWidth();
    int h = bitmap.getHeight();

    int[] pix = new int[w * h];
    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.getPixels(pix, 0, w, 0, 0, w, h);

    int wm = w - 1;
    int hm = h - 1;
    int wh = w * h;
    int div = radius + radius + 1;

    int r[] = new int[wh];
    int g[] = new int[wh];
    int b[] = new int[wh];
    int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
    int vmin[] = new int[Math.max(w, h)];

    int divsum = (div + 1) >> 1;
    divsum *= divsum;
    int dv[] = new int[256 * divsum];
    for (i = 0; i < 256 * divsum; i++) {
        dv[i] = (i / divsum);
    }

    yw = yi = 0;

    int[][] stack = new int[div][3];
    int stackpointer;
    int stackstart;
    int[] sir;
    int rbs;
    int r1 = radius + 1;
    int routsum, goutsum, boutsum;
    int rinsum, ginsum, binsum;

    for (y = 0; y < h; y++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        for (i = -radius; i <= radius; i++) {
            p = pix[yi + Math.min(wm, Math.max(i, 0))];
            sir = stack[i + radius];
            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);
            rbs = r1 - Math.abs(i);
            rsum += sir[0] * rbs;
            gsum += sir[1] * rbs;
            bsum += sir[2] * rbs;
            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }
        }
        stackpointer = radius;

        for (x = 0; x < w; x++) {

            r[yi] = dv[rsum];
            g[yi] = dv[gsum];
            b[yi] = dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];

            if (y == 0) {
                vmin[x] = Math.min(x + radius + 1, wm);
            }
            p = pix[yw + vmin[x]];

            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[(stackpointer) % div];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];

            yi++;
        }
        yw += w;
    }
    for (x = 0; x < w; x++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        yp = -radius * w;
        for (i = -radius; i <= radius; i++) {
            yi = Math.max(0, yp) + x;

            sir = stack[i + radius];

            sir[0] = r[yi];
            sir[1] = g[yi];
            sir[2] = b[yi];

            rbs = r1 - Math.abs(i);

            rsum += r[yi] * rbs;
            gsum += g[yi] * rbs;
            bsum += b[yi] * rbs;

            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }

            if (i < hm) {
                yp += w;
            }
        }
        yi = x;
        stackpointer = radius;
        for (y = 0; y < h; y++) {
            // Preserve alpha channel: ( 0xff000000 & pix[yi] )
            pix[yi] = ( 0xff000000 & pix[yi] ) | ( dv[rsum] << 16 ) | ( dv[gsum] << 8 ) | dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];

            if (x == 0) {
                vmin[y] = Math.min(y + r1, hm) * w;
            }
            p = x + vmin[y];

            sir[0] = r[p];
            sir[1] = g[p];
            sir[2] = b[p];

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[stackpointer];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];

            yi += w;
        }
    }

    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.setPixels(pix, 0, w, 0, 0, w, h);

    return (bitmap);
}

5 个答案:

答案 0 :(得分:11)

Android上的模糊效果总是很难。基本上你必须在外观和性能之间做出决定。模糊看起来越长越好,如果模糊本身不是瞬间的,那么你就无法真正为模糊设置动画。

原始模糊算法可以产生非常好的效果,但由于这一点,它也很慢,无法实现模糊动画。为了演示有效模糊这个图像需要什么,我通过缩放位图创建了一个简单的模糊动画:

public class BlurAnimation extends Animation {

    private final ImageView imageView;
    private final Bitmap bitmap;
    private final float startValue;
    private final float stopValue;
    private final float difValue;

    private BlurAnimation(ImageView imageView, Bitmap bitmap, int startValue, int stopValue) {
        this.imageView = imageView;
        this.bitmap = bitmap;
        this.startValue = startValue;
        this.stopValue = stopValue;
        this.difValue = stopValue - startValue;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);

        int current = (int)(this.difValue * interpolatedTime + this.startValue + 0.5f);
        Bitmap blurred = quickBlur(this.bitmap, current);
        this.imageView.setImageBitmap(blurred);
    }

    public Bitmap quickBlur(Bitmap bitmap, int factor) {
        if(factor <= 0) {
            return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
        }
        return Bitmap.createScaledBitmap(bitmap, bitmap.getWidth() / factor, bitmap.getHeight() / factor, true);
    }
}

这很有效(尽管仍有一些滞后),但结果无法与你的模糊算法相比,它看起来很糟糕:

enter image description here

所以你看,在模糊图像方面很难将性能和美观结合起来,但有一些选择首先是RenderScriptRenderScript非常快,并且内置高斯模糊滤镜。我从未使用它,但从我听到的可能是你的问题的解决方案。

你也可以尝试加载图像的已缩小版本,这将产生与上面的gif相同的效果,但会更快。缺点是在Animation中使用它会再次出现问题,但如果您只是需要模糊的图像并且您并不真正关心质量,那么您应该选择此选项。

您可以找到有关RenderScript和其他快速模糊选项in this answer

的更多信息

答案 1 :(得分:6)

我不会用代码过多地延长我的答案,因为老实说我没有它,但我会试着指出你必须考虑的关键事项和一些有用的链接。

首先,方法:

  • 尽可能快地模糊
  • 缓存模糊结果
  • 交叉衰落

尽可能快地模糊:

你的算法看起来非常好,我相信它确实产生了很好的效果,但实际情况是它在单个线程中在Java VM中运行。使用RenderScript,您绝对可以获得更好的性能。这是因为RenderScript会自动将渲染过程缩放到多处理器和GPU。

它的基本代码如下,直接取自android-developers.blogspot.com

RenderScript rs = RenderScript.create(theActivity);
ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(mRS, Element.U8_4(rs));;
Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
theIntrinsic.setRadius(25.f);
theIntrinsic.setInput(tmpIn);
theIntrinsic.forEach(tmpOut);
tmpOut.copyTo(outputBitmap);

缓存模糊结果:

那是因为Blur需要时间。此外还会有很多内存含义,你必须仔细观察。你肯定无法在回收视图和内容的滚动列表中做得很好。提示可能是使用Picasso库(LINK),并处理您的线程。像这样:

Picasso
   .with(context)
   .load(/* asset/res/file/url */)
   .transform(new BlurTransformation(value))
   .into(target);

// The transform method is automatically called by Picasso in a background thread
public class BlurTransformation implements com.squareup.picasso.Transformation {
    private final float radius;

    // radius is corner radii in dp
    public BlurTransformation(final float radius) {
        this.radius = radius;
    }

    @Override
    public Bitmap transform(final Bitmap source) {
       // do the transformation and return it here
    }

    @Override
    public String key() {
        return "blur"+Float.toString(radius);
    }
}

// you can also use this to get it instantly
Picasso... all the code... .get();

<强>交叉衰落:

这里是你在性能和漂亮外观之间取得平衡的地方。实际情况是,如果没有实际渲染每一帧的每一帧,就不可能实现真实动画,但是在几个关键帧之间交叉淡入淡出可以获得很好的效果。

您将使用多少个关键帧取决于您想要的性能,设备上的可用内存量等等。

如果您只使用2个关键帧(1个不模糊且1个完全模糊),您可以应用TransitionDrawable。如果你想要更精细的效果,你必须创建一系列交叉淡入淡出过渡或者可能创建自己的自定义绘图。

只有2个关键帧,您可以在Yahoo Weather app上看到一个示例,如果您希望查看一个包含几个关键帧的示例,请检查Muzei live wallpaper

非常有用的链接*

在以下链接中,您将看到Muzei创作者(Roman Nurik,也是Google为Android工作的工程师之一)关于他如何实现关键帧的一个很好的讨论,为什么这是唯一的方法做到这一点以及为什么,虽然代码更复杂,但它最终会产生更漂亮的结果:(简短版本)https://plus.google.com/+RomanNurik/posts/2sTQ1X2Cb2Z(完整版)https://medium.com/@romannurik/serendipitous-ideas-3a1721a6f716

此链接是Muzei动态壁纸的源代码,您可以在其中查看他如何计算关键帧并为壁纸设置动画:https://github.com/romannurik/muzei

祝你好运,编码愉快!

答案 2 :(得分:1)

模糊本身​​需要花费大量时间,即使在新设备上也是如此。动画模糊会更糟。 RenderScript也不会有太大帮助。最好的办法是在模糊和非模糊图像之间交叉淡入淡出:

package com.example.simon.crossfadeblur;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;

public class MainActivity extends Activity {

    public Bitmap blur(Bitmap sentBitmap, int radius) {

        // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>

        Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);

        if (radius < 1) {
            return (null);
        }

        int w = bitmap.getWidth();
        int h = bitmap.getHeight();

        int[] pix = new int[w * h];
        Log.e("pix", w + " " + h + " " + pix.length);
        bitmap.getPixels(pix, 0, w, 0, 0, w, h);

        int wm = w - 1;
        int hm = h - 1;
        int wh = w * h;
        int div = radius + radius + 1;

        int r[] = new int[wh];
        int g[] = new int[wh];
        int b[] = new int[wh];
        int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
        int vmin[] = new int[Math.max(w, h)];

        int divsum = (div + 1) >> 1;
        divsum *= divsum;
        int dv[] = new int[256 * divsum];
        for (i = 0; i < 256 * divsum; i++) {
            dv[i] = (i / divsum);
        }

        yw = yi = 0;

        int[][] stack = new int[div][3];
        int stackpointer;
        int stackstart;
        int[] sir;
        int rbs;
        int r1 = radius + 1;
        int routsum, goutsum, boutsum;
        int rinsum, ginsum, binsum;

        for (y = 0; y < h; y++) {
            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
            for (i = -radius; i <= radius; i++) {
                p = pix[yi + Math.min(wm, Math.max(i, 0))];
                sir = stack[i + radius];
                sir[0] = (p & 0xff0000) >> 16;
                sir[1] = (p & 0x00ff00) >> 8;
                sir[2] = (p & 0x0000ff);
                rbs = r1 - Math.abs(i);
                rsum += sir[0] * rbs;
                gsum += sir[1] * rbs;
                bsum += sir[2] * rbs;
                if (i > 0) {
                    rinsum += sir[0];
                    ginsum += sir[1];
                    binsum += sir[2];
                } else {
                    routsum += sir[0];
                    goutsum += sir[1];
                    boutsum += sir[2];
                }
            }
            stackpointer = radius;

            for (x = 0; x < w; x++) {

                r[yi] = dv[rsum];
                g[yi] = dv[gsum];
                b[yi] = dv[bsum];

                rsum -= routsum;
                gsum -= goutsum;
                bsum -= boutsum;

                stackstart = stackpointer - radius + div;
                sir = stack[stackstart % div];

                routsum -= sir[0];
                goutsum -= sir[1];
                boutsum -= sir[2];

                if (y == 0) {
                    vmin[x] = Math.min(x + radius + 1, wm);
                }
                p = pix[yw + vmin[x]];

                sir[0] = (p & 0xff0000) >> 16;
                sir[1] = (p & 0x00ff00) >> 8;
                sir[2] = (p & 0x0000ff);

                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];

                rsum += rinsum;
                gsum += ginsum;
                bsum += binsum;

                stackpointer = (stackpointer + 1) % div;
                sir = stack[(stackpointer) % div];

                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];

                rinsum -= sir[0];
                ginsum -= sir[1];
                binsum -= sir[2];

                yi++;
            }
            yw += w;
        }
        for (x = 0; x < w; x++) {
            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
            yp = -radius * w;
            for (i = -radius; i <= radius; i++) {
                yi = Math.max(0, yp) + x;

                sir = stack[i + radius];

                sir[0] = r[yi];
                sir[1] = g[yi];
                sir[2] = b[yi];

                rbs = r1 - Math.abs(i);

                rsum += r[yi] * rbs;
                gsum += g[yi] * rbs;
                bsum += b[yi] * rbs;

                if (i > 0) {
                    rinsum += sir[0];
                    ginsum += sir[1];
                    binsum += sir[2];
                } else {
                    routsum += sir[0];
                    goutsum += sir[1];
                    boutsum += sir[2];
                }

                if (i < hm) {
                    yp += w;
                }
            }
            yi = x;
            stackpointer = radius;
            for (y = 0; y < h; y++) {
                // Preserve alpha channel: ( 0xff000000 & pix[yi] )
                pix[yi] = ( 0xff000000 & pix[yi] ) | ( dv[rsum] << 16 ) | ( dv[gsum] << 8 ) | dv[bsum];

                rsum -= routsum;
                gsum -= goutsum;
                bsum -= boutsum;

                stackstart = stackpointer - radius + div;
                sir = stack[stackstart % div];

                routsum -= sir[0];
                goutsum -= sir[1];
                boutsum -= sir[2];

                if (x == 0) {
                    vmin[y] = Math.min(y + r1, hm) * w;
                }
                p = x + vmin[y];

                sir[0] = r[p];
                sir[1] = g[p];
                sir[2] = b[p];

                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];

                rsum += rinsum;
                gsum += ginsum;
                bsum += binsum;

                stackpointer = (stackpointer + 1) % div;
                sir = stack[stackpointer];

                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];

                rinsum -= sir[0];
                ginsum -= sir[1];
                binsum -= sir[2];

                yi += w;
            }
        }

        Log.e("pix", w + " " + h + " " + pix.length);
        bitmap.setPixels(pix, 0, w, 0, 0, w, h);

        return (bitmap);
    }

    public class FadeView extends FrameLayout {
        private long mFadeDelay = 1000;
        private ImageView mFirst;
        private ImageView mSecond;
        private boolean mFirstShowing;

        public FadeView(Context context) {
            super(context);
            init(context);
        }

        public FadeView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }

        public FadeView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context);
        }

        private void init(Context c){
            mFirst = new ImageView(c);
            mSecond = new ImageView(c);

            mFirst.setAlpha(1.0f);
            mSecond.setAlpha(0.0f);

            mFirstShowing = true;

            addView(mFirst);
            addView(mSecond);
        }

        public void setFadeDelay(long fadeDelay) {
            mFadeDelay = fadeDelay;
        }

        public void ShowImage(Drawable d){
            if(mFirstShowing){
                mSecond.setImageDrawable(d);
                mSecond.animate().alpha(1.0f).setDuration(mFadeDelay);
                mFirst.animate().alpha(0.0f).setDuration(mFadeDelay);
            }else {
                mFirst.setImageDrawable(d);
                mSecond.animate().alpha(0.0f).setDuration(mFadeDelay);
                mFirst.animate().alpha(1.0f).setDuration(mFadeDelay);
            }

            mFirstShowing = !mFirstShowing;
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final FadeView fw = new FadeView(this);
        setContentView(fw);

        fw.setOnClickListener(new View.OnClickListener() {

            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
            Bitmap blurredBitmap = blur(bitmap, 100);

            Drawable d1 = new BitmapDrawable(getResources(), blurredBitmap);
            Drawable d2 = getResources().getDrawable(R.drawable.image);
            boolean flag;

            @Override
            public void onClick(View view) {
                if(flag){
                    fw.ShowImage(d1);
                }else {
                    fw.ShowImage(d2);
                }
                flag = !flag;
            }
        });
    }

}

注意:交叉淡化代码取自:Source(点击动画)

我能想到的另一种方法是使用JQuery并在WebView内部设置模糊动画。

答案 3 :(得分:0)

imageview = (ImageView) itemView.findViewById(R.id.imageView);

        BitmapDrawable drawable = (BitmapDrawable) imageview.getDrawable();
        Bitmap bitmap = drawable.getBitmap();
        Bitmap blurred = blurRenderScript(bitmap, 25);
        imageview.setImageBitmap(blurred);   

//这些是模糊方法

@SuppressLint("NewApi")
    private Bitmap blurRenderScript(Bitmap smallBitmap, int radius) {

        try {
            smallBitmap = RGB565toARGB888(smallBitmap);
        } catch (Exception e) {
            e.printStackTrace();
        }


        Bitmap bitmap = Bitmap.createBitmap(
                smallBitmap.getWidth(), smallBitmap.getHeight(),
                Bitmap.Config.ARGB_8888);

        RenderScript renderScript = RenderScript.create(context);

        Allocation blurInput = Allocation.createFromBitmap(renderScript, smallBitmap);
        Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap);

        ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript,
                Element.U8_4(renderScript));
        blur.setInput(blurInput);
        blur.setRadius(radius); // radius must be 0 < r <= 25
        blur.forEach(blurOutput);

        blurOutput.copyTo(bitmap);
        renderScript.destroy();

        return bitmap;

    }

    private Bitmap RGB565toARGB888(Bitmap img) throws Exception {
        int numPixels = img.getWidth() * img.getHeight();
        int[] pixels = new int[numPixels];

        //Get JPEG pixels.  Each int is the color values for one pixel.
        img.getPixels(pixels, 0, img.getWidth(), 0, 0, img.getWidth(), img.getHeight());

        //Create a Bitmap of the appropriate format.
        Bitmap result = Bitmap.createBitmap(img.getWidth(), img.getHeight(), Bitmap.Config.ARGB_8888);

        //Set RGB pixels.
        result.setPixels(pixels, 0, result.getWidth(), 0, 0, result.getWidth(), result.getHeight());
        return result;
    }

答案 4 :(得分:0)

我参加聚会有点晚了,但是如果我没有理解你,你想要“淡化模糊”,那么它会不断增加?

您可以通过将两个ImageView堆叠在一起来实现此效果。下面的图像显示未触摸的图像,上面的图像是预先渲染的模糊图像。上面一个必须是不可见的(alpha = 0f)。接下来,淡入上部ImageView并淡出下部ImageView。这将给你想要的效果。您可以通过播放时间和transperancies来自定义效果。

祝你好运:)