码迷,mamicode.com
首页 > 其他好文 > 详细

高斯模糊加图片展示(仿ipad qq图片点击展示)

时间:2016-05-13 02:59:06      阅读:240      评论:0      收藏:0      [点我收藏+]

标签:


1.前言

       项目中有需要点击展示大图的,然而发现手机端的图片展示更多的是黑色背景+图片展示,最多加上个缩放动画。更让强迫症纠结的是:因为android的本身的原因(从nexus到小米魅族),使用activity进行图片展示会有几率看到statusbar下滑消失动画(对的,没看错,有几率),单单是几率两字就让人纠结不已......随萌生出了些本博客代码——dialog+高斯模糊+图片展示的想法。

效果:

技术分享

录制看着卡,真机上流畅。


2.高斯模糊

       高斯模糊在苹果系统中很常见,然而在android系统中却鲜有闻,很多app对于高斯模糊的态度是能避免就避免的,究其原因很简单——费时,这里大致讲下高斯模糊的原理:假设一张100*100的图片,上边有10000个像素点,每个像素点作为中心点,按照高斯分布的概率算均值,也就是说,每多一个点,计算时间就会增加很多。闲话不叙,上例子:

技术分享

这是截的原图。然后,以30个像素点为半径进行高斯模糊:

技术分享

打印了下log,可以看出耗时是:I/need time: 671 .....(高斯模糊采用的是fastblur方法,如果嫌效率一般,可以考虑用jni,据说速度能降低一半,但是,放在这里的,还是不够)

然而图片并不能让我们满意,虽然有点模糊的意思了,但是感觉还是略微有点棱角......

好吧,那就将半径设为60,看看结果如何:

技术分享

只能说感觉还好吧,但是用了868ms,上一张我个人比较满意的150的高斯模糊:

技术分享

然而耗时1156ms.

如果以这种速度去处理图片那就很尴尬了,总不能在做相应操作的时候去等个1s多吧,虽然可以把图片处理放在子线程中,不影响主线程对其他任务的处理,然而这并不是一个靠谱的解决方案,所以决定去尝试下图片压缩:处理速度慢的根本原因是因为想像素数量多,如果我进行压缩呢?

嗯 先上个对比图:

技术分享

看着差别大么?

一个是长宽压缩过,并进行8像素半径高斯模糊的,一个是原图基础上150高斯模糊的,更喜欢哪个?

左边是原图的,右边是压缩的,压缩后的长宽是原来的十六分之一,更让人高兴的是,压缩+高斯模糊全过程持续了3ms......对没错,一个1156 一个3ms。然而唯一比较遗憾的是,android貌似没有局部截图的工具,如果自己获取整个页面再生成bitmap处理,整个生成bitmap在nexus5上就需要接近40ms,所以暂时来说,没想到有什么好的实时高斯模糊的办法。所以,高斯从淡到完全显示用的是原图上边铺上高斯模糊图,然后alpha动画走起

ok,进入今天的正题,高斯模糊+图片显示

3.高斯模糊背景的图片展示

    之前难点都讲的差不多了,直接上代码吧:

    

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.example.angusfine.mywidget.R;


/**
 * Created by AngusFine on 2016/3/22.
 */
public class ImagePreviewDialog extends Dialog implements View.OnClickListener{
    private final int animationTime = 250; //动画时间
    private Context context;
    private ImageView targetView; //目标ImageView
    private View mParentView; //DisplayFrame氛围内的RootView
    private int[]location = new int[2]; //targetView 屏幕坐标
    private Bitmap gaussBg; //mParentView高斯的结果
    private ImageView ivGauss;
    private ImageView ivShadow;
    private ImageView ivShow; //targetView展示
    private View currentView; //dialog对应的View
    private int currentW;
    private int currentH;
    private int displayH;
    private Rect rect; //displayframe对应的存储信息
    private boolean vertical = false;
    private boolean backable = true;
    float fY; //动画终点Y坐标
    float dY;
    float fX;
    float dX;

    /**
     *
     * @param context
     * 类中方法调用对应View的绘制顺序,不可修改
     */
    public ImagePreviewDialog(Context context){
        super(context, R.style.GaussDialog);
        this.context = context;
        Log.i("DecordhashCode",String.valueOf(((Activity)context).getWindow().getDecorView().hashCode()));
        ViewGroup decor = (ViewGroup) ((Activity)context).getWindow().getDecorView();
        Log.i("DecordhashCoded",String.valueOf(decor.hashCode()));
        for(int i = 0;i<decor.getChildCount();i++){
            if(decor.getChildAt(i)instanceof LinearLayout){
                ViewGroup ll = (ViewGroup)decor.getChildAt(i);
                for(int j = 0;j<ll.getChildCount();j++){
                    if (ll.getChildAt(j)instanceof FrameLayout){
                        this.mParentView = ll.getChildAt(j);
                        break;
                    }
                }
            }
        }
        initBg();
        getStatusBarInfo();
    }

    public ImagePreviewDialog setTargetView(ImageView view){
        ((Activity)context).getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
        targetView = view;
        getImageViewInfo();
        initTargetView();
        initGaussBitmap();
        initAnimation();
        return this;
    }

    /**
     * bg初始化
     */
    private void initBg(){
        currentView = LayoutInflater.from(getContext()).inflate(R.layout.dialog_gauss_bg_pic,null);
        setContentView(currentView);
        ivGauss = (ImageView) currentView.findViewById(R.id.iv_gauss);
        ivShadow = (ImageView) currentView.findViewById(R.id.iv_shadow);
        currentView.setOnClickListener(this);
    }

    /**
     * 获取展示View的对应信息
     */
    private void getImageViewInfo(){
        targetView.getLocationOnScreen(location);
        currentW = targetView.getMeasuredWidth();
        currentH = targetView.getMeasuredHeight();
    }


    /**
     * 状态栏信息
     */
    private void getStatusBarInfo(){
        rect = new Rect();
        ((Activity)context).getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
        displayH = ((Activity)context).getWindow().getDecorView().getMeasuredHeight();
    }

    /**
     * 高斯模糊
     */
    private void initGaussBitmap(){
        gaussBg = convertViewToBitmap(mParentView,mParentView.getWidth(),mParentView.getHeight());
        gaussBg = zoomImage(gaussBg,mParentView.getMeasuredWidth()/16,mParentView.getMeasuredHeight()/16);
        gaussBg = gaussBlur(gaussBg,5,true);
        ivGauss.setImageBitmap(gaussBg);
    }


    /**
     * 加载展示View
     */
    private void initTargetView(){
        FrameLayout.LayoutParams layoutParams= new FrameLayout.LayoutParams(currentW,currentH);
        ivShow = new ImageView(context);
        ivShow.setImageDrawable(targetView.getDrawable());
        ivShow.setX(location[0]);
        ivShow.setY(location[1]-rect.top);
        ivShow.setScaleType(targetView.getScaleType());
        ivShow.setLayoutParams(layoutParams);
        ((ViewGroup)currentView).addView(ivShow);
    }

    private void initAnimation(){
        float w = targetView.getMeasuredWidth();
        float h = targetView.getMeasuredHeight();
        float dw = rect.right - rect.left;
        final float dh = rect.bottom - rect.top;
        float ratio = h/w;
        float dRatio = dh/dw;
        final float maxRatio;
        backable = true;
        ivShadow.setAlpha(0f);
        if(ratio>dRatio){
            vertical = true;
            maxRatio = dh/h;
            fY = ((maxRatio-1)*h)/2;
            dY = location[1]-fY;
            fX = location[0]-dw/2+w/2;
        }else{
            maxRatio = dw/w;
            dY = location[1] - dh/2 +h/2;
            fX = ((maxRatio-1)*w)/2;
            dX = location[0]-fX;
        }
        final AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f,1.0f);
        alphaAnimation.setDuration(animationTime);
        alphaAnimation.setFillAfter(true);
        ObjectAnimator animator = ObjectAnimator.ofFloat(ivShow,"an",1.0f,maxRatio).setDuration(200);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float cVal = (Float) animation.getAnimatedValue();
                float YDRatio = (cVal-1)/(maxRatio-1);
                float dRatio = 1-YDRatio;
                if(vertical) {
                    ivShow.setScaleX(cVal);
                    ivShow.setScaleY(cVal);
                    ivShow.setX(location[0] - fX * (YDRatio));
                    ivShow.setY(dY * dRatio + fY-rect.top*dRatio);
                }else{
                    ivShow.setY(location[1]-rect.top*dRatio-dY*YDRatio);
                    ivShow.setX(dX*dRatio+fX);
                    ivShow.setScaleX(cVal);
                    ivShow.setScaleY(cVal);
                }
                ivShadow.setAlpha(YDRatio);
            }
        });
        ivGauss.startAnimation(alphaAnimation);
        animator.start();

    }

    private void backAnimation(){
        if(!backable)
            return;
        backable = false;
        float w = targetView.getMeasuredWidth();
        float h = targetView.getMeasuredHeight();
        float dw = rect.right - rect.left;
        final float dh = rect.bottom - rect.top;
        float ratio = h/w;
        float dRatio = dh/dw;
        final float maxRatio;
        if(ratio>dRatio){
            vertical = true;
            maxRatio = dh/h;
        }else{
            maxRatio = dw/w;
            fX = ((maxRatio-1)*w)/2;
            dX = location[0] - fX;
        }
        AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f,0f);
        alphaAnimation.setDuration(animationTime);
        alphaAnimation.setFillAfter(true);
        alphaAnimation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                ivShow.setVisibility(View.GONE);
                ImagePreviewDialog.super.dismiss();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        ObjectAnimator animator = ObjectAnimator.ofFloat(ivShow,"an",maxRatio,1f).setDuration(animationTime);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float cVal = (Float) animation.getAnimatedValue();
                float YDRatio = (cVal-1)/(maxRatio-1);
                float dRatio = 1-YDRatio;
                if(vertical) {
                    ivShow.setScaleX(cVal);
                    ivShow.setScaleY(cVal);
                    ivShow.setX(location[0] - fX * (YDRatio));
                    ivShow.setY(dY * dRatio + fY-rect.top*dRatio);
                }else{
                    ivShow.setY(location[1]-rect.top*dRatio-dY*YDRatio);
                    ivShow.setX(fX+dRatio*dX);
                    ivShow.setScaleX(cVal);
                    ivShow.setScaleY(cVal);
                }
                ivShow.setAlpha(YDRatio);
            }
        });
        animator.start();

        ivGauss.startAnimation(alphaAnimation);
        ivShadow.startAnimation(alphaAnimation);

    }

    @Override
    public void onBackPressed() {
        backAnimation();
    }

    /**
     * 图片缩放
     * @param targetBitmap 目标bitmap
     * @param newWidth  目标宽度
     * @param newHeight  目标高度
     * @return
     */
    private Bitmap zoomImage(Bitmap targetBitmap, double newWidth, double newHeight) {
        float width = targetBitmap.getWidth();
        float height = targetBitmap.getHeight();
        Matrix matrix = new Matrix();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        matrix.postScale(scaleWidth, scaleHeight);
        Bitmap bitmap = Bitmap.createBitmap(targetBitmap, 0, 0, (int) width, (int) height, matrix, true);
        return bitmap;
    }

    /**
     * View转Bitmap
     * @param view 目标View
     * @param bitmapWidth 宽度
     * @param bitmapHeight 高度
     * @return
     */
    private Bitmap convertViewToBitmap(View view, int bitmapWidth, int bitmapHeight){
        Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
        view.draw(new Canvas(bitmap));
        return bitmap;
    }

    /**
     * 高斯模糊
     * @param sentBitmap 目标bitmap
     * @param radius 高斯半径
     * @param canReuseInBitmap  是否原图修改
     * @return
     */
    private Bitmap gaussBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) {
        Bitmap bitmap;
        if (canReuseInBitmap) {
            bitmap = sentBitmap;
        } else {
            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];
        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++) {
                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;
            }
        }
        bitmap.setPixels(pix, 0, w, 0, 0, w, h);
        return (bitmap);
    }

    @Override
    public void onClick(View v) {
        backAnimation();
    }
}
然后布局代码:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <ImageView
        android:id="@+id/iv_gauss"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <ImageView
        android:id="@+id/iv_shadow"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/transparent_gray"/>

</FrameLayout>
以及style设置:
<style name="GaussDialog">
        <item name="android:windowBackground">@color/transparent</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowFullscreen">false</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:backgroundDimEnabled">false</item>
        <item name="android:background">#00000000</item>
    </style>

色值设置:

<color name="transparent_gray">#88000000</color>

嗯,使用方法很简单,只要两行代码:

imagePreviewDialog = new ImagePreviewDialog(this);
imagePreviewDialog.setTargetView(iv).show();

(第二行代码,需要在目标View显示后进行调用,否则获得的getmeasuredwidth以及高度会为0)

ok,这就完事了

其实这个版本很不完善,当时代码写的很急,后来也因为各种原因没在进行完善,功能上可以做不少的优化及扩展,以后有空再说吧
转帖请标明



高斯模糊加图片展示(仿ipad qq图片点击展示)

标签:

原文地址:http://blog.csdn.net/mcy456/article/details/51339149

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!