最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android利用繪制緩沖實現代碼雨效果

 更新時間:2024年03月18日 08:50:00   作者:時光少年  
看過很多代碼雨的前端實現,卻很少看到過Android代碼雨效果的實現,當然 open gl es的實現是有的,一個主要的原因是,在Android Canvas繪制時,很少有人考慮使用繪制緩沖,所以本文將給大家介紹Android如何利用繪制緩沖實現代碼雨效果,需要的朋友可以參考下

前言

看過很多代碼雨的前端實現,卻很少看到過Android代碼雨效果的實現,當然 open gl es的實現是有的。一個主要的原因是,在Android Canvas繪制時,很少有人考慮使用繪制緩沖,我們之前有一篇文章,就實現了基于繪制緩沖的煙花效果,當然,本篇也是利用繪制緩沖來實現這種效果。

為什么需要繪制緩沖呢?原因如下:

繪制緩沖可以保留上次的繪制數據為下次繪制使用。

那,既然使用了繪制緩沖,我們理論上得使用Surface,為什么這么說呢,因為setLayerType開啟緩沖會影響到繪制效果,其次繪制也比較耗時,因此為了避免這種情況,使用Surface是必要的優(yōu)化手段。

當然,相較于上次的《基于繪制緩沖的煙花效果》,這一篇我們的內容其實并不多。

效果預覽

我們可以看到,下雨的時候,拖尾部分會慢慢隱藏,主要技巧是多次覆蓋半透明黑色,在視覺上反應的是漸漸變暗的alpha動畫,上一篇的煙花效果也使用了這種技巧。

實現

下面是具體實現,在這個過程我們會使用到Bitmap作為可視化緩沖,而我們知道,Surface自帶雙緩沖,但是為什么不用呢?

原因是Surface的雙緩沖會導致雨滴閃爍,因為front和back會交替繪制,導致繪制效果不連續(xù),因此,只能避免這種問題了。

繪制范圍確定

在實現代碼雨時,我們肯定要初始化畫筆等,但是,一個需要思考的問題是,如何控制雨滴重復利用,其次如何知道繪制多大范圍。

首先,我們要處理的一個問題是,需要下“多少行雨”,在這里我們可以測量字體,讓屏幕寬度除以字體寬度,但是一般情況下字體寬度可以能不一致,一個比較合理的方案是,獲取Paint的textSize,當然,也可以遍歷字符,獲取最大的寬度或者高度。

// 雨滴行數
int columCount = (int) (getContentWidth() / mPaint.getTextSize()); 

不過,我們看效果就能發(fā)現一個現象,文本繪制之后,位置是不變的,只有“雨滴頭”似乎在移動,雨滴頭也不會覆蓋到原來的位置,因此,我們需要保存每行雨滴頭的"高度的倍數"。

drops = new int[columCount];

文案

我們給一段簡單的文案,重復繪制即可。

char[] characters = "張三愛李四,Alice love you".toCharArray();

繪制實現

下面是繪制邏輯的核心代碼

將上一次的效果變暗

canvas.drawColor(argb(0.1f, 0f, 0f, 0f));//加深暗度,使得以前繪制的區(qū)域變暗

讓高度遞增

雨肯定是要從上到下,那么雨滴的高度是需要遞增的,另外,移出界面的當然要重置“高度的倍數”到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;
    }

性能優(yōu)化

我們雖然使用了lockHardwareCanvas,但是,在大范圍繪制還是有些卡頓,繪制緩沖是有性能損耗的,那么怎么才能優(yōu)化呢?

繪制的優(yōu)化手段當然是減少重繪,但是最重要的是減少繪制面積。

在這里,我們可以換一種思路,繪制等比例前縮小Bitmap,繪制后在往Surface繪制時放大Bitmap,當然,這種會產生鋸齒,不過,解決方法是有的,我們這里使用過濾工具,僅僅相應的優(yōu)化即可。

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

經過優(yōu)化之后,流暢度當然會大幅提升。

總結

到這里本篇就結束了,上一篇,我們實現Android上繪制緩沖的煙花效果,本篇,我們通過代碼雨加深對繪制緩沖的理解,其次,也提供了優(yōu)化方法,希望本篇內容對你有所幫助。

下面是源碼:

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代碼雨的資料請關注腳本之家其它相關文章!

相關文章

  • Android 開機應用掃描相關總結

    Android 開機應用掃描相關總結

    本篇文章只是作為指南引導去看PkMS,不會貼大段代碼進行分析,更多是基于方法分析實現的邏輯,另外就是代碼是基于Android 11,與Android 10之前代碼有比較大的差別。
    2021-05-05
  • Android開發(fā)之基于DialogFragment創(chuàng)建對話框的方法示例

    Android開發(fā)之基于DialogFragment創(chuàng)建對話框的方法示例

    這篇文章主要介紹了Android開發(fā)之基于DialogFragment創(chuàng)建對話框的方法,結合實例形式分析了DialogFragment創(chuàng)建對話框的具體功能與布局相關實現技巧,需要的朋友可以參考下
    2017-08-08
  • Android通過LIstView顯示文件列表的兩種方法介紹

    Android通過LIstView顯示文件列表的兩種方法介紹

    過ListView顯示SD卡中的文件列表一共有兩種方法,一是:通過繼承ListActivity顯示;二是:利用BaseAdapter顯示,具體實現如下,感興趣的朋友可以參考下哈
    2013-06-06
  • Android和JavaScript相互調用的方法

    Android和JavaScript相互調用的方法

    這篇文章主要介紹了Android和JavaScript相互調用的方法,實例分析了Android的WebView執(zhí)行JavaScript及JavaScript訪問Android的技巧,需要的朋友可以參考下
    2015-12-12
  • MTK Android平臺開發(fā)流程

    MTK Android平臺開發(fā)流程

    這篇文章主要介紹了MTK在Android平臺開發(fā)的流程,一共分析了44個步驟,需要的朋友學習下吧。
    2017-12-12
  • Android對話框使用方法詳解

    Android對話框使用方法詳解

    這篇文章主要介紹了Android對話框使用方法,包括提示對話框、單選對話框、復選對話框、列表對話框等,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Android Dialog 設置字體大小的具體方法

    Android Dialog 設置字體大小的具體方法

    這篇文章介紹了Android Dialog 設置字體大小的具體方法,希望能幫助到有同樣需求的朋友,可能我的方法不是最好的,也希望有朋友指點
    2013-09-09
  • Android編程實現GridView控件點擊圖片變暗效果的方法

    Android編程實現GridView控件點擊圖片變暗效果的方法

    這篇文章主要介紹了Android編程實現GridView控件點擊圖片變暗效果的方法,簡單分析了GridView控件事件響應及屬性操作的相關技巧,需要的朋友可以參考下
    2017-06-06
  • Android編程實現全局獲取Context及使用Intent傳遞對象的方法詳解

    Android編程實現全局獲取Context及使用Intent傳遞對象的方法詳解

    這篇文章主要介紹了Android編程實現全局獲取Context及使用Intent傳遞對象的方法,結合實例形式分析了Android全局Context的獲取及Intent傳遞對象的具體操作方法,需要的朋友可以參考下
    2017-08-08
  • Android studio創(chuàng)建第一個app

    Android studio創(chuàng)建第一個app

    這篇文章主要為大家詳細介紹了如何使用Android studio創(chuàng)建你的第一個項目Hello World,感興趣的小伙伴們可以參考一下
    2016-05-05

最新評論

页游| 辽中县| 龙南县| 昭苏县| 旺苍县| 邳州市| 得荣县| 中阳县| 铜梁县| 吉木乃县| 如皋市| 涪陵区| 华阴市| 满洲里市| 万年县| 乐至县| 略阳县| 双牌县| 临湘市| 牟定县| 武平县| 庆云县| 滕州市| 栖霞市| 南木林县| 舟曲县| 宁陕县| 威海市| 长汀县| 琼结县| 北安市| 包头市| 石门县| 新郑市| 乌兰浩特市| 庆云县| 酒泉市| 江北区| 荥阳市| 涪陵区| 永登县|