In diesem Tutorial möchte ich erklären wie ich ein benutzerdefiniertes PopupDialog für eine Activity erstelle.
Für mein aktuelles Projekt benötige ich unter anderem diverse Dialoge welche es nicht standardmäßig von Android gibt zbsp:.
- DateTimePickerDialog (zur Auswahl eines Zeitstempels)
- TagCloudDialog (zum setzen von Tags)
Hier möchte ich beschreiben wie ich mein TagCloudDialog erstellt habe, welches dazu dient einen oder mehrere Tags aus einer Liste (ListView) zu wählen welche nach dem Bestätigen des Dialoges auf der Activity dargestellt werden.
Grundsätzlich habe ich mich an dem bestehenden Beispiel zur DialogFragment API von der Android-Hilfe gehalten.
Inhaltsverzeichnis
- Die Klasse TagCloudDialog
- Das Layout “tagclouddialog.xml”
- Befüllen der Liste mit Hilfe des ArrayAdapters
- Interaktion zwischen dem Dialog und der Activity
- Event für die Schaltfläche “OK”
- Event für die Schaltfläche “Abbrechen”
Die Klasse TagCloudDialog
Die Klasse TagCloudDialog wird später unser Dialog darstellen.
import android.app.DialogFragment; public class TagCloudDialog extends DialogFragment{ }
Es erweitert erstmal die Klasse DialogFragment welche die Grundfunktionalitäten eines PopupDialoges mitbringt.
Als erstes überschreiben wir die Methode onCreateDialog(Bundle bundle) folgendermaßen:
@Override public Dialog onCreateDialog(Bundle savedInstanceState) { LayoutInflater inflater = getActivity().getLayoutInflater(); this.view = inflater.inflate(R.layout.tagclouddialog, null); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setView(this.view); builder.setMessage(getResources().getString(R.string.dateTimePickerDialogTitel)) .setPositiveButton(getResources().getString(R.string.dateTimePickerDialogTitelOKBtn), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { } }) .setNegativeButton(getResources().getString(R.string.dateTimePickerDialogTitelAbbrechenBtn), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { } }); return builder.create(); }
In der Zeile 3 und 4 wird das Layout aus der XML Datei geladen (dazu kommen wir später). Es wird sich das View Objekt für später als Instanzvariable gespeichert da wir von außen auf dieses zugriff benötigen (mit der dazugehörigen Getter Methode).
Die Zeilen 9 und 13 beinhalten die Events für die beiden Schaltflächen “OK” und “Abbrechen”. Für die Verarbeitung der beiden Events benötigen wir folgendes Interface, welches wir uns erstellen:
import android.app.DialogFragment; public interface DialogListener { public void onDialogPositiveClick(DialogFragment dialog); public void onDialogNegativeClick(DialogFragment dialog); }
Dieses Interface stellt unsere beiden Methoden bereit, welches wir unserem Konstruktor in der Klasse TagCloudDialog übergeben.
public class TagCloudDialog extends DialogFragment{ private View view; private DialogListener dialogListener; public TagCloudDialog(DialogListener dialogListener){ this.dialogListener = dialogListener; }
Das Layout “tagclouddialog.xml”
Die Datei “tagclouddialog.xml” beinhaltet unsere UI Komponenten für den Dialog. In meinem Fall wird es eine Liste sein, in welcher man diverse Zeilen anwählen kann.
Somit wird das Layout wie folgt aussehen :
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal"> <ListView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tagsView" /> </LinearLayout>
Nun benötigen wir noch den Aufbau unseres anwählbaren ListItems welches ich unter dem Dateinamen “tagclouddialoglistitem.xml” im Ordner “layout” speichere.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tagCloudItemChkBox" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="Beispieltext" android:id="@+id/tagCloudItemTextView" /> </LinearLayout>
Befüllen der Liste mit Hilfe des ArrayAdapters
Für das befüllen der ListView benötigen wir eine Klasse welche die Klasse ArrayAdapter erweitert. Diese lege ich mir in mein Package “tagcloud” ab. Es gibt diverse Tutorials wo dieses als innere Klasse umgesetzt wird jedoch finde ich dieses nicht für sinnvoll da so die Klasse unnötig lang wird.
Die Klasse “TagCloudListAdapter”
Wenn wir die Klasse ArrayAdapter mit extends hinzufügen müssen wir mindestens einen Kontruktor überladen.
import android.content.Context; import android.widget.ArrayAdapter; public class TagCloudListAdapter extends ArrayAdapter<TagItem> { public TagCloudListAdapter(Context context, int resource, List<TagItem> tagItems) { super(context, resource,tagItems); } }
Zuweisen der View für die Darstellung der CheckboxItems
Um der ListView unser benutzerdefiniertes Aussehen zu verpassen (tagclouddialog.xml) müssen wir folgende Methode implementieren.
@Override public View getView(int position, View convertView, ViewGroup parent) {}
In dieser Methode erstellen wir die View für die ListItems.
@Override public View getView(int position, View view, ViewGroup parent) { LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = vi.inflate(R.layout.tagclouddialoglistitem, null); TagItem tagItem = this.tagIitems.get(position); CheckBox chkBox = (CheckBox) view.findViewById(R.id.tagCloudItemChkBox); chkBox.setChecked(tagItem.isSelected()); TextView textView = (TextView) view.findViewById(R.id.tagCloudItemTextView); textView.setText(tagItem.getText()); return view; }
Nun müssen wir an der Checkbox ein Eventhandler registrieren damit wir die gewählten Tags später auslesen können.
Es ist darauf zu achten das in der Zeile 1 die Variable “tagItem” final ist damit diese auf dem View.OnClickListener erreichbar ist. (Sollte dieses nicht sein gibt der Compiler eine aussagekräftige Fehlermeldung, bzw. die IDE wird vorher dieses durch eine Meldung anzeigen.)
final TagItem tagItem = this.tagIitems.get(position); CheckBox chkBox = (CheckBox) view.findViewById(R.id.tagCloudItemChkBox); chkBox.setSelected(tagItem.isSelected()); chkBox.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { CheckBox cb = (CheckBox) v; tagItem.setSelected(cb.isChecked()); } });
Registrieren des Adapters an der ListView
Nachdem wir die Logik für die ListView implementiert haben, wird nun der Adapter an der ListView angemeldet / registriert.
ListView listView = (ListView) this.view.findViewById(R.id.tagsView); listView.setAdapter(new TagCloudListAdapter(getActivity().getApplicationContext(), R.layout.tagclouddialoglistitem, tagItems));
Da ich den Adapter in einer eigenen Klasse geschrieben habe (quasi keine innere Klasse der Klasse TagCloudDialog) muss ich den Context übergeben.
Interaktion zwischen dem Dialog und der Activity
Nun noch die Schaltflächen für die Interaktion mit der übergeordneten Activity bestücken.
In der Zeile 6 & 11 werden die Methoden aus dem Interface “DialogListener” aufgerufen.
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setView(this.view); builder.setMessage(getResources().getString(R.string.tagCloudDialogTitel)) .setPositiveButton(getResources().getString(R.string.okBtn), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialogListener.onDialogPositiveClick(tagCloudDialog); } }) .setNegativeButton(getResources().getString(R.string.abbrechenBtn), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialogListener.onDialogNegativeClick(tagCloudDialog); } });
Event für die Schaltfläche “OK”
Diese Methode
- entfernt als erstes alle TagItemViews aus dem LinearLayout,
- durchläuft die TagItems mit einer For Each Schleife und,
- wenn das TagItem selektiert ist wird dieses dem LinearLayout hinzugefügt.
@Override public void onDialogPositiveClick(DialogFragment dialog) { LinearLayout tagCloud = (LinearLayout) this.view.findViewById(R.id.tagCloudLinearLayout); tagCloud.removeAllViews(); for (TagItem tagItem : this.tagItems) { if (tagItem.isSelected()) { TagItemView item = new TagItemView(this.view.getContext(), this, tagItem); this.tagViewItems.add(item); tagCloud.addView(item, getLayoutParams(0)); } } }
Event für die Schaltfläche “Abbrechen”
Das Event für die Schaltfläche “Abbrechen” muss nicht extra programmiert werden, denn dieses hat sein Verhalten von dem AlertDialog geerbt.