2015-03-30 1 views
2

J'utilise la vue de surface pour montrer certains graphiques, le problème est qu'il y a un effet de scintillement lorsque je déplace la figure à l'écran, je comprends que cela est dû à un problème de double tampon, même si Je suis passé par de nombreux posts, je suis incapable de résoudre le problème, jetez un oeil à mon code et aidez-moi à le corriger.Scintillement lors de l'utilisation de la vue de surface

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 
     } 
} 

Répondre

2

Si vous appelez lockCanvas(), vous devez appuyer sur chaque pixel dans le rectangle sale. Puisque vous l'appelez sans rect rectiligne, cela signifie mettre à jour chaque pixel sur le canevas.

Je crois que le problème avec votre code est que, lorsque mTouched est false, vous ne dessinez rien du tout. Parce que la surface est à double ou triple tampon, vous réaffichez le contenu d'une image précédente, ce qui provoquera un effet de vibration.

Je pense que tout ce que vous devez faire est de déplacer le test pour mTouched avant l'appel lockCanvas(), donc vous ne retournez pas les tampons si vous ne dessinez rien.

Vous pouvez parcourir le SurfaceView lifecycle appendix dans le document d'architecture graphique si vous ne l'avez pas vu auparavant, car la gestion des threads donne parfois des surprises.

+0

Merci pour l'effort, je l'ai fait réparer, tout ce que j'avais à faire était d'écrire un autre pour le if (mTouched) { } et tous les dessins là-bas. –

+0

Pouvez-vous s'il vous plaît jeter un oeil à cette question aussi: - http://stackoverflow.com/questions/29297931/custom-views-inside-canvas-android –

-1

Le clignotement est habituellement un problème étrange, c'est donc ma meilleure estimation sur la façon de résoudre votre cas spécifique.

Je peux voir à partir de votre code, vous déclarez une série de commandes différentes à appliquer à la toile, celles-ci sont dessinées une à la fois dans la toile, dans l'ordre du code, au moment où votre code lockCanvas et la combinaison de ces éléments que je crois est la raison de votre scintillement.

Parce que:

  • les tirages ne sont pas en cours d'exécution au cours du système VSYNC (parce SurfaceViews)
  • il a fait un à la fois dans l'ordre (qui prend du temps et rend le scintillement perceptible).

je peux penser à deux solutions pour cela:

  1. Je peux voir que vous n'appeler drawColor et drawRect votre point de vue. En outre, vous ne perdez pas de temps à le faire. Donc, je ne vois vraiment pas de raison pour l'utilisation de SurfaceView. Factoriser la classe à une extends View normale et effectuer les tirant à l'intérieur onDraw et appeler invalidate() chaque fois que nécessaire pour redessiner (je crois que ce sera dans les événements tactiles)

  2. S'il y a un code que vous omettez par souci de concision qui ne fait faire le SurfaceView vraiment nécessaire, vous pouvez allouer une toile temporaire avec un bitmap en utilisant la même taille de la toile de l'écran. Faites tout le dessin sur ce canevas temporaire et utilisez uniquement l'appel drawBitmap sur votre canevas à l'écran. Un petit exemple de code pour cela suit.

.

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

rappeler que est juste un exemple de code et non une solution complète, vous devrez faire tous les contrôles normaux pour la toile est valide, etc.

+1

Les vues de surface sont à double ou triple tampon. Le dessin qui se produit entre 'lockCanvas()' et 'unlockCanvas()' se produit hors écran, et n'est pas affiché avant le déverrouillage, ainsi dessiner sur un canevas temporaire ajoute simplement une couche supplémentaire de mise en mémoire tampon sans effet. – fadden

+0

n'hésitez pas à donner une réponse correcte alors. – Budius