Stand: 08.09.2018
Bisher kennen Sie "Grafik" in zwei Formen: zum einen in Form von vorgegebenen UI-Komponenten wie Buttons oder Textfelder, zum anderen in Form von Bitmaps, die Sie frei platzieren können.
In diesem Kapitel geht es darum, selbst Grafik durch verschiedene Zeichenbefehle zu erzeugen und gegebenenfalls interaktiv neu zu zeichnen.
11.1 Zeichnen
11.1.1 Eigene View-Klasse
Um einfache Grundformen wie Linien, Kreise und Rechecke zu zeichnen, erstellt man eine Unterklasse der Klasse View. Dort überschreibt man die Methode onDraw
, um eigene Objekte zu zeichnen.
Üblicherweise erstellt man die eigenen View-Klasse innerhalb der jeweiligen Aktivität, damit man gegebenenfalls über Instanzvariablen Infomationen mit der aktuellen Aktivität austauschen kann oder Übergänge per Intent realisieren kann.
Hier sehen wir das minimale Grundgerüst einer eigenen Klasse DrawingView
mit Konstruktor und der Methode onDraw
.
public class MainActivity extends AppCompatActivity {
...
class DrawingView extends View {
public DrawingView(Context context) {
super(context);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Code zum Zeichnen
}
}
}
Jetzt müssen wir unsere neue View noch einbinden. In onCreate
erzeugen Sie dann eine Instanz dieser Klasse und sagen der Activity, dass dies "ihre View" ist:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final DrawingView view = new DrawingView(this);
setContentView(view);
}
Das heißt natürlich auch, dass Ihre Layout-Datei activity_main.xml
nicht mehr "zuständig" ist und ignoriert wird.
11.1.2 Auf die Canvas zeichnen
In der Methode onDraw
, die wir ja überschreiben, bekommen wir ein Objekt vom Typ Canvas
(engl. für Leinwand). Man kann sich ein Canvas-Objekt tatsächlich wie ein Leinwand vorstellen, auf der man zeichnet. Die Befehle zum Zeichnen sind Methoden der Klasse Canvas.
Grundformen
Sie können dann Grundformen wie Linien, Rechtecke, Ellipsen und Kreise zeichnen mit den Methoden drawLine
, drawRect
, drawOval
und drawCircle
. Dabei übergeben Sie die jeweils erforderlichen Koordinaten und Größenangaben und dann noch ein Objekt vom Typ Paint
, auf das wir im nächsten Abschnitt eingehen.
Hier ein Beispiel:
public class MainActivity extends AppCompatActivity {
...
class DrawingView extends View {
public DrawingView(Context context) {
super(context);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(400, 400, 200, new Paint());
canvas.drawRect(800, 200, 1200, 600, new Paint());
}
}
}
Sie sollten sehen:
Styling und Text
Zum Styling verwendet man die Klasse Paint
. Es empfiehlt sich, Paint-Objekte als Instanzvariablen zu deklarieren, damit sie nicht bei jedem der häufigen Aufrufe von onDraw
erst hergestellt werden müssen.
class DrawingView extends View {
private Paint paint1 = new Paint();
private Paint paint2 = new Paint();
private Paint paintText = new Paint();
...
protected void onDraw(Canvas canvas) {
...
}
}
In onDraw
kann man die Paint-Objekte anschließend konfigurieren und gibt sie dann den Zeichenbefehlen mit.
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint1.setStyle(Paint.Style.STROKE);
paint1.setStrokeWidth(10);
paint1.setColor(Color.RED);
canvas.drawCircle(400, 400, 200, paint1);
canvas.drawLine(700, 200, 700, 600, paint1);
paint2.setStyle(Paint.Style.FILL);
paint2.setColor(Color.BLUE);
canvas.drawRect(800, 200, 1200, 600, paint2);
canvas.drawOval(1300, 200, 1500, 600, paint2);
paintText.setTextSize(60);
paintText.setFakeBoldText(true);
paintText.setColor(Color.GRAY);
paintText.setTextAlign(Paint.Align.CENTER);
canvas.drawText("Grundformen", canvas.getWidth()/2, 100, paintText);
}
Sie sehen, dass man die Kontour (Stroke) und die Füllung (Fill) bestimmen kann.
Text zeichnen Sie mit drawText
. Beim Styling kann man Größe, Alignierung und "fake bold" (fett) angeben ("fake" deshalb, weil einfach die Dicke vergrößert wird und nicht ein dafür angefertigter Zeichensatz).
Bitmaps
Sie können auch Bitmaps, also vorgegebene Grafikdateien (JPG, GIF, PNG etc.) einbinden. Wir nehmen dazu an, Sie haben eine Datei "smiley.jpg" im Ressourcenverzeichnis "drawable" abgelegt.
Dann können Sie die Bitmap laden und anschließend mit drawBitmap
auf die Canvas zeichnen.
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.smiley);
canvas.drawBitmap(bitmap, (canvas.getWidth() - bitmap.getWidth())/2,
(canvas.getHeight() - bitmap.getHeight())/2, new Paint());
Aus Performance-Gründen würde man die obere Zeile in onCreate
ausführen und die Bitmap-Variable als Instanzvariable anlegen.
11.1.3 Shape als Ressource
Sie können eine Form auch in XML definieren und direkt ins Layout einbinden, ohne eine Zeile Java-Code zu schreiben.
Zunächst spezifizieren wir unsere Form im Ordner (mit beispielhaftem Dateinamen):
res/drawable/shape.xml
Zum Beispiel einen roten Kreis:
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#FF0000" />
<size android:width="200dp" android:height="200dp"/>
</shape>
Dieser wird mit Hilfe eines ImageView-Objekts in das Layout eingefügt:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/shape" />
Hier ein Beispiel für einen Kreis nur mit Kontour und ohne Füllfarbe:
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<stroke android:color="#000000" android:width="3dp"/>
<size android:width="200dp" android:height="200dp"/>
</shape>
11.2 Interaktion
Möchten Sie den Kreis immer an die Position eines Touch hängen, müssen Sie einen Touch-Listener hinzufügen, der die Position des Kreises verändert.
Am Ende des Listeners müssen Sie ein invalidate()
einfügen, das dafür sorgt, dass das onDraw() aufgerufen und der Kreis somit neu gezeichnet wird.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final DrawingView view = new DrawingView(this);
setContentView(view);
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent motionEvent) {
view.setPos(motionEvent.getX(), motionEvent.getY());
invalidate();
return true;
}
});
}
class DrawingView extends View {
private Paint paint = new Paint();
private float x;
private float y;
public DrawingView(Context context) {
super(context);
}
public void setPos(float x, float y) {
this.x = x;
this.y = y;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GREEN);
canvas.drawCircle(x, y, 200, paint);
}
}