Android 에서 흔히 볼 수 있 는 그래 픽 그리 기 방식 요약

18086 단어 android도형.OpenGL
도형 그리 기 개요
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 방식 보다 성능 이 효율 적 입 니 다.그러나 일반적인 상황 에서 그 리 는 라인 을 만 들 고 Surface Holder.Callback 인 터 페 이 스 를 실현 하여 Surface View 의 생명 주 기 를 관리 해 야 하 는데 그 실현 논 리 는 View+Canvas 보다 약간 복잡 하 다.또한 3D 렌 더 링 은 지원 되 지 않 으 며 Surface 는 View hierachy 에 있 지 않 기 때문에 View 의 속성 에 의 해 제어 되 지 않 기 때문에 이동,크기 조정 등 변환 을 할 수 없고 다른 View Group 에 넣 을 수 없 으 며 Surface View 는 끼 워 넣 을 수 없습니다.
    
    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 와 마찬가지 로 이동,회전,크기 조정,애니메이션 등 변 화 를 할 수 있 습 니 다.이러한 방식 도 단점 이 있 습 니 다.하드웨어 가 가속 화 된 창 에서 만 사용 할 수 있 습 니 다.메모리 사용량 은 Surface View 보다 높 고 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 3 차원 그래 픽 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 방법 을 초기 화하 고 연결 을 연 후 egl Initialize 방법 으로 초기 화 합 니 다.
    -EGLConfig 대상 을 가 져 오고 표면 을 렌 더 링 하 는 설정 정 보 를 egl Choose Config 방법 으로 EGLConfig 를 얻 을 수 있 는 지 확인 합 니 다.
    -렌 더 링 표면 EGLSurface 를 만 듭 니 다.EGLDisplay 와 EGLConfig 를 통 해 eglCreate WindowSurface 나 eglCreate PbufferSurface 방법 으로 렌 더 링 표면 을 만 들 고 EGLSurface 를 얻 습 니 다.
    -렌 더 링 컨 텍스트 EGLContext 를 만 들 고 EGLDisplay 와 EGLConfig 를 통 해 eglCreate Context 방법 으로 렌 더 링 컨 텍스트 를 만 들 고 EGLContext 를 얻 을 수 있 습 니 다.
    -바 인 딩 컨 텍스트 는 egl MakeCurrent 방법 을 통 해 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()방법 내 매개 변 수 는 Canvas 가 아 닌 GL 10 을 사용 합 니 다
  • GLSurfaceView + OpenGL ES
    EGL 환경 을 구축 하 는 것 이 번 거 롭 고 스 레 드 를 튼튼 하 게 유지 해 야 하기 때문에 Surface View 를 직접 사용 하여 OpenGL 을 그리 기 가 불편 합 니 다.다행히 안 드 로 이 드 플랫폼 은 GLSurfaceView 류 를 제공 하여 개발 자 들 이 OpenGL 의 렌 더 링 개발 을 신속하게 진행 할 수 있 도록 이러한 논 리 를 잘 밀봉 했다.GLSurfaceView 클래스 를 사용 하여 그래 픽 렌 더 링 을 하려 면 GLSurfaceView.Renderer 인 터 페 이 스 를 실현 해 야 합 니 다.이 인 터 페 이 스 는 onDrawFrame(GL 10 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 환경 을 구축 할 때 주의해 야 합 니 다.eglCreate Window Surface()에 들 어 오 는 매개 변 수 는 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
    총결산
    안 드 로 이 드 에서 흔히 볼 수 있 는 그래 픽 그리 기 방식 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 안 드 로 이 드 그래 픽 그리 기 방식 에 관 한 내용 은 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부탁드립니다!

    좋은 웹페이지 즐겨찾기