Android View의 SurfaceView 이점

6700 단어
View UI 업데이트 문제
뷰의 onDraw 방법은 백엔드 라인에서 GUI 요소를 수정하면 현저하게 금지되기 때문에 이렇게 할 수 없습니다.SurfaceView는 View의 UI를 빠르게 업데이트해야 하거나 렌더링 코드가 GUI 스레드를 너무 오래 차단하는 경우에 적합합니다.
SurfaceView와 View의 차이점
  • 본질적 차이인surfaceView는 새로 만들어진 단독 라인에서 화면을 다시 그릴 수 있고 View는 UI의 메인 라인에서 화면을 업데이트해야 한다.
  • 일반 View는 왜 주 스레드에서만 UI를 업데이트할 수 있습니까?
  • UI 스레드는 인터페이스와 배포 창 이벤트를 그리는 데 가장 중요한 스레드로 막힐 수도 없고 스레드가 안전한 것도 아니다.
  • 일반적인 인터페이스 그리기와 배달 이벤트는 다중 스레드 접근이어야 하지만 다중 스레드 처리를 할 때 접근 자원의 정확성을 확보하기 위해 일부 조작에 동기화 자물쇠를 달아서 효율을 떨어뜨릴 뿐만 아니라 스레드의 기다림과 스레드 상하문 전환도 관련된다.
  • 효율을 높이기 위해 UI 스레드는 이런 번거로운 다중 스레드 메커니즘을 사용하지 않고 UI 작업의 정확성을 확보하기 위해 UI 스레드에서만 UI를 조작할 수 있다.UI가 아닌 스레드에서post나runOnUiThread를 통해view를 새로 고칠 수 있습니다.
  • UI 메인 스레드는 다중 스레드로 접근하는 것이 안전하지 않기 때문에 화면을 업데이트할 때 문제를 일으킬 수 있습니다. 예를 들어 화면을 업데이트하는 시간이 너무 길면 메인 UI 스레드는 당신이 그리고 있는 함수에 의해 막힐 수 있습니다.그러면 버튼, 터치스크린 등의 메시지에 응답할 수 없습니다.

  • SurfaceView 서브스레드 업데이트 UI로 인한 문제 이벤트 동기화 문제예를 들어 터치스크린을 눌렀을 때surfaceView에서thread 처리가 필요합니다. 일반적으로 이벤트queue의 디자인이 터치 이벤트를 저장해야 합니다. 이것은 라인의 동기화와 관련이 있기 때문에 좀 복잡합니다.
  • 두 가지 실례 상황
  • 화면은 onTouch에 의존하여 업데이트할 수 있기 때문에 계속 업데이트할 필요가 없고 업데이트 효과가 간단하며 소모 자원이 적다. 이번 터치와 다음 터치의 시간 간격이 비교적 길기 때문에 invalidate로 업데이트하면 된다.
  • 화면이 계속 업데이트되고 있다. 예를 들어 한 사람이 달리기를 하고 있는데main UI thread가 막히지 않도록 단독thread가 ui를 끊임없이 다시 그려야 하기 때문에SurfaceView를 사용해야 한다.


  • SurfaceView 원리
  • Surface란?간단하게 말하면 Surface는 화면 버퍼에 대응하고 모든 window는 하나의 Surface에 대응한다. 모든 View는 Surface에 그려져 있다. 전통적인view는 화면 버퍼를 공유하고 모든 그림은 UI 라인에서 해야 한다
  • Surface와 SurfaceView의 관계SurfaceView는 백그라운드 스레드로 그릴 수 있기 때문에 Canvas 대신 Surface 객체를 봉인합니다.자원에 민감한 조작이나 빠른 업데이트나 고속 프레임 속도를 요구하는 곳, 예를 들어 3D 도형을 사용하거나 게임을 만들거나 실시간으로 카메라를 미리 보는 것이 특히 유용하다.
  • SurfaceHolder가 무엇입니까?CallBack? SurfaceHolder.Callback은 기본 서피스가 생성, 제거 또는 변경될 때 콜백 알림을 제공합니다.그림은surface가 생성된 후에만 그릴 수 있기 때문에SurfaceHolder.Callback의surfaceCreated와surfaceDestroyed는 그래픽 처리 코드의 경계가 됩니다.서피스 Holder, 서피스를 조작하고 캔버스에 그려진 효과와 애니메이션을 처리하며 표면, 크기, 픽셀 등을 제어하는 서피스의 용기와 컨트롤러로 활용할 수 있다.
  • 사용 대가가 GUI 스레드와 독립적으로 그림을 그리는 대가는 추가 메모리 소모이기 때문에 맞춤형 뷰를 만드는 효과적인 방법이지만 때로는 필수적일 수도 있지만 Surface View를 사용할 때는 여전히 조심해야 한다.
  • 라이프 사이클 SurfaceView가 표시되면 Surface가 생성됩니다.SurfaceView가 숨겨지기 전에 Surface가 제거됩니다.이렇게 하면 자원을 절약할 수 있다.
  • 모든SurfaceView와SurfaceHolder를 주의해야 한다.Callback 방법은 모두 UI 스레드에서 호출되어야 하며, 일반적으로 응용 프로그램의 메인 스레드이다.렌더링 라인에 접근할 변수는 동기화 처리를 해야 합니다.

  • SurfaceView 생성
  • 새 SurfaceView를 만들려면 SurfaceView를 확장한 클래스를 새로 만들고 SurfaceHolder를 실행해야 합니다.Callback.그림 그리기 작업은 하위 라인에서 진행되기 때문에 템플릿 코드는 일반적으로 Runnable 인터페이스를 직접 실현할 수 있습니다.만약 비교적 복잡한 상황이라면, 아마도 스레드 탱크를 사용하여 작업 대기열을 처리해야 할 것이다.두 번의 이벤트 트리거 간격이 한 번의 이벤트 처리 시간보다 짧을 수 있기 때문에 응답의 정확성을 확보하기 위해 일반적으로 작업 대기열로 처리합니다.
  • SurfaceHolder 콜백은 현재 유효한 Surface가 포함된 하단의 Surface가 작성되고 제거될 때 View에 알리고 SurfaceHolder 객체에 대한 참조를 전달합니다.
  • 전형적인 SurfaceView 디자인 모델은Thread에서 파생된 클래스를 포함하고 현재의SurfaceHolder에 대한 인용을 받아들여 독립적으로 업데이트할 수 있다.

  • 장면 작업
  • SurfaceView가 사용하는 방식은 모든 View에서 파생된 클래스와 동일합니다.다른 뷰처럼 애니메이션을 적용하여 레이아웃에 배치할 수 있습니다.
  • SurfaceView 캡슐화된 Surface는 이 장 앞에서 설명한 모든 표준 Canvas 방법을 사용하여 드로잉을 그리고 완전한 OpenGL ES 라이브러리를 지원합니다.
  • OpenGL을 사용하면 Surface에서 지원하는 2D나 3D 대상을 다시 그릴 수 있다. 2D 캔버스에서 같은 효과를 시뮬레이션하는 것보다 하드웨어 가속(사용 가능할 때)에 의존하여 성능을 크게 향상시킬 수 있다.
  • Google Earth 기능을 사용하는 어플리케이션이나 몰입감을 제공하는 대화형 게임 등 동적 3D 이미지를 표시하는 데 특히 유용합니다.그것은 카메라의 미리보기를 실시간으로 보여주는 가장 좋은 선택이다.

  • 간단한 도판 코드
    package com.hcsys.display.views;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.util.AttributeSet;
    import android.util.EventLog;
    import android.view.MotionEvent;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    
    /**
     * des:
     * author: admin
     * time: 2016/10/24 11:32.
     * e-mail:[email protected]
     */
    public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
        // SurfaceHolder
        private SurfaceHolder mHolder;
        //      Canvas
        private Canvas mCanvas;
        //       
        private volatile boolean mIsDrawing;
        //  
        private Paint mPaint;
        //    
        private Path mPath;
    
        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() {
            mHolder = getHolder();
            mHolder.addCallback(this);
            setFocusable(true);
            setFocusableInTouchMode(true);
            this.setKeepScreenOn(true);
            //mHolder.setFormat(PixelFormat.OPAQUE);
            mPaint = new Paint();
            mPaint.setColor(Color.BLUE);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(10);
            mPaint.setAntiAlias(true);//   
            mPath = new Path();
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            mIsDrawing = true;
            new Thread(this).start();
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            mIsDrawing = false;
        }
    
        @Override
        public void run() {
            long start = System.currentTimeMillis();
            while (mIsDrawing) {
                draw();
            }
            long end = System.currentTimeMillis();
            // 50 - 100
            if (end - start < 100) {//           100ms
                try {
                    Thread.sleep(100 - (end - start));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private synchronized void draw() {
            try {
                mCanvas = mHolder.lockCanvas();
                mCanvas.drawColor(Color.WHITE);
                mCanvas.drawPath(mPath,mPaint);
            } catch (Exception e) {
            } finally {
                if (mCanvas != null){
                    mHolder.unlockCanvasAndPost(mCanvas);
                }
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    mPath.moveTo(event.getX(),event.getY());
                    break;
                case MotionEvent.ACTION_MOVE:
                    mPath.lineTo(event.getX(),event.getY());
                    break;
                case MotionEvent.ACTION_UP:
    
                    break;
            }
            return true;
        }
    }
    

    좋은 웹페이지 즐겨찾기