Je komplizierter die Anwendungen sind, die Sie schreiben, desto ausgefeiltere Steuerelemente werden Sie benötigen. Android bietet eine große Sammlung von Standardsteuerelementen, die einige grundlegende Funktionen bieten.
Wenn das nicht ausreicht, können Sie versuchen, diese zu kombinieren und zusammengesetzte Steuerelemente (Fragmente) zu erstellen, um komplexere Aufgaben zu bewältigen. Aber auch diese Lösung hat ihre Grenzen und schließlich reichen sowohl die Standardsteuerelemente als auch die zusammengesetzten Steuerelemente nicht mehr aus. Das ist der Zeitpunkt, an dem Sie Ihre eigenen, benutzerdefinierten Steuerelemente implementieren müssen. Und genau das werden wir heute tun.
Forschung und Planung
Überlegen Sie zunächst, was Ihr Steuerelement tun soll. Vielleicht gibt es jemanden, der bereits ein solches Steuerelement geschrieben hat? Wenn ja, sollten Sie in Erwägung ziehen, es zu verwenden - selbst wenn es Ihren Anforderungen nicht vollständig entspricht, ist es wahrscheinlich gerechtfertigt, einige Funktionen zu opfern, um viele Stunden Arbeit zu sparen. Ein guter Ausgangspunkt für die Suche nach fortgeschrittenen Steuerelementen ist Android Arsenal.
Wenn Sie nicht gefunden haben, was Sie suchen, lesen Sie weiter.
Benutzerdefinierte Steuerung
Sie haben also beschlossen, Ihr eigenes Steuerelement von Grund auf zu schreiben. Toll! Das kann tatsächlich eine recht unterhaltsame Aufgabe sein. Werfen wir einen kurzen Blick auf die Aspekte der benutzerdefinierten Steuerung, die Sie berücksichtigen müssen.
Layout
Das Android-Framework verfügt über ein recht leistungsfähiges Layoutsystem, mit dem Sie eine feste Größe für das Steuerelement festlegen, es an die Größe seines Inhalts anpassen oder an die Größe des übergeordneten Steuerelements anpassen können. Die Steuerelemente können auch in verschiedenen Arten von Containern platziert werden, die ihre eigenen Regeln für das Layout der untergeordneten Steuerelemente haben. Wenn Sie Ihr eigenes Steuerelement schreiben, müssen Sie eine vorhersehbare Strategie für die Größe Ihres Steuerelements entwerfen, damit es sich nahtlos in dieses System einfügt.
Siehe
Dies ist natürlich einer der wichtigsten Aspekte von benutzerdefinierten Steuerelementen. Android ermöglicht es Ihnen, sowohl 2D- als auch 3D-Steuerelemente zu implementieren, indem es die erforderlichen Basisklassen und Werkzeuge bereitstellt. Zum Zeichnen Ihres Steuerelements können Sie sowohl Primitive (Linien, Ellipsen, Rechtecke) als auch komplexe Drawables und Bilder verwenden, so dass Sie effektiv die volle Kontrolle über das Aussehen Ihres Steuerelements haben. Denken Sie jedoch daran, dass mobile Anwendungen besondere Anforderungen an Steuerelemente stellen (z.B. müssen sie groß genug sein, um durch Berührung bedient zu werden).
Interaktion
Standard-Steuerelemente interagieren mit dem Benutzer auf eine vordefinierte Weise. Wenn Sie Ihr eigenes benutzerdefiniertes Steuerelement schreiben, müssen Sie diesen Mechanismus selbst implementieren. Glücklicherweise stellt Android Ihnen alle Informationen darüber zur Verfügung, was der Benutzer zu tun versucht (Tastatur- und Berührungsereignisse, einschließlich Multitouch), so dass Sie alles wissen, was Sie brauchen.
Attribute
Das anfängliche Aussehen und Verhalten von Steuerelementen wird durch XML-Attribute gesteuert. Wenn Sie zum Beispiel die Größe des Textes auf dem Steuerelement festlegen möchten, können Sie das Attribut
Probenkontrolle
Beginnen wir mit einer sehr einfachen Kontrolle.
Wir werden ein Steuerelement schreiben, das als Hintergrund-Histogrammbalken für Statistiklisteneinträge dienen soll. Die besondere Anforderung besteht darin, die Breite des Balkens als Prozentsatz der gesamten Breite des Steuerelements anstelle von absoluten Werten (z.B. Gerätepixel) festlegen zu können. Natürlich kann dies auch ohne ein benutzerdefiniertes Steuerelement erreicht werden, aber lassen Sie uns der Übung halber klein anfangen und etwas Eigenes schreiben.
Grundlagen
Erstellen Sie zunächst ein Paket im Projekt, das unsere Steuerelemente enthalten wird. Fügen Sie dann eine neue Klasse hinzu und nennen Sie sie PercentBar. Der ursprüngliche Code sollte wie folgt aussehen:
import android.view.View;
/**
* Erstellt von Wojciech am 2016-06-20.
*/
public class PercentBar extends View {
}
Da Steuerelemente in Android als Views bezeichnet werden und die Klasse View - wie Sie sich denken können - eine Basisklasse für alle Views ist, werden wir genau von dieser Klasse abstammen. Da View keinen parameterlosen Konstruktor hat, müssen wir einen überschreiben. Lassen Sie uns diesen wählen:
public PercentBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
Der Parameter attrs vom Typ AttributeSet wird sich beim Zugriff auf benutzerdefinierte Attribute als nützlich erweisen. Wir werden später auf dieses Thema zurückkommen.
Lassen Sie uns nun das Layout betrachten. Wie Sie wissen, müssen für ein Steuerelement in Android die Attribute layout_width und layout_height definiert sein. Außerdem können beide entweder eine Größeneinheit (wie 10dp) oder einen von zwei speziellen Werten enthalten: match_parent oder wrap_content. Der erste Wert teilt dem Steuerelement mit, dass eine seiner Dimensionen perfekt mit der Dimension des übergeordneten Elements übereinstimmen muss. Der zweite Wert fordert das Steuerelement auf, so klein wie möglich zu sein (in der angegebenen Achse), ohne jedoch seinen Inhalt zu verdecken. Diese Werte werden nicht direkt an das Steuerelement übergeben, sondern werden vom Layoutsystem und dem übergeordneten Container des Steuerelements (ViewGroup) verwendet, um zu ermitteln, wie groß das Steuerelement sein darf. Denken Sie daran, dass, auch wenn das Steuerelement eine bestimmte Größe haben soll, dies manchmal nicht möglich ist (z.B. weil sein übergeordneter Container nicht groß genug ist) und es daher beschnitten werden muss. Denken Sie also daran, das Steuerelement so zu implementieren, dass es so viel Platz beansprucht, wie es benötigt, aber auch in der Lage ist, sich selbst auf einer kleineren Fläche als der gewünschten zu zeichnen.
Ein Steuerelement nimmt am Layoutprozess teil, indem es die Methode onMeasure überschreibt und implementiert:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
}
Die Parameter enthalten kodierte Informationen über die Anforderungen an die Breite und Höhe des Steuerelements sowie Werte für diese Anforderungen. Sie können View.MeasureSpec.getMode und View.MeasureSpec.getSize verwenden und einen dieser Parameter übergeben, um den Messmodus bzw. die Größe zu erhalten.
Es gibt drei mögliche Messmodi:
-
- MeasureSpec.UNSPECIFIED - das übergeordnete Steuerelement gibt keine Anforderungen für eine Dimension an und das Steuerelement kann so groß sein, wie es will.
-
- MeasureSpec.AT_MOST - die Kontrolle kann beliebig lang sein, aber nur bis zum angegebenen Wert.
- MeasureSpec.EXACTLY - das übergeordnete Steuerelement hat bestimmte Dimensionswerte für das Steuerelement ermittelt und diese werden unabhängig von der gewünschten Größe des Steuerelements verwendet.
Es gibt noch eine weitere Einschränkung: Wenn Sie onMeasure implementieren, sind Sie verpflichtet, setMeasuredDimension aufzurufen und ausgewertete Dimensionen zu übergeben, bevor die Methode onMeasure beendet wird. Wenn Sie diesen Vertrag nicht einhalten, wird eine Ausnahme ausgelöst.
Wie Sie wissen, gibt es einige Arten von Einheiten, die Sie verwenden können, um die Größe des Steuerelements anzugeben, z.B. dp (device pixels), sp (scalable pixels) und so weiter. Bei dieser Methode arbeiten wir jedoch mit rohen Pixeln. Dies ist auch bei der Bewertung der Größe des Steuerelements zu berücksichtigen. Wenn Sie mehr über Einheiten und Einheitenumrechnungen lesen möchten, besuchen Sie die Dokumentation und diesen StackOverflow-Thread.
Layout
Kehren wir zu unserem Histogrammbalken-Steuerelement zurück und implementieren onMeasure. Da das Steuerelement sehr einfach ist und keinen verschachtelten Inhalt hat, der es zu einer bestimmten Größe zwingen würde, wie im Fall von MeasureSpec.EXACTLY und MeasureSpec.AT_MOST, kann das Steuerelement so viel Platz verwenden, wie ihm zugewiesen wird und wenn es keine Einschränkungen gibt(MeasureSpec.UNSPECIFIED) und wir können einige vordefinierte Dimensionswerte verwenden:
private final int DEFAULT_WIDTH = 100;
private final int DEFAULT_HEIGHT = 100;
(...)
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int Breite = 0, Höhe = 0;
switch (View.MeasureSpec.getMode(widthMeasureSpec)) {
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
width = View.MeasureSpec.getSize(widthMeasureSpec);
Pause;
case MeasureSpec.UNSPECIFIED:
Breite = DEFAULT_WIDTH;
Pause;
}
switch (View.MeasureSpec.getMode(heightMeasureSpec)) {
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
height = View.MeasureSpec.getSize(heightMeasureSpec);
Pause;
case MeasureSpec.UNSPECIFIED:
Höhe = DEFAULT_HEIGHT;
Pause;
}
setMeasuredDimension(Breite, Höhe);
}
Unser Steuerelement ist nicht als eigenständiges Steuerelement gedacht (wir werden es z.B. als Hintergrund für andere Steuerelemente verwenden), daher hat es keine besonderen Anforderungen an die Größe. Deshalb wird die Größe von 100×100 Pixeln angenommen, wenn keine angegeben ist - diese Situation sollte bei normaler Verwendung nie eintreten.
Breite in Prozent
Da die Breite des Balkens ein Prozentsatz der Gesamtbreite des Steuerelements sein soll, benötigen wir ein entsprechendes Feld mit Getter und Setter, das den Prozentwert steuert:
private float percentage;
public float getPercentage() {W
Prozentsatz zurückgeben;
}
public void setPercentage(float percentage) {
this.percentage = percentage;
invalidate();
}
Beachten Sie, dass setter eine zusätzliche Methode aufruft: invalidate. Da die Änderung des Prozentsatzes erfordert, dass das Steuerelement neu gezeichnet wird, rufen wir invalidate auf, um das Framework darüber zu informieren, dass das Steuerelement nicht mehr den richtigen Inhalt anzeigt. Die Entscheidung, wann genau das Steuerelement neu gezeichnet wird, wird aus Leistungsgründen dem Framework überlassen - das Framework kann Steuerelemente, die einen solchen Bedarf melden, in einer bestimmten Reihenfolge anordnen und gruppieren, um unnötige Zeichnungsdurchläufe zu vermeiden.
Attribute
Jetzt können wir uns daran machen, das Steuerelement zu zeichnen. Das ist keine schwierige Aufgabe - wir müssen nur ein gefülltes Rechteck einer bestimmten Größe zeichnen. Aber wenn wir es im Sinne von Android machen wollen, sollten wir dem Benutzer erlauben, ein Drawable anzugeben, das als Hintergrund für den Histogrammbalken dienen soll. Und genau hier kommen die Attribute ins Spiel.
Um benutzerdefinierte Attribute für ein Steuerelement festzulegen, müssen wir eine XML-Datei vorbereiten, die Namen und Typen der Attribute enthält. In unserem Fall heißt sie attr.xml und sieht wie folgt aus:
Der declare-styleable-Tag gibt an, welche Klasse das Steuerelement repräsentiert, dem wir benutzerdefinierte Attribute zuweisen möchten. Jeder attr-Tag gibt ein Attribut mit einem bestimmten Namen und Typ an. Da wir einen Verweis auf ein zeichenbares Objekt erhalten möchten, verwenden wir reference als Format (Typ des Attributs).
Jetzt müssen wir nur noch einen Weg finden, den Wert zu erhalten, der dem Attribut in der Layout-XML-Datei zugewiesen wurde. Hier kommt der Parameter AttributeSet des Konstruktors ins Spiel.
private Drawable barDrawable;
public Drawable getBarDrawable() {
return barDrawable;
}
public void setBarDrawable(Drawable barDrawable) {
this.barDrawable = barDrawable;
invalidate();
}
public PercentBar(Context context, AttributeSet attrs) {
super(kontext, attrs);
Prozentsatz = 50.0f;
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PercentBar, 0, 0);
versuchen {
setBarDrawable(a.getDrawable(R.styleable.PercentBar_barDrawable));
} finally {
a.recycle();
}
}
Denken Sie daran, dass der Benutzer ein Attribut möglicherweise gar nicht setzt. In diesem Fall sollten wir das zugrunde liegende Feld auf einen Standardwert setzen.
Zeichnung
Schließlich können wir uns auf das Zeichnen konzentrieren. Das gesamte Zeichnen sollte in einer überschriebenen onDraw-Methode durchgeführt werden. Das Android-Framework stellt ein Canvas-Objekt zur Verfügung, mit dem alle Aufgaben, die gezeichnet werden müssen, ausgeführt werden können.
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (barDrawable != null) {
barDrawable.setBounds(0, 0, (int) (canvas.getWidth() * (percentage / 100.0f)), canvas.getHeight());
barDrawable.draw(canvas);
} sonst {
Paint p = new Paint();
p.setColor(Farbe.rgb(255, 0, 0));
p.setStyle(Paint.Style.FILL);
canvas.drawRect(new Rect(0, 0, canvas.getWidth(), canvas.getHeight()), p);
}
}
Modus bearbeiten
Das Android-Framework bietet eine weitere Funktion, die wir hier nicht verwenden werden, die aber sicherlich erwähnenswert ist. Da Android Studio das Steuerelement normalerweise im Vorschaufenster verwendet, können wir eine spezielle Methode verwenden: isInEditMode(), um festzustellen, ob das Steuerelement in der Live-Anwendung oder im Bearbeitungsmodus von Android Studio verwendet wird. Im letzteren Fall kann das Steuerelement gefälschte Platzhalterdaten anzeigen, so dass der Programmierer sehen kann, wie das Steuerelement aussieht, wenn es mit echten Daten gefüllt ist. Sogar einige der Standard-Android-Steuerelemente (wie RecyclerView) nutzen diese Funktion.
Kontrolle verwenden
Lassen Sie uns unser neues Steuerelement ausprobieren. Um es in einer Activity zu platzieren, müssen wir den vollqualifizierten Klassennamen verwenden. Für Attribute ist ein zusätzlicher XML-Namespace erforderlich, aber wir können den guten alten res-auto verwenden, um ihn aufzulösen.
Schließlich erstellen Sie das Projekt neu, um die funktionierende Steuerung im Vorschaubereich von Android Studio zu sehen:
Den Quellcode der Beispielanwendung finden Sie in unserem GitHub-Repository.[:]
Unsere Ideen
Weitere Blogs
Contact



