2017-12-15 8 views
2

Ich habe eine Anwendung, in der ich ein Bild in einem ImageView in einem Fragment aus der Aktivität angezeigt anzeigen. Ich möchte auf Berührungsereignisse im ImageView reagieren, indem ich kleine punktförmige Kreise um die Koordinaten des Berührungsereignisses herum zeichne.Effizientes Zeichnen über ein imageView, das sich in einem Fragment als Reaktion auf ein Berührungsereignis befindet

Nach einigen googeln und Lesen implementiert ich den folgenden Code innerhalb der Fragment-Klasse:

public class ImageViewFragment extends Fragment implements View.OnTouchListener 
{ 
    private ImageView mImageView; 
    private Bitmap mBitmap; 
    private List<Pair<Integer, Integer>> mScribblePointsCoords; 
    private Paint mPaint; 

public interface OnScribbleUpdate 
{ 
    void onNewScribblePoint(List<Pair<Integer, Integer>> coordinates); 

} 

@Override 
public boolean onTouch(View v, MotionEvent event) 
{ 
    v.onTouchEvent(event); 
    return true; 
} 
@Override 
public void onCreate(Bundle savedInstanceState) 
{ 
    super.onCreate(savedInstanceState); 
    mScribblePointsCoords = new ArrayList<>(); 
} 
@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
{ 
    View view = inflater.inflate(R.layout.image_view_fragment, container, false); 
    view.setOnTouchListener(new View.OnTouchListener() 
    { 
     @Override 
     public boolean onTouch(View v, MotionEvent event) 
     { 
      if(event.getAction() == MotionEvent.ACTION_DOWN) 
      { 
       int x = (int) event.getX(); 
       int y = (int) event.getY(); 
       mScribblePointsCoords.add(new Pair<>(x, y)); 
       Bitmap tempBitmap = mBitmap.copy(mBitmap.getConfig(), true); 
       Canvas canvas = new Canvas(tempBitmap); 
       canvas.drawCircle(x, y, R.dimen.default_scribble_point_radius, mPaint); 
       mImageView.setImageDrawable(new BitmapDrawable(getResources(), tempBitmap)); 
       mBitmap = tempBitmap; 
       return true; 
      } 
      return false; 
     } 
    }); 
    mPaint = new Paint(); 
    mPaint.setColor(Color.BLUE); 
    mImageView = view.findViewById(R.id.new_photo_view); 
    mImageView.setImageBitmap(mBitmap); 
    return view; 
} 

@Override 
public void onResume() 
{ 
    super.onResume(); 
    mImageView.setImageBitmap(mBitmap); 
} 

public void setImage(Bitmap bmp) 
{ 
    mBitmap = bmp.copy(bmp.getConfig(), false); 
    if(this.isResumed()) 
    { 
     mImageView.setImageBitmap(mBitmap); 
    } 
} 

public void clearScribble() 
{ 
    mScribblePointsCoords.clear(); 
} 

public List<Pair<Integer, Integer>> getScribbleCoordinates() 
{ 
    return mScribblePointsCoords; 
} 
} 

Edit: image_view_fragment.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
      xmlns:app="http://schemas.android.com/apk/res-auto" 
      xmlns:tools="http://schemas.android.com/tools" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      tools:context="cofproject.tau.android.cof.ImageViewFragment"> 

    <ImageView 
     android:id="@+id/new_photo_view" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:visibility="visible" 
     app:srcCompat="@android:color/background_dark"/> 
</FrameLayout> 

Ende bearbeiten

Nun, das Problem damit ist, dass ich ge t die Gesamtheit des ImageViews wurde durchgehend blau dargestellt (ich habe verschiedene Radiusgrößen für R.dimen.default_scribble_point_radius wie 5, 0,5 und 0,1 ausprobiert, aber ohne Erfolg).

Vorher:

Before clicking the image

Nach:

After clicking the image

Ich habe eine dreiteilige Frage:

  1. Was mache ich falsch und wie kann ich ein einfacher Punkt-ähnlicher Kreis, anstatt zu haben Der gesamte ImageView wird blau.

  2. Ich denke, dass meine onTouch(View v, MotionEvent event) Methode sehr ineffizient ist, da es die Bitmap für jeden hinzugefügten Punkt neu zeichnen musste, und ich mich fragte, ob es einen effizienten Weg gibt, die gleiche Sache zu erreichen.

  3. Würde eine transparente Ansicht von sowesort, auf der die Zeichnung stattfinden wird, mir Schwierigkeiten ersparen, und wenn ja, wie kann so etwas erreicht werden?

Ihre Meinung und Anleitung wird sehr geschätzt.

Antwort

1

Sie sollten mit einem transparenten View gehen, der genau über (z-weise) ImageView positioniert ist. Der Hauptgrund dafür ist die Leistung: Ein ImageView ist eine ziemlich schwere Art von View, wenn es darum geht, kleine blaue Kreise zu zeichnen.

So erstellen Sie eine benutzerdefinierte View, die die Zeichnung tun und auf die ImageView setzen wird. Ich erreicht dies, indem sie beide in einem FrameLayout setzen:

<FrameLayout 
    android:layout_width="200dp" 
    android:layout_height="200dp"> 

    <ImageView 
     android:id="@+id/imageView" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"/> 

    <com.example.paintingcircles.DottedView 
     android:id="@+id/dottedView" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"/> 
</FrameLayout> 

Ich folgte Ihrem Setup und verwendet eine anonyme OnTouchListener aber natürlich könnte man genauso gut haben DottedView Überschreibung onTouchEvent().

In onCreate():

final DottedView dottedView = (DottedView) findViewById(R.id.dottedView); 
dottedView.setOnTouchListener(new View.OnTouchListener() 
{ 
    @Override 
    public boolean onTouch(View view, MotionEvent event) 
    { 
     if(event.getAction() == MotionEvent.ACTION_DOWN) 
     { 
      int x = (int) event.getX(); 
      int y = (int) event.getY(); 
      dottedView.addScribblePointsCoords(new Pair<>(x, y)); 
      dottedView.invalidate(); 
      return true; 
     } 
     return false; 
    } 
}); 

DottedView.java:

public class DottedView extends View 
{ 
    private ArrayList<Pair<Integer, Integer>> mScribblePointsCoords = new ArrayList<>(); 
    private int radius = 5; 
    private Paint mPaint; 

    public DottedView(Context context) 
    { 
     super(context); 
     init(); 
    } 

    public DottedView(Context context, @Nullable AttributeSet attrs) 
    { 
     super(context, attrs); 
     init(); 
    } 

    public DottedView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) 
    { 
     super(context, attrs, defStyleAttr); 
     init(); 
    } 

    @Override 
    protected void onDraw(Canvas canvas) 
    { 
     super.onDraw(canvas); 
     for (Pair<Integer, Integer> pair: mScribblePointsCoords){ 
      canvas.drawCircle(pair.first, pair.second, radius, mPaint); 
     } 
    } 

    public void addScribblePointsCoords(Pair pair){ 
     mScribblePointsCoords.add(pair); 
    } 

    private void init() 
    { 
     mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     mPaint.setColor(Color.BLUE); 
    } 
} 
+0

Leider habe ich keine Ahnung, warum Sie einen blauen Image bekommen. Aber mein Ansatz scheint nicht in dieses Problem zu laufen (vielleicht weil ich nicht versuche, Bitmaps zu behandeln), zumindest auf Emulatoren mit Lollipop (Android 5) .und Nougat (Android 7) – 0X0nosugar

+0

Danke für die detaillierte Antwort. Ich erhalte diese Warnung: Benutzerdefinierte Ansicht '' DottedView'' hat 'setOnTouchListener' aufgerufen, überschreibt aber' performClick' nicht Sollte ich damit beschäftigt sein? –

+0

Ich denke, die zwei Antworten auf [diese SO-Post] (https://stackoverflow.com/questions/27462468/custom-view-overrides-ontouchevent-but-not-performclick/32995457) decken es ziemlich gut ab: override performClick() wenn Sie erwarten, dass eine Komponente (z. B. Accessibility Services) sie aufruft. Sonst wird es nicht notwendig sein. – 0X0nosugar

Verwandte Themen