当前位置: 代码网 > it编程>App开发>Android > Android利用绘制缓冲实现代码雨效果

Android利用绘制缓冲实现代码雨效果

2024年05月15日 Android 我要评论
前言看过很多代码雨的前端实现,却很少看到过android代码雨效果的实现,当然 open gl es的实现是有的。一个主要的原因是,在android canvas绘制时,很少有人考虑使用绘制缓冲,我们

前言

看过很多代码雨的前端实现,却很少看到过android代码雨效果的实现,当然 open gl es的实现是有的。一个主要的原因是,在android canvas绘制时,很少有人考虑使用绘制缓冲,我们之前有一篇文章,就实现了基于绘制缓冲的烟花效果,当然,本篇也是利用绘制缓冲来实现这种效果。

为什么需要绘制缓冲呢?原因如下:

绘制缓冲可以保留上次的绘制数据为下次绘制使用。

那,既然使用了绘制缓冲,我们理论上得使用surface,为什么这么说呢,因为setlayertype开启缓冲会影响到绘制效果,其次绘制也比较耗时,因此为了避免这种情况,使用surface是必要的优化手段。

当然,相较于上次的《基于绘制缓冲的烟花效果》,这一篇我们的内容其实并不多。

效果预览

我们可以看到,下雨的时候,拖尾部分会慢慢隐藏,主要技巧是多次覆盖半透明黑色,在视觉上反应的是渐渐变暗的alpha动画,上一篇的烟花效果也使用了这种技巧。

实现

下面是具体实现,在这个过程我们会使用到bitmap作为可视化缓冲,而我们知道,surface自带双缓冲,但是为什么不用呢?

原因是surface的双缓冲会导致雨滴闪烁,因为front和back会交替绘制,导致绘制效果不连续,因此,只能避免这种问题了。

绘制范围确定

在实现代码雨时,我们肯定要初始化画笔等,但是,一个需要思考的问题是,如何控制雨滴重复利用,其次如何知道绘制多大范围。

首先,我们要处理的一个问题是,需要下“多少行雨”,在这里我们可以测量字体,让屏幕宽度除以字体宽度,但是一般情况下字体宽度可以能不一致,一个比较合理的方案是,获取paint的textsize,当然,也可以遍历字符,获取最大的宽度或者高度。

// 雨滴行数
int columcount = (int) (getcontentwidth() / mpaint.gettextsize()); 

不过,我们看效果就能发现一个现象,文本绘制之后,位置是不变的,只有“雨滴头”似乎在移动,雨滴头也不会覆盖到原来的位置,因此,我们需要保存每行雨滴头的"高度的倍数"。

drops = new int[columcount];

文案

我们给一段简单的文案,重复绘制即可。

char[] characters = "张三爱李四,alice love you".tochararray();

绘制实现

下面是绘制逻辑的核心代码

将上一次的效果变暗

canvas.drawcolor(argb(0.1f, 0f, 0f, 0f));//加深暗度,使得以前绘制的区域变暗

让高度递增

雨肯定是要从上到下,那么雨滴的高度是需要递增的,另外,移出界面的当然要重置“高度的倍数”到0,不过这里为了保证随机性,我们加点随机变量

 if (drops[i] * textsize > height && math.random() > 0.975) {
      drops[i] = 0;  //重置高度的倍数
 }
  drops[i]++;

文本的绘制

文本的绘制比较随机了,下面绘制,但要保证高度为:当前高度 x textsize,宽度为 i x textsize

int index = (int) math.floor(math.random() * characters.length);
canvas.drawtext(characters, index, 1, i * textsize, drops[i] * textsize, paint);

下面是核心代码实现

if (build.version.sdk_int >= build.version_codes.m) {
    canvas = surface.lockhardwarecanvas();
} else {
    canvas = surface.lockcanvas(null);
}
drawchars(mbitmapcanvas, mpaint);
canvas.drawbitmap(mbitmapcanvas.getbitmap(), matrix, null);
surface.unlockcanvasandpost(canvas);

surfaceview使用

本篇我们使用了surfaceview,surfaceview性能好,但是兼容性差,不适合可滑动和需要调整大小的界面,当然,我们最需要注意的一点是,使用surfaceview时,是没有办法控制surface的销毁的,因此,在回调函数中,必须要加锁。

   public void surfacedestroyed(surfaceholder holder) {
        surface drawsurface = surface;
        if (drawsurface == null) {
            return;
        }
        synchronized (drawsurface) {
            isrunning = false;
        }
        if (drawthread != null) {
            try {
                drawthread.interrupt();
            } catch (throwable e) {
                e.printstacktrace();
            }
        }
        drawthread = null;
    }

性能优化

我们虽然使用了lockhardwarecanvas,但是,在大范围绘制还是有些卡顿,绘制缓冲是有性能损耗的,那么怎么才能优化呢?

绘制的优化手段当然是减少重绘,但是最重要的是减少绘制面积。

在这里,我们可以换一种思路,绘制等比例前缩小bitmap,绘制后在往surface绘制时放大bitmap,当然,这种会产生锯齿,不过,解决方法是有的,我们这里使用过滤工具,仅仅相应的优化即可。

mbitmappaint =  new textpaint(paint.anti_alias_flag);
mbitmappaint.setantialias(true);
mbitmappaint.setdither(true);

下面我们改造一下,缩小之后,绘制时使用matrix放大

canvas canvas = null;
if (sizechanged || drops == null) {
    if (mbitmapcanvas != null) {
        mbitmapcanvas.recycle();
    }
    //缩小绘制缓冲面积
    mbitmapcanvas = new bitmapcanvas(bitmap.createbitmap(getwidth() / 2, getheight() / 2, bitmap.config.rgb_565));
    int columcount = (int) (mbitmapcanvas.getbitmap().getwidth() / mpaint.gettextsize());
    drops = new int[columcount];
    arrays.fill(drops, 1);
    sizechanged = false;
}

if (build.version.sdk_int >= build.version_codes.m) {
    canvas = surface.lockhardwarecanvas();
} else {
    canvas = surface.lockcanvas(null);
}
drawchars(mbitmapcanvas, mpaint);

matrix.reset();
//放大
matrix.setscale(2, 2);

canvas.drawbitmap(mbitmapcanvas.getbitmap(), matrix, mbitmappaint);

surface.unlockcanvasandpost(canvas);

经过优化之后,流畅度当然会大幅提升。

总结

到这里本篇就结束了,上一篇,我们实现android上绘制缓冲的烟花效果,本篇,我们通过代码雨加深对绘制缓冲的理解,其次,也提供了优化方法,希望本篇内容对你有所帮助。

下面是源码:

public class coderainview extends surfaceview implements runnable, surfaceholder.callback {
    private textpaint mpaint;
    private textpaint mbitmappaint;

    private surface surface;
    private boolean sizechanged;
    private bitmapcanvas mbitmapcanvas;

    {
        initpaint();
    }

    public coderainview(context context) {
        this(context, null);
    }

    public coderainview(context context, @nullable attributeset attrs) {
        super(context, attrs);
        getholder().addcallback(this);
    }

    private void initpaint() {
        //否则提供给外部纹理绘制
        mpaint = new textpaint(paint.anti_alias_flag);
        mpaint.setantialias(true);
        mpaint.setdither(true);
        mpaint.setstrokecap(paint.cap.round);
        mpaint.setstyle(paint.style.fill);
        mpaint.settextsize(20);

        mbitmappaint =  new textpaint(paint.anti_alias_flag);
        mbitmappaint.setantialias(true);
        mbitmappaint.setdither(true);

        paintcompat.setblendmode(mpaint, blendmodecompat.plus);
    }


    thread drawthread = null;


    char[] characters = "张三爱李四,alice love you".tochararray();
    int[] drops = null;

    private volatile boolean isrunning = false;
    private final object locksurface = new object();

    matrix matrix = new matrix();

    @override
    public void run() {
        while (true) {
            synchronized (surface) {
                try {
                    thread.sleep(32);
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
                if (!isrunning || thread.currentthread().isinterrupted()) {
                    synchronized (locksurface) {
                        if (surface != null && surface.isvalid()) {
                            surface.release();
                        }
                        surface = null;
                    }
                    break;
                }


                canvas canvas = null;
                if (sizechanged || drops == null) {
                    if (mbitmapcanvas != null) {
                        mbitmapcanvas.recycle();
                    }
                    mbitmapcanvas = new bitmapcanvas(bitmap.createbitmap(getwidth() / 2, getheight() / 2, bitmap.config.rgb_565));
                    int columcount = (int) (mbitmapcanvas.getbitmap().getwidth() / mpaint.gettextsize());
                    drops = new int[columcount];
                    arrays.fill(drops, 1);
                    sizechanged = false;
                }

                if (build.version.sdk_int >= build.version_codes.m) {
                    canvas = surface.lockhardwarecanvas();
                } else {
                    canvas = surface.lockcanvas(null);
                }
                drawchars(mbitmapcanvas, mpaint);

                matrix.reset();
                matrix.setscale(2, 2);

                canvas.drawbitmap(mbitmapcanvas.getbitmap(), matrix, mbitmappaint);

                surface.unlockcanvasandpost(canvas);
            }
        }

    }

    @override
    public void surfacecreated(@nonnull surfaceholder holder) {
        this.drawthread = new thread(this);
        this.surface = holder.getsurface();
        this.isrunning = true;
        this.drawthread.start();
    }

    @override
    public void surfacechanged(surfaceholder holder, int format, int width, int height) {

    }

    @override
    public void surfacedestroyed(surfaceholder holder) {
        surface drawsurface = surface;
        if (drawsurface == null) {
            return;
        }
        synchronized (drawsurface) {
            isrunning = false;
        }
        if (drawthread != null) {
            try {
                drawthread.interrupt();
            } catch (throwable e) {
                e.printstacktrace();
            }
        }
        drawthread = null;
    }

    static class bitmapcanvas extends canvas {
        bitmap bitmap;

        public bitmapcanvas(bitmap bitmap) {
            super(bitmap);
            this.bitmap = bitmap;
        }

        public bitmap getbitmap() {
            return bitmap;
        }

        public void recycle() {
            if (bitmap == null || bitmap.isrecycled()) {
                return;
            }
            bitmap.recycle();
        }
    }

    void drawchars(canvas canvas, paint paint) {
        canvas.drawcolor(argb(0.1f, 0f, 0f, 0f));
        paint.setcolor(0xff00ff00);
        int height = getheight();
        float textsize = paint.gettextsize();

        for (int i = 0; i < drops.length; i++) {
            int index = (int) math.floor(math.random() * characters.length);
            canvas.drawtext(characters, index, 1, i * textsize, drops[i] * textsize, paint);
            if (drops[i] * textsize > height && math.random() > 0.975) {
                drops[i] = 0;
            }
            drops[i]++;
        }
    }

    public static int argb(float alpha, float red, float green, float blue) {
        return ((int) (alpha * 255.0f + 0.5f) << 24) |
                ((int) (red * 255.0f + 0.5f) << 16) |
                ((int) (green * 255.0f + 0.5f) << 8) |
                (int) (blue * 255.0f + 0.5f);
    }


}

以上就是android利用绘制缓冲实现代码雨效果的详细内容,更多关于android代码雨的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com