返回顶部
首页 > 资讯 > 移动开发 >Android中常见的图形绘制方式总结
  • 238
分享到

Android中常见的图形绘制方式总结

2024-04-02 19:04:59 238人浏览 独家记忆
摘要

目录图形绘制概述 View + canvas SurfaceView + Canvas TextureView + Canvas SurfaceView + OpenGL ES GL

图形绘制概述

Android平台提供丰富的官方控件给开发者实现界面UI开发,但在实际业务中经常会遇到各种各样的定制化需求,这必须由开发者通过自绘控件的方式来实现。通常Android提供了Canvas和OpenGL ES两种方式来实现,其中Canvas借助于Android底层的Skia 2D向量图形处理函数库来实现的。具体如何通过Canvas和OpenGL来绘制图形呢?这必须依赖于Android提供的View类来具体实现,下面组合几种常见的应用方式,如下所示:

Canvas

  • View + Canvas
  • SurfaceView + Canvas
  • TextureView + Canvas

OpenGL ES

  • SurfaceView + OpenGL ES
  • GLSurfaceView + OpenGL ES
  • TextureView + OpenGL ES

View + Canvas

这是一种通常使用的自绘控件方式,通过重写View类的onDraw(Canvas canvas)方法实现。当需要刷新绘制图形时,调用invalidate()方法让View对象自身进行刷新。该方案比较简单,涉及自定义逻辑较少,缺点是绘制逻辑在UI线程中进行,刷新效率不高,且不支持3D渲染。


public class CustomView extends View {
    public CustomView(Context context) {
        super(context);
    }
 
    public CustomView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
 
    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        // draw whatever.
    }
}

SurfaceView + Canvas

这种方式相对于View + Canvas方式在于使用SurfaceView,因此会在Android的WMS系统上创建一块自己的Surface进行渲染绘制,其绘制逻辑可以在独立的线程中进行,因此性能相对于View + Canvas方式更高效。但通常情况下需要创建一个绘制线程,以及实现SurfaceHolder.Callback接口来管理SurfaceView的生命周期,其实现逻辑相比View + Canvas略复杂。另外它依然不支持3D渲染,且Surface因不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换,也不能放在其它ViewGroup中,SurfaceView 不能嵌套使用。


public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
 
    private boolean mRunning = false;
    private SurfaceHolder mSurfaceHolder;
 
    public CustomSurfaceView(Context context) {
        super(context);
        initView();
    }
 
    public CustomSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }
 
    public CustomSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }
 
    private void initView() {
        getHolder().addCallback(this);
    }
 
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mSurfaceHolder = holder;
        new Thread(this).start();
    }
 
    @Override
    public void surfaceChanged(SurfaceHolder holder, int fORMat, int width, int height) {
        mSurfaceHolder = holder;
    }
 
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mRunning = false;
    }
 
    @Override
    public void run() {
        mRunning = true;
        while (mRunning) {
            SystemClock.sleep(333);
            Canvas canvas = mSurfaceHolder.lockCanvas();
            if (canvas != null) {
                try {
                    synchronized (mSurfaceHolder) {
                        onRender(canvas);
                    }
                } finally {
                    mSurfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    }
 
    private void onRender(Canvas canvas) {
        // draw whatever.
    }
}

TextureView + Canvas

该方式同SurfaceView + Canvas方式有些类似,但由于它是通过TextureView来实现的,所以可以摒弃Surface不在View hierachy中缺陷,TextureView不会在WMS中单独创建窗口,而是作为View hierachy中的一个普通View,因此可以和其它普通View一样进行移动,旋转,缩放,动画等变化。这种方式也有自身缺点,它必须在硬件加速的窗口中才能使用,占用内存比SurfaceView要高,在5.0以前在主UI线程渲染,5.0以后有单独的渲染线程。


public class CustomTextureView extends TextureView implements TextureView.SurfaceTextureListener, Runnable {
 
    private boolean mRunning = false;
    private SurfaceTexture mSurfaceTexture;
    private Surface mSurface;
    private Rect mRect;
 
    public CustomTextureView(Context context) {
        super(context);
        initView();
    }
 
    public CustomTextureView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }
 
    public CustomTextureView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }
 
    private void initView() {
        setSurfaceTextureListener(this);
    }
 
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        mSurfaceTexture = surface;
        mRect = new Rect(0, 0, width, height);
        mSurface = new Surface(mSurfaceTexture);
        new Thread(this).start();
    }
 
    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        mSurfaceTexture = surface;
        mRect = new Rect(0, 0, width, height);
        mSurface = new Surface(mSurfaceTexture);
    }
 
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        mRunning = false;
        return false;
    }
 
    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
 
    }
 
    @Override
    public void run() {
        mRunning = true;
        while (mRunning) {
            SystemClock.sleep(333);
            Canvas canvas = mSurface.lockCanvas(mRect);
            if (canvas != null) {
                try {
                    synchronized (mSurface) {
                        onRender(canvas);
                    }
                } finally {
                    mSurface.unlockCanvasAndPost(canvas);
                }
            }
        }
    }
 
    private void onRender(Canvas canvas) {
        canvas.drawColor(Color.RED);
        // draw whatever.
    }
}

以上都是2D图形渲染常见的方式,如果想要进行3D图形渲染或者是高级图像处理(比如滤镜、AR等效果),就必须得引入OpenGL ES来实现了。OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 api 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计,是一种图形渲染API的设计标准,不同的软硬件开发商在OpenGL API内部可能会有不同的实现方式。

下面介绍一下在Android平台上,如何进行OpenGL ES渲染绘制,通常有以下三种方式:

SurfaceView + OpenGL ES

EGL是OpenGL API和原生窗口系统之间的接口,OpenGL ES 的平台无关性正是借助 EGL 实现的,EGL 屏蔽了不同平台的差异。如果使用OpenGL API来绘制图形就必须先构建EGL环境。

通常使用 EGL 渲染的一般步骤:

- 获取 EGLDisplay对象,建立与本地窗口系统的连接调用eglGetDisplay方法得到EGLDisplay。

- 初始化EGL方法,打开连接之后,调用eglInitialize方法初始化。

- 获取EGLConfig对象,确定渲染表面的配置信息调用eglChooseConfig方法得到 EGLConfig。

- 创建渲染表面EGLSurface通过EGLDisplay和EGLConfig,调用eglCreatewindowsurface或eglCreatePbufferSurface方法创建渲染表面得到EGLSurface。

- 创建渲染上下文EGLContext通过EGLDisplay和EGLConfig,调用eglCreateContext方法创建渲染上下文,得到EGLContext。

- 绑定上下文通过eglMakeCurrent 方法将 EGLSurface、EGLContext、EGLDisplay 三者绑定,绑定成功之后OpenGLES环境就创建好了,接下来便可以进行渲染。

- 交换缓冲OpenGLES 绘制结束后,使用eglSwapBuffers方法交换前后缓冲,将绘制内容显示到屏幕上,而屏幕外的渲染不需要调用此方法。

- 释放EGL环境绘制结束后,不再需要使用EGL时,需要取消eglMakeCurrent的绑定,销毁 EGLDisplay、EGLSurface、EGLContext三个对象。

以上EGL环境构建比较复杂,这里先不做过多解释,下面可以通过代码参考其具体实现:


public class OpenGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private boolean mRunning = false;
    private SurfaceHolder mSurfaceHolder;
 
    public OpenGLSurfaceView(Context context) {
        super(context);
        initView();
    }
 
    public OpenGLSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }
 
    public OpenGLSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }
 
    private void initView() {
        getHolder().addCallback(this);
    }
 
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mSurfaceHolder = holder;
        new Thread(this).start();
    }
 
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mSurfaceHolder = holder;
    }
 
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mRunning = false;
    }
 
    @Override
    public void run() {
        //创建一个EGL实例
        EGL10 egl = (EGL10) EGLContext.getEGL();
        //
        EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        //初始化EGLDisplay
        int[] version = new int[2];
        egl.eglInitialize(dpy, version);
 
        int[] configSpec = {
                EGL10.EGL_RED_SIZE,      5,
                EGL10.EGL_GREEN_SIZE,    6,
                EGL10.EGL_BLUE_SIZE,     5,
                EGL10.EGL_DEPTH_SIZE,   16,
                EGL10.EGL_NONE
        };
 
        EGLConfig[] configs = new EGLConfig[1];
        int[] num_config = new int[1];
        //选择config创建opengl运行环境
        egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config);
        EGLConfig config = configs[0];
 
        EGLContext context = egl.eglCreateContext(dpy, config,
                EGL10.EGL_NO_CONTEXT, null);
        //创建新的surface
        EGLSurface surface = egl.eglCreateWindowSurface(dpy, config, mSurfaceHolder, null);
        //将opengles环境设置为当前
        egl.eglMakeCurrent(dpy, surface, surface, context);
        //获取当前opengles画布
        GL10 gl = (GL10)context.getGL();
 
        mRunning = true;
        while (mRunning) {
            SystemClock.sleep(333);
            synchronized (mSurfaceHolder) {
                onRender(gl);
 
                //显示绘制结果到屏幕上
                egl.eglSwapBuffers(dpy, surface);
            }
        }
 
        egl.eglMakeCurrent(dpy, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        egl.eglDestroySurface(dpy, surface);
        egl.eglDestroyContext(dpy, context);
        egl.eglTerminate(dpy);
    }
 
    private void onRender(GL10 gl) {
        gl.glClearColor(1.0F, 0.0F, 0.0F, 1.0F);
        // Clears the screen and depth buffer.
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT
                | GL10.GL_DEPTH_BUFFER_BIT);
    }
}

从上面的代码可以看到,相对于SurfaceView + Canvas的绘制方式,主要有以下两点变化:

  • 在while(true)循环前后增加了EGL环境构造的代码
  • onRender()方法内参数用的是GL10而不是Canvas

GLSurfaceView + OpenGL ES

由于构建EGL环境比较繁琐,以及还需要健壮地维护一个线程,直接使用SurfaceView进行OpenGL绘制并不方便。幸好Android平台提供GLSurfaceView类,很好地封装了这些逻辑,使开发者能够快速地进行OpenGL的渲染开发。要使用GLSurfaceView类进行图形渲染,需要实现GLSurfaceView.Renderer接口,该接口提供一个onDrawFrame(GL10 gl)方法,在该方法内实现具体的渲染逻辑。


public class OpenGLGLSurfaceView extends GLSurfaceView implements GLSurfaceView.Renderer {
    public OpenGLGLSurfaceView(Context context) {
        super(context);
        setRenderer(this);
    }
 
    public OpenGLGLSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setRenderer(this);
    }
 
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // pass through
    }
 
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        gl.glViewport(0, 0, width, height);
    }
 
    @Override
    public void onDrawFrame(GL10 gl) {
        gl.glClearColor(1.0F, 0.0F, 0.0F, 1.0F);
        // Clears the screen and depth buffer.
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT
                | GL10.GL_DEPTH_BUFFER_BIT);
    }
}

TextureView + OpenGL ES

该方式跟SurfaceView + OpenGL ES使用方法比较类似,使用该方法有个好处是它是通过TextureView来实现的,所以可以摒弃Surface不在View hierachy中缺陷,TextureView不会在WMS中单独创建窗口,而是作为View hierachy中的一个普通View,因此可以和其它普通View一样进行移动,旋转,缩放,动画等变化。这里使用TextureView类在构建EGL环境时需要注意,传入eglCreateWindowSurface()的参数是SurfaceTexture实例。


public class OpenGLTextureView extends TextureView implements TextureView.SurfaceTextureListener, Runnable {
    private boolean mRunning = false;
    private SurfaceTexture mSurfaceTexture;
 
    public OpenGLTextureView(Context context) {
        super(context);
        initView();
    }
 
    public OpenGLTextureView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }
 
    public OpenGLTextureView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }
 
    private void initView() {
        setSurfaceTextureListener(this);
    }
 
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        mSurfaceTexture = surface;
        new Thread(this).start();
    }
 
    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        mSurfaceTexture = surface;
    }
 
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        mRunning = false;
        return false;
    }
 
    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
 
    }
 
    @Override
    public void run() {
        //创建一个EGL实例
        EGL10 egl = (EGL10) EGLContext.getEGL();
        //
        EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        //初始化EGLDisplay
        int[] version = new int[2];
        egl.eglInitialize(dpy, version);
 
        int[] configSpec = {
                EGL10.EGL_RED_SIZE,      5,
                EGL10.EGL_GREEN_SIZE,    6,
                EGL10.EGL_BLUE_SIZE,     5,
                EGL10.EGL_DEPTH_SIZE,   16,
                EGL10.EGL_NONE
        };
 
        EGLConfig[] configs = new EGLConfig[1];
        int[] num_config = new int[1];
        //选择config创建opengl运行环境
        egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config);
        EGLConfig config = configs[0];
 
        EGLContext context = egl.eglCreateContext(dpy, config,
                EGL10.EGL_NO_CONTEXT, null);
        //创建新的surface
        EGLSurface surface = egl.eglCreateWindowSurface(dpy, config, mSurfaceTexture, null);
        //将opengles环境设置为当前
        egl.eglMakeCurrent(dpy, surface, surface, context);
        //获取当前opengles画布
        GL10 gl = (GL10)context.getGL();
 
        mRunning = true;
        while (mRunning) {
            SystemClock.sleep(333);
            synchronized (mSurfaceTexture) {
                onRender(gl);
 
                //显示绘制结果到屏幕上
                egl.eglSwapBuffers(dpy, surface);
            }
        }
 
        egl.eglMakeCurrent(dpy, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        egl.eglDestroySurface(dpy, surface);
        egl.eglDestroyContext(dpy, context);
        egl.eglTerminate(dpy);
    }
 
    private void onRender(GL10 gl) {
        gl.glClearColor(1.0F, 0.0F, 1.0F, 1.0F);
        // Clears the screen and depth buffer.
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT
                | GL10.GL_DEPTH_BUFFER_BIT);
    }
}

代码示例参考

GitHub.com/sunjinbo/hi…

总结

到此这篇关于Android中常见图形绘制方式的文章就介绍到这了,更多相关Android图形绘制方式内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Android中常见的图形绘制方式总结

本文链接: https://lsjlt.com/news/129675.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Android中常见的图形绘制方式总结
    目录图形绘制概述 View + Canvas SurfaceView + Canvas TextureView + Canvas SurfaceView + OpenGL ES GL...
    99+
    2024-04-02
  • Android中常见的图形绘制方式有哪些
    这篇文章主要讲解了“Android中常见的图形绘制方式有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Android中常见的图形绘制方式有哪些”吧!目录图形绘制概述View + Canv...
    99+
    2023-06-20
  • PixiJS怎么绘制常见图形
    这篇文章主要介绍“PixiJS怎么绘制常见图形”,在日常操作中,相信很多人在PixiJS怎么绘制常见图形问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PixiJS怎么绘制常见图形”的疑惑有所帮助!接下来,请跟...
    99+
    2023-07-05
  • PythonOpenCV学习之图形绘制总结
    目录背景一、画线二、画矩形三、画圆四、画多边形五、画文本六、鼠标绘制总结背景 使用OpenCV进行图形绘制是一种必备的技能,在图像的任务中,不管是图像检测还是图像识别,我们都需要通过...
    99+
    2024-04-02
  • android图像绘制(七)ClipRect局部绘图/切割原图绘制总结
    杂语:看了很多程序猿都有写博客的习惯,看来我也得练练,不管写的好不好了,学到点什么体会就写写吧。 内容解说:这几天开始学游戏地图制作,今天小小的总结一下Canvas的clipR...
    99+
    2022-06-06
    Android
  • python怎么通过Matplotlib绘制常见的图形
    今天小编给大家分享一下python怎么通过Matplotlib绘制常见的图形的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所...
    99+
    2024-04-02
  • Android切圆角的几种常见方式总结
    Android 中有哪些可以切圆角的实现方式呢? 本文总结一下常用的方式。 以下内容分为以下几部分: 利用 Drawable 的 shape xml 实现 CardV...
    99+
    2022-06-06
    Android
  • python使用Matplotlib绘制多种常见图形
    目录柱状图水平绘制柱状图多个柱状图叠加型柱状图散点图气泡图直方图箱线图添加文字描述添加文字描述 方法二多个图形描绘 subplots使用Pandas 绘图Matplotlib官网&n...
    99+
    2024-04-02
  • Python中常见的导入方式总结
    目录一、直接导入模块二、直接导入包三、导入包中的一个模块四、导入模块并取别名五、从模块(或者包)中取出特定函数(不建议)六、从包中取出特定模块一、直接导入模块 import 模块名 ...
    99+
    2024-04-02
  • android绘制圆形图片的两种方式示例
    android绘制圆形图片的两种方式 看下效果先 下面有完整的示例代码 使用BitmapShader(着色器) 我们在绘制view 的时候 就是小学上美术课 用水彩笔在...
    99+
    2022-06-06
    示例 图片 Android
  • Android绘图常用方法汇总
    Android绘图常用方法有哪些,下面一一为大家列举: 1、有关画笔(Paint)的方法 Paint mPaint= new Paint(); mPaint.setAntiA...
    99+
    2022-06-06
    方法 方法汇总 Android
  • python通过Matplotlib绘制常见的几种图形(推荐)
    目录python通过Matplotlib绘制常见的几种图形一、使用matplotlib对几种常见的图形进行绘制1、柱状图 2、水平绘制柱状图 3、多个柱状图 4、叠加型柱状图 5、散...
    99+
    2024-04-02
  • python怎么使用Matplotlib绘制多种常见图形
    今天小编给大家分享一下python怎么使用Matplotlib绘制多种常见图形的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。...
    99+
    2023-06-30
  • Android编程绘制圆形图片的方法
    本文实例讲述了Android编程绘制圆形图片的方法。分享给大家供大家参考,具体如下: 效果图如下: 第一步:新建RoundView自定义控件继承View package c...
    99+
    2022-06-06
    方法 图片 Android
  • js中常见的6种继承方式总结
    目录前言1、原型继承2、盗用构造函数3、组合继承4、原型式继承5、寄生式继承6、寄生式组合继承总结前言 js是门灵活的语言,实现一种功能往往有多种做法,ECMAScript没有明确的...
    99+
    2024-04-02
  • R语言中常见的几种创建矩阵形式总结
    矩阵概述 R语言的实质实质上是与matlab差不多的,都是以矩阵为基础的 在R语言中,矩阵(matrix)是将数据按行和列组织数据的一种数据对象,相当于二维数组,可以用于描述二维的数...
    99+
    2024-04-02
  • Matplotlib绘制子图的常见几种方法
    前言 Matplotlib的可以把很多张图画到一个显示界面,在作对比分析的时候非常有用。 对应的有plt的subplot和figure的add_subplo的方法,参数可以是一个三位...
    99+
    2024-04-02
  • Pyecharts 绘制3种常用的图形
    目录1.上下组合2.左右组合3.一轴多图大家好,今天给大家利用 Pyecharts 绘制上下组合图、左右组合图、一轴多图,好用超经典,分析给大家 1.上下组合 from pyecha...
    99+
    2024-04-02
  • Java常见异常及处理方式总结
    目录一、概述二、异常分类三、声明及抛出四、捕获异常五、捕获多个异常六、自定义异常七、异常堆栈一、概述 异常指不期而至的各种状况,它在程序运行的过程中发生。作为开发者,我们都希望自己写...
    99+
    2024-04-02
  • Android中ListView的几种常见的优化方法总结
    Android中的ListView应该算是布局中几种最常用的组件之一了,使用也十分方便,下面将介绍ListView几种比较常见的优化方法: 首先我们给出一个没有任何优化的Li...
    99+
    2022-06-06
    方法 listview 优化 Android
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作