2017-07-10 1 views
1

j'ai la classe suivante ci-dessous qui se prolonge Voir et a la méthode onDraw de substitution pour dessiner cirles sur un bitmap qui a été chargé dans.enregistrement d'une toile, bitmap avec des cercles dessinés

Les charges bitmap avec succès et est affiché sur l'écran. Lorsque l'utilisateur touche l'écran, un cercle est dessiné.

J'ai un bouton d'action flottant qui compresse l'image bitmap dans un octet []. Cet octet [] est ensuite envoyé à une autre activité à afficher. Malheureusement, le bitmap résultant n'a aucun cercle en place. Canvas est un objet local dans onDraw et mCBitmap et tCanvas sont des variables globales de sorte que la méthode saveImage peut accéder aux données.

Quelqu'un peut-il me dire pourquoi aucun des cercles n'est copié dans le bitmap résultant?

import android.content.Context; 
import android.content.res.AssetManager; 
import android.content.res.Resources; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.Rect; 
import android.graphics.RectF; 
import android.os.Environment; 
import android.util.AttributeSet; 
import android.util.DisplayMetrics; 
import android.util.Log; 
import android.util.SparseArray; 
import android.util.TypedValue; 
import android.view.MotionEvent; 
import android.view.View; 
import android.widget.Toast; 

import org.apache.commons.io.IOUtils; 

import java.io.BufferedInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.DataInputStream; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import android.graphics.PointF; 

/** 
* Created by MatthewW on 14/06/2017. 
*/ 

public class TouchView extends View { 

    private static final String TAG = TouchView.class.getSimpleName(); 
    Bitmap bgr; 
    File tempFile; 
    private byte[] imageArray; 
    private Paint pTouch; 

    Context context; 

    private SparseArray<ColouredCircle> mPointers; 
    public int x; 
    public int y; 


    int circleCount; 
    int radius; 
    protected byte [] data; 

    Bitmap mCBitmap; 
    Canvas tCanvas; 




    public TouchView(Context context) { 
     super(context); 

     this.context = context; 

    } 



    public TouchView(Context context, AttributeSet attr) { 
     super(context,attr); 
     Log.e(TAG, "inside touchview constructor"); 

     this.context = context; 

     radius = 70; 
     circleCount = 0; 



     copyReadAssets(); 

     imageArray = new byte[(int)tempFile.length()]; 


     Log.e(TAG, "imageArray has length = " + imageArray.length); 



     try{ 

      InputStream is = new FileInputStream(tempFile); 
      BufferedInputStream bis = new BufferedInputStream(is); 
      DataInputStream dis = new DataInputStream(bis); 


      int i = 0; 

      while (dis.available() > 0) { 
       imageArray[i] = dis.readByte(); 
       i++; 
      } 

      dis.close(); 


     } catch (Exception e) { 

      e.printStackTrace(); 
     } 



     Bitmap bm = BitmapFactory.decodeByteArray(imageArray, 0, imageArray.length); 

     if(bm == null){ 
      Log.e(TAG, "bm = null"); 
     }else{ 
      Log.e(TAG, "bm = not null"); 
     } 



     mPointers = new SparseArray<ColouredCircle>(); 


     bgr = bm.copy(bm.getConfig(), true); 





     bm.recycle(); 

     pTouch = new Paint(Paint.ANTI_ALIAS_FLAG); 
     // pTouch.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT)); 
     pTouch.setColor(Color.RED); 
     pTouch.setStyle(Paint.Style.STROKE); 

     pTouch.setStrokeWidth(5); 




    }// end of touchView constructor 




    private void copyReadAssets() { 

     AssetManager assetManager = context.getAssets(); 

     InputStream in = null; 
     OutputStream out = null; 
     tempFile = new File(context.getFilesDir(), "bodymap.jfif"); 
     try { 
      in = assetManager.open("bodymap.jfif"); 
      out = context.openFileOutput(tempFile.getName(), Context.MODE_WORLD_READABLE); 

      IOUtils.copy(in, out); 

      in.close(); 
      in = null; 
      out.flush(); 
      out.close(); 
      out = null; 
     } catch (Exception e) { 
      Log.e(TAG, e.getMessage()); 
     } 


    } 


    @Override 
    public void onDraw(Canvas canvas){ 
     super.onDraw(canvas); 



     Log.e(TAG, "about to draw bgr "); 
     // canvas.drawBitmap(bgr, 0, 0, null); 

     DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 
     int width = metrics.widthPixels; 
     int height = metrics.heightPixels; 



     Rect frameToDraw = new Rect(0, 0, width, height); 
     RectF whereToDraw = new RectF(0, 0, width, height - 300); 

     canvas.drawBitmap(bgr,frameToDraw,whereToDraw, null); 




     if(mPointers != null) { 

      Log.e(TAG, "mPointers.size() = " + mPointers.size()); 

      for (int i = 0; i < mPointers.size(); i++) { 

       PointF p = mPointers.get(i).getPointF(); 
       x = (int) p.x; 
       y = (int) p.y; 

       pTouch.setColor(mPointers.get(i).getColour()); 


       canvas.drawCircle(x, y, radius, pTouch); 




       mCBitmap = Bitmap.createBitmap(bgr.getWidth(), bgr.getHeight(), bgr.getConfig()); 

       tCanvas = new Canvas(mCBitmap); 

       tCanvas.drawBitmap(bgr, 0, 0, null); 

       tCanvas.drawCircle(x, y, radius, pTouch); 

       tCanvas.drawBitmap(mCBitmap, 0, 0, null); 






      } 

     } 






    }//end of onDraw 





    @Override 
    public boolean onTouchEvent(MotionEvent me) { 
     switch (me.getActionMasked()) { 
      case MotionEvent.ACTION_DOWN: 

      case MotionEvent.ACTION_POINTER_DOWN: { 
       /*int ai = me.getActionIndex(); 
       PointF pt = new PointF(me.getX(ai), me.getY(ai)); 
       mPointers.put(me.getPointerId(ai), pt); 

       Log.e(TAG, "mPointers.size() = " + mPointers.size() + "me.getPointerId(ai) = " 
           + me.getPointerId(ai) + " me.getX(ai) = " + me.getX(ai) + " me.getY(ai) = " + me.getY(ai));*/ 

       int ai = me.getActionIndex(); 
       PointF pt = new PointF(me.getX(ai), me.getY(ai)); 

       ColouredCircle cc = new ColouredCircle(pTouch.getColor(),pt); 

       mPointers.put(circleCount, cc); 

       circleCount++; 

       invalidate(); 
       return true; 
      } 

      case MotionEvent.ACTION_UP: { 

      } 

      case MotionEvent.ACTION_POINTER_UP: { 
       /*int pid = me.getPointerId(me.getActionIndex()); 
       mPointers.remove(pid);*/ 
       return true; 
      } 

      case MotionEvent.ACTION_MOVE: { 
       /*for (int i = 0; i < me.getPointerCount(); ++i) { 
        PointF pt = mPointers.get(me.getPointerId(i)); 
        pt.set(me.getX(i), me.getY(i)); 
        invalidate(); 
       }*/ 
       return true; 
      } 
     } 
     return false; 
    } 



    public void showToastMessage(String mess){ 

     Toast.makeText(TouchView.this.getContext(), mess.toString(), Toast.LENGTH_SHORT).show(); 

    } 

    public int getRadius() { 
     return radius; 
    } 




    public void setRadius(int r) { 
     radius = r; 
     invalidate(); 
    } 


    public void setCircleColour(String colourMode){ 


     if(colourMode.equalsIgnoreCase("RED")){ 

      pTouch.setColor(Color.RED); 

     }else if(colourMode.equalsIgnoreCase("BLUE")){ 

      pTouch.setColor(Color.BLUE); 

     }else if(colourMode.equalsIgnoreCase("GREY")){ 

      pTouch.setColor(Color.GRAY); 

     } 

    } 



    public byte[] saveImage(){ 



     ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     mCBitmap.compress(Bitmap.CompressFormat.JPEG, 100 /*ignored for PNG*/, bos); 
     data = bos.toByteArray(); 
     try { 
      bos.flush(); 
      bos.close(); 
     } catch (IOException e1) { 
      // TODO Auto-generated catch block 
      e1.printStackTrace(); 
     } 

     try { 
      bos.flush(); 
      bos.close(); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

     if (data == null){ 
      Log.e(TAG, "data in touchview before save clicked is null"); 
     }else{ 
      Log.e(TAG, "data in touchview before saved clicked is not null and has length + " + data.length); 
     } 

     return data; 

    } 

}//end of class 

[EDIT1]

J'ai changé mes méthodes onDraw() et SaveImage() à ce qui suit ci-dessous. L'image est maintenant enregistrée avec les cercles en place. Cependant, l'image bgr est très petite et en haut à gauche de l'écran (le reste de l'écran est noir). Les cercles sont à la bonne place car ils sont dans l'original, c'est juste l'image bg dans la taille de l'écran.

Comment est-ce que je peux rendre la copie de l'image bg au Bitmap résultant en pleine taille?

@Override 
    public void onDraw(Canvas canvas){ 
     super.onDraw(canvas); 



     Log.e(TAG, "about to draw bgr "); 
     // canvas.drawBitmap(bgr, 0, 0, null); 

     DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 
     int width = metrics.widthPixels; 
     int height = metrics.heightPixels; 



     Rect frameToDraw = new Rect(0, 0, width, height); 
     RectF whereToDraw = new RectF(0, 0, width, height - 300); 

     canvas.drawBitmap(bgr,frameToDraw,whereToDraw, null); 




     if(mPointers != null) { 

      Log.e(TAG, "mPointers.size() = " + mPointers.size()); 

      for (int i = 0; i < mPointers.size(); i++) { 

       PointF p = mPointers.get(i).getPointF(); 
       x = (int) p.x; 
       y = (int) p.y; 

       pTouch.setColor(mPointers.get(i).getColour()); 


       canvas.drawCircle(x, y, radius, pTouch); 





      } 

     } 






    }//end of onDraw 

.

public byte[] saveImage(){ 

     DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 
     int width = metrics.widthPixels; 
     int height = metrics.heightPixels - 300; 

     Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 
     //Bitmap bitmap = Bitmap.createBitmap(bgr.getWidth(), bgr.getHeight(), bgr.getConfig()); 

     Canvas canvas = new Canvas(bitmap); 
     this.draw(canvas); 

     ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     bitmap.compress(Bitmap.CompressFormat.JPEG, 100 /*ignored for PNG*/, bos); 
     data = bos.toByteArray(); 
     try { 
      bos.flush(); 
      bos.close(); 
     } catch (IOException e1) { 
      // TODO Auto-generated catch block 
      e1.printStackTrace(); 
     } 

     try { 
      bos.flush(); 
      bos.close(); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

     if (data == null){ 
      Log.e(TAG, "data in touchview before save clicked is null"); 
     }else{ 
      Log.e(TAG, "data in touchview before saved clicked is not null and has length + " + data.length); 
     } 

     return data; 

    } 

.

Voici une photo de l'activité qui permet à l'utilisateur de placer des cercles sur l'image. L'image bg est en plein écran.

enter image description here

Voici une image de la sortie dans l'activité suivante où l'image avec des cercles est transformé en un octet [] et affiché. Comme vous pouvez le voir, l'image bg n'est pas en plein écran mais les cercles sont dans les bonnes positions.

enter image description here

Répondre

2

En onDraw(...), le changement:

Rect frameToDraw = new Rect(0, 0, width, height); 

Pour:

Rect frameToDraw = new Rect(0, 0, bgr.getWidth(), bgr.getHeight()); 

Une raison pour laquelle vous ne disposez pas de ce problème lorsque vous affichez l'image dans onDraw(...), lorsque onDraw(...) est appelé par le cadre pourrait être que l'objet Canvas fourni par le cadre est acce matériel lerated par défaut, si vous avez un sdk cible> = 14 (source, deuxième alinéa, première phrase)

Comme on le voit here, lorsqu'une toile est l'accélération matérielle, il semble sa densité est définie à 0. Cependant, si vous appelez Bitmap.getDensity() sur votre champ TouchView#bgr, vous verrez que ce ne sera pas 0.C'est parce que les bitmaps, une fois créés, ont la densité de l'écran de l'appareil actuel.

Ensuite, vous dessiné votre bitmap haute densité (sur mon appareil, la densité est de 320 ppi) sur votre 0 ppi Canvas -> il sera dessiné pour paraître plus grand.

Toutefois, dans saveImage, vous créez un Bitmap à partir de zéro, puis vous créez un Canvas à partir de ce bitmap, puis vous fournissez ce canevas pour appeler draw(...). Cette nouvelle toile que vous venez de créer ne sera pas accélérée matériellement, et aura aussi la densité de votre bitmap nouvellement créé (320 ppi, dans mon cas).

Lorsque vous dessinez l'image de votre carte corporelle en onDraw(...), vous allez dessiner sur cette toile avec 320dp, la même que votre image. Cela fonctionnerait toujours correctement, mais vous spécifiez que vous voulez dessiner une région de screenWidth x screenHeight, en supposant 1920x1080, de votre carte du corps, qui est probablement plus petite, supposons 1000x600, avec frameToDraw.

Que fera Android dans ce cas? Eh bien, Javadoc de Canvas#drawBitmap(...) nous dit ce qui suit:

Note: si la peinture contient un maskfilter qui génère un masque qui étend au-delà de la largeur/hauteur d'origine du bitmap (par exemple BlurMaskFilter), le bitmap sera dessiné comme s'il était dans un Shader avec le mode CLAMP. Ainsi, la couleur en dehors de la largeur/hauteur d'origine sera la couleur de bord répliquée.

Ce qui signifie essentiellement: Je vais prendre vos 1000x600 et dessine vos pixels comme ils devraient être tirés, et le reste des pixels jusqu'à 1920x1080, je vais remplir avec une couleur de mon choix (probablement noir ou blanc ou transparent?)

Maintenant, vous dites, par whereToDraw que vous voulez que votre région 1920x1080 soit mappée à une région 1620x1080. Pour ce faire, Android va encore réduire votre bitmap, rendant le bodymap encore plus petit.

La solution la plus simple à cela est de spécifier les dimensions réelles de votre bitmap, en onDraw(...), lors de la création de votre région frameToDraw.

De plus, si ces fragments de code testent du code, c'est bien, mais assurez-vous de suivre les directives mentionnées here, lorsque vous travaillez avec onDraw(...), lorsque vous nettoierez votre code.

Les points les plus importants seraient que:

  • votre ne devrait vraiment pas allouer objet dans vos méthodes onDraw(...), car ils pourraient se dit très souvent, et cela va tuer votre garbage collector. (c'est-à-dire frameToDraw & whereToDraw - vous pouvez les préallouer dans le constructeur)

  • Vous pouvez obtenir une image bitmap de votre écran plus facilement en utilisant le cache de dessin de la vue.

il suffit de changer votre saveImage à:

public byte[] saveImage() { 
    //reder the view once, in software. 
    buildDrawingCache(); 
    //get the rendered bitmap 
    Bitmap bitmap = getDrawingCache(); 

    ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100 /*ignored for PNG*/, bos); 
    byte[] data = bos.toByteArray(); 
    //destroy the drawing cache, to clear up the memory 
    destroyDrawingCache(); 

    try { 
     bos.flush(); 
     bos.close(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 

    if (data == null) { 
     Log.e(TAG, "data in touchview before save clicked is null"); 
    } else { 
     Log.e(TAG, "data in touchview before saved clicked is not null and has length + " + data.length); 
    } 

    return data; 
} 

Voir:

Espérons que cela aide

+0

Salut, Merci pour une réponse si détaillée. Je pense que je dois faire beaucoup plus de lecture sur les graphiques android. Cela fonctionne bien maintenant! Je vais également faire ces changements pour placer les objets dans le constructeur. Merci encore! – turtleboy