Flickering while using surface view

笑着哭i 提交于 2020-01-04 19:00:54

问题


I am using surface view to show some graphics, the problem is that there is a flickering effect when I am moving the figure in the screen, I understand that this is due to double buffering problem, even though I went through many posts, I am unable to fix the problem, please take a look at my code and help me get this fixed.

public class CustomSurfaceView  extends SurfaceView implements Runnable{

      Thread mThread             =  null;
      SurfaceHolder mSurfaceHolder;
      volatile boolean mRunning  =  false;
      Bitmap mBitmap;
      boolean mTouched;
      float mTouched_x,mTouched_y;
      Context mContext;
      float mCurrentPosOfRect1x1,mCurrentPosOfRect1y1,mCurrentPosOfRect1x2,mCurrentPosOfRect1y2;
      float mCurrentPosOfRect2x1,mCurrentPosOfRect2y1,mCurrentPosOfRect2x2,mCurrentPosOfRect2y2;

      private Paint mPaint   =  new Paint(Paint.ANTI_ALIAS_FLAG);
      boolean isInitialized  =  false;

      /**
       * Constructor..
       */
      public CustomSurfaceView(Context context) {
          super(context);
          mSurfaceHolder = getHolder();
          mBitmap   =   BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
          mContext  =   context;

          mCurrentPosOfRect1x1  =   100;
          mCurrentPosOfRect1y1  =   100;
          mCurrentPosOfRect1x2  =   300;
          mCurrentPosOfRect1y2  =   300;

          mCurrentPosOfRect2x1  =   300;
          mCurrentPosOfRect2y1  =   300;
          mCurrentPosOfRect2x2  =   500;
          mCurrentPosOfRect2y2  =   500;
      }

      public void onResumeMySurfaceView(){
          mRunning  =   true;
          mThread       =   new Thread(this);
          mThread.start();
      }

      public void onPauseMySurfaceView(){
          boolean retry = true;
          mRunning = false;
          while(retry){
              try {
                  mThread.join();
                  retry = false;
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }

      @Override
      public void run() {
       while(mRunning){
           if(mSurfaceHolder.getSurface().isValid()){
               Canvas canvas = mSurfaceHolder.lockCanvas();
               //... actual drawing on canvas

               mPaint.setStyle(Paint.Style.STROKE);

               if(mTouched){
                      canvas.drawColor(Color.BLACK); 

                      mPaint.setColor(Color.BLACK);
                      mPaint.setStrokeWidth(3);
                      mPaint.setStrokeWidth(0);
                      mPaint.setColor(Color.CYAN);
                      //Left,top 
                      //Right bottom.
                      if(!isInitialized){
                          canvas.drawRect(mCurrentPosOfRect1x1, mCurrentPosOfRect1y1,mCurrentPosOfRect1x2, mCurrentPosOfRect1y2,mPaint);
                          isInitialized =   true;

                      }

                      mPaint.setColor(Color.BLACK);
                      mPaint.setStrokeWidth(3);
                      mPaint.setStrokeWidth(0);
                      mPaint.setColor(Color.BLUE);
                      //Left,top 
                      //Right bottom.
                      if(!isInitialized){
                          canvas.drawRect(mCurrentPosOfRect2x1, mCurrentPosOfRect2y1,mCurrentPosOfRect2x2, mCurrentPosOfRect2y2,mPaint);
                          isInitialized = true;

                      }

                      if(isInitialized){
                          //Check whether the touch points are inside the rectangle & then move...
                          if((mTouched_x>mCurrentPosOfRect1x1) && (mTouched_x<mCurrentPosOfRect1x2) && (mTouched_y>mCurrentPosOfRect1y1) && (mTouched_y<mCurrentPosOfRect1y2)){
                              mCurrentPosOfRect1x1  =   mTouched_x-100;
                              mCurrentPosOfRect1x2  =   mTouched_x+100;
                              mCurrentPosOfRect1y1  =   mTouched_y-100;
                              mCurrentPosOfRect1y2  =   mTouched_y+100;
                          }else if((mTouched_x>mCurrentPosOfRect2x1) && (mTouched_x<mCurrentPosOfRect2x2) && (mTouched_y>mCurrentPosOfRect2y1) && (mTouched_y<mCurrentPosOfRect2y2)){
                              mCurrentPosOfRect2x1  =   mTouched_x-100;
                              mCurrentPosOfRect2x2  =   mTouched_x+100;
                              mCurrentPosOfRect2y1  =   mTouched_y-100;
                              mCurrentPosOfRect2y2  =   mTouched_y+100;

                          }
                      }

                      canvas.drawRect(mCurrentPosOfRect1x1, mCurrentPosOfRect1y1,mCurrentPosOfRect1x2, mCurrentPosOfRect1y2, mPaint);
                      mPaint.setColor(Color.RED);
                      canvas.drawRect(mCurrentPosOfRect2x1,  mCurrentPosOfRect2y1,mCurrentPosOfRect2x2, mCurrentPosOfRect2y2, mPaint);
                      mPaint.setColor(Color.BLUE);


                      Paint paint = new Paint() {
                            {
                                setStyle(Paint.Style.STROKE);
                                setStrokeCap(Paint.Cap.ROUND);
                                setStrokeWidth(3.0f);
                                setAntiAlias(true);
                            }
                        };

                        paint.setColor(Color.YELLOW);

                        final Path path = new Path();
                        final float x1 = mCurrentPosOfRect1x1+ 100;
                        final float y1 = mCurrentPosOfRect1y1 + 100;



                        final float x3 = (mCurrentPosOfRect2x1+ 100) ;
                        final float y3 = (mCurrentPosOfRect2y1 + 100);

                        final float x2 = (x1 +200);
                        final float y2 = (y1 -100);

                        final float x4  = (x3-100);
                        final float y4  = (y3+200);

                        path.moveTo(x1, y1);

                        path.cubicTo(x2,y2,x4,y4,x3,y3);
                        canvas.drawPath(path, paint);

                }

               mSurfaceHolder.unlockCanvasAndPost(canvas);
        }

      }

    }
      @Override
       public boolean onTouchEvent(MotionEvent event){

          mTouched_x = event.getX();
           mTouched_y = event.getY();
          int action = event.getAction();
           switch(action){
           case MotionEvent.ACTION_DOWN:
            mTouched = true;
            break;
           case MotionEvent.ACTION_MOVE:
            mTouched = true;
            break;
           case MotionEvent.ACTION_UP:
            mTouched = false;
            break;
           case MotionEvent.ACTION_CANCEL:
            mTouched = false;
            break;
           case MotionEvent.ACTION_OUTSIDE:
            mTouched = false;
            break;
           default:
           }
           return true; //processed
       }
}

回答1:


If you call lockCanvas(), you need to draw on every pixel in the dirty rect. Since you're calling it without a dirty rect, that means updating every pixel on the Canvas.

I believe the problem with your code is that, when mTouched is false, you're not drawing anything at all. Because the Surface is double- or triple-buffered, you're re-displaying the contents of a previous frame, which is going to cause a vibration effect.

I think all you need to do is move the test for mTouched before the lockCanvas() call, so you don't flip the buffers if you're not going to draw anything.

You may want to look through the SurfaceView lifecycle appendix in the graphics architecture doc if you haven't seen it before, as the thread management sometimes yields surprises.




回答2:


Clear your surfaceviewholder with these lines and make it ready again before playing or drawing any thing on it.

These lines will clear the surface view after using surfaceview at least once

Canvas canvas = mSurfaceHolder.lockCanvas();
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// Draw someting
mSurfaceHolder.unlockCanvasAndPost(canvas);
mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
mSurfaceHolder.setFormat(PixelFormat.OPAQUE);

Here this line will make it ready for second time playing the video

mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);



回答3:


flickering is usually a weird issue, so that's my best guess on how to solve on your specific case.

I can see from your code, you're declaring a series of different commands to be applied to the canvas, those are being drawn one at a time in the canvas, in the order of the code, at the moment that your code lockCanvas and the combination of those elements that I believe is the reason for your flickering.

Because:

  • those draws are not being performed during the system VSYNC (because SurfaceViews)
  • it's done one at a time in sequence (which takes time and makes the flicker noticeable).

I can think of two solutions for this:

  1. I can see you're only calling drawColor and drawRect on your view. Also, you're not performing any time consuming on it. So I really don't see a reason for the usage of SurfaceView. Refactor the class to a normal extends View and perform them drawing inside onDraw and call invalidate() whenever necessary to re-draw (I believe it will be inside the touch events)

  2. If there's some code you omit for brevity that actually does make the SurfaceView really necessary, you can allocate a temporary canvas with a bitmap using the same size of the screen canvas. Do all the drawing on this temporary canvas and use only the drawBitmap call on your on-screen canvas. A small sample code for this follows.

.

 // init your objects inside the `surfaceCreated` callback
 Bitmap tempBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
 Canvas tempCanvas = new Canvas(tempBitmap);


// then on your thread.
 while(mRunning){
    tempCanvas. // ... do all your drawing operations here
    Canvas canvas = mSurfaceHolder.lockCanvas();
    canvas.drawBitmap(tempBitmap, 0, 0, null);
    mSurfaceHolder.unlockCanvasAndPost(canvas);
 }

remember that's just a sample code and not a complete solution, you'll have to do all the normal checks for canvas is valid, etc.



来源:https://stackoverflow.com/questions/29348022/flickering-while-using-surface-view

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!