Java Android - Dialog anzeigen lassen

paxtn

Captain
Registriert
März 2007
Beiträge
3.702
Nabend allerseits,

ich programmiere bereits seit ein paar Monaten für Android und habe auch Gefallen daran gefunden, allerdings zerbreche ich mir nun schon seit Stunden an einer Sache den Kopf und komme mit Google etc. auch nicht weiter :(

Um kurz zu beschreiben, was ich machen möchte:
Ich habe verschiedene Personen und zu diesen Personen mehrere Daten wie z.B. Telefonnummern. Diese Infos werden allesamt als Listviews dargestellt. Für die Darstellung habe ich einen eigenen ArrayAdapter eingebaut, da außer dem Text rechts noch ein Bearbeiten- und Lösch-Button (zwei einzelne ImageButtons) angezeigt werden soll.
Klappt auch wunderbar.

Nun möchte ich aber, dass wenn der Anwender auf den Bearbeiten Button tippt, ein Dialog angezeigt wird, wo er dann eine neue Information (z.B. eine neue Telefonnummer) eingeben kann. Wenn er dann auf Speichern tippt, wird die SQLite-Datenbank entsprechend aktualisiert (alter Wert aktualisiert) und die Oberfläche aktualisiert.

Mit Dialogen habe ich bereits gearbeitet und konnte welche über die Menü-Leiste bereits starten.

Meine Frage: Wie kann von einem Button aus, der sich in einem ArrayAdapter befindet, einen Dialog starten? Oder muss ich den anders starten? (z.B. über die MainActivity?)

Ich würde mich über eure Hilfestellung freuen. Danke.

Viele Grüße,
paxtn
 
Starte doch lieber eine neue Activity, wo der Benutzer dann editieren kann.

Kannst du bei einer ListView dann z.B. so machen:
Code:
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        	 @SuppressWarnings("unchecked")
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        		if(position == 0) return;
        		HashMap<String, Object> data = (HashMap<String, Object>)(parent.getItemAtPosition(position));
				int userId = (Integer)data.get("id");				
				Intent in;
				in = new Intent(EditUserActivity.this, CurrentActivity.class);
				in.putExtra("userId", userId);									
				startActivity(in);
			}
		}
        );
 
Ja , das über eine neue Activity zu machen war auch die Notlösung, nur hätte ich es per Dialog optisch einfach schöner gefunden ;)

Da ich immer noch keine Möglichkeiten per Google gefunden habe, werde ich das nun erstmal per Activity umsetzen.

Falls jemand dennoch eine Idee für die Nutzung eines Dialoges hierfür hat, kann dies gern noch äußern :)
 
Da gibt es eine Möglichkeit.

Zuerst fügst du deinem Adapter ein Event hinzu. In der getView-Methode des Adapters verknüpfst du die erstellten Buttons mit dem Event und gibst z.B. eine ID des Objekts/Datensatzes mit, der für das SQL später wichtig ist:
Code:
public class CustomAdapter extends BaseAdapter
{
	private CustomEventClassListener onAddDataClickListener;
	
	private void setOnAddDataClickListener(CustomEventClassListener onAddDataClickListener)
	{
		this.onAddDataClickListener= onAddDataClickListener;
	}
	...

	@Override
	public View getView(int position, View convertView, ViewGroup parent)
	{
		ViewHolder holder;
		Object displayItem = getListItem(position);

		if (convertView == null)
		{
			// Layout für die Zeile laden
			LayoutInflater vi = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			convertView = vi.inflate(rowViewResourceId, null);

			// Button-Event setzen und beim Klick das Event mit der entsprechenden ID feuern
			holder = new ViewHolder();
			holder.btnAddData.setOnClickListener(new View.OnClickListener()
			{
				@Override
				public void onClick(View v)
				{
					if (onAddDataClickListener != null) onAddDataClickListener.addData(holder.getDataSetID());
				}
			});

			// erfasste Views für das nächste Zeichnen der selben Zeile merken
			convertView.setTag(holder);
		}
		else
		{
			// bereits erfasste View der Zeile abrufen
			holder = (ViewHolder)convertView.getTag();
		}

		// ID zu dieser anzuzeigenden View setzen
		holder.setDataSetID(displayItem.getID());

		return convertView;
	}
}

Ich bin jetzt mal davon ausgegangen, dass du in der getView-Methode einen ViewHolder verwendest.
Wenn nicht, dann solltest du dir die Sache anschauen, weil es die Performance der ListView erheblich erhöht und dabei auch noch Speicher spart.

In der dazugehörigen Activity reagierst du auf dieses Event und zeigst den Dialog an:
Code:
public class MainActivity extends Activity
{
	private CustomAdapter listAdapter;
	private AlertDialog.Builder dialogBuilder;
	private Object currentDataObjectID;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		listAdapter = new CustomAdapter(...
		listAdapter.setOnAddDataClickListener(new CustomEventClassListener()
		{
			@Override
			public void addData(Object id)
			{
				// ID des aktuellen Datenobjekts merken
				currentDataObjectID = id;
				// Eingabedialog anzeigen
				dialogBuilder.show();		
			}
		}

		final EditText etNewData = new EditText(MainActivity.this);

		// Eingabedialog erstellen
		dialogBuilder = new AlertDialog.Builder(MainActivity.this);
		dialogBuilder.setTitle(R.string.title);
		dialogBuilder.setMessage(R.string.message);
		dialogBuilder.setIcon(R.drawable.icon);
		dialogBuilder.setView(etNewData);

		dialogBuilder.setNegativeButton(R.string.abbrechen, new DialogInterface.OnClickListener()
		{
			@Override
			public void onClick(DialogInterface dialog, int which)
			{
				dialog.cancel();
			}
		});

		dialogBuilder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
		{
			@Override
			public void onClick(DialogInterface dialog, int which)
			{
				String newData = etNewData.getText().toString();
				// Daten in die DB schreiben
				insertNewDataToDB(currentDataObjectID, newData);

				// Daten aktualisieren (ich weiß nicht wie du die Daten aus der DB lädst, aber so ähnlich müsste es ja aussehen)
				listAdapter.add(index, newDataValue);

				// Anzeige aktualisieren				
				listAdapter.notifyDataSetChanged();
						
				dialog.dismiss();
			}
		});
	}
}
 
Danke für deinen ausführlichen Beitrag. :)

Ach per Events, auf die Idee bin ich gar nicht gekommen ^^

Auf deine Empfehlung hin, habe ich nun einen Viewholder mit eingebaut.

Allerdings werde ich aus deinem "CustomEventClassListener" nicht schlau, wie sieht der aus? Bzw. was soll der enthalten?
 
Der CustomEventClassListener ist dein eigenes Event für den Adapter, der die notwendigen Daten (z.b. ID) dem übergibt, der an dieses Event angebunden ist.
Er würde ungefähr so aussehen:
Code:
public interface CustomEventClassListener {
	public void addData(int id);
}
 
Achso, ja klar ^^ Danke dir :)

Ich werde das dann heute bzw. morgen mal in das Programm einpflegen.

Ich melde mich, wenn irgendwas noch nicht klappen sollte bzw. wenn ich es geschafft habe ;)

Edit:

So, nach langem hin und her habe ich es noch nicht geschafft, es zu implemenieren. Irgendetwas stimmt da noch nicht, er startet beim antippen gar nichts, also es passiert rein gar nichts.

Hier mal die jeweiligen Code-Auszüge (mit ggf. Kommentaren). Zuerst der ArrayAdapter für die Telefonnummer:
PHP:
public class PhoneArrayAdapter extends ArrayAdapter<String>{
	private final Context context;
	private final List<String> values;
	
	private PhoneNumberEventClassListener onAddPhoneNumberClickListener;
	
	public void setOnAddPhoneNumberClickListener(PhoneNumberEventClassListener onAddPhoneNumberEventClassListener) {
		this.onAddPhoneNumberClickListener = onAddPhoneNumberEventClassListener;
	}
	
	public PhoneArrayAdapter(Context context, List<String> phoneNumbersStringList) {
		super(context, R.layout.address_mail_phone_list_item, phoneNumbersStringList);
		this.context = context;
		this.values = phoneNumbersStringList;
	}
	
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		
		final AddressMailPhoneViewHolder holder;
		Object displayItem = getItem(position); 
//hier war ich mir nicht sicher, was du mit "getListItem" meintest und schloss letzten Endes nur hierauf, aber war mir dabei sehr unsicher
		
		if (convertView == null)
		{
			LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			convertView = inflater.inflate(R.layout.address_mail_phone_list_item, null);
			
			holder = new AddressMailPhoneViewHolder();
			holder.textInformation = (TextView) convertView.findViewById(R.id.addressMailPhoneTextView);
			holder.imageButtonChange = (ImageButton) convertView.findViewById(R.id.addressMailPhoneChangeButton);
			holder.imageButtonDelete = (ImageButton) convertView.findViewById(R.id.addressMailPhoneDeleteButton);
			holder.textInformation.setText(values.get(position));
			holder.imageButtonChange.setImageResource(R.drawable.bearbeitencyan);
			holder.imageButtonDelete.setImageResource(R.drawable.delete);
			
			holder.imageButtonChange.setOnClickListener(new View.OnClickListener() {
				
				public void onClick(View v) {
					if (onAddPhoneNumberClickListener != null) onAddPhoneNumberClickListener.addPhoneNumber(
holder.textInformation.getText().toString()); 
//auch hier war ich mir unsicher, was du mit "holder.getDataSetID()" gemeint hattest, daher habe ich auf dieses hier geschlossen
				} 
			});
			
			convertView.setTag(holder);
			
		}
		else
		{
			holder = (AddressMailPhoneViewHolder) convertView.getTag();
		}
			
		holder.textInformation.setText(displayItem.toString()); //Selbige Unsicherheit wie oben
		
		return convertView;
	}
	

}

Nun der entsprechende Code aus der onCreate-Methode von der MainActivity. Variablen wie ListApapter etc. habe ich entsprechend deklariert.
PHP:
List<String> customersAsString = new ArrayList<String>();
        for (int i = 0; i < customersValues.size(); i++) {
        	customersAsString.add(customersValues.get(i).getFirst_name() + " " + customersValues.get(i).getLast_name());
    	}
        
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
            R.layout.contract_list_item, customersAsString);
        setListAdapter(adapter);
        
        listAdapter = new PhoneArrayAdapter(context, phoneNumbersStringList);
        listAdapter.setOnAddPhoneNumberClickListener(new PhoneNumberEventClassListener() {
			
			public void addPhoneNumber(String phoneNumber) {
				currentDataObjectPhoneNumber = phoneNumber;
				dialogBuilder.show();
			}
		});
        
        final EditText editNewData = new EditText(MainActivity.this);
        
        dialogBuilder = new AlertDialog.Builder(MainActivity.this);
        dialogBuilder.setTitle("titel");
        dialogBuilder.setMessage("message");
        dialogBuilder.setIcon(R.drawable.ic_launcher);
        dialogBuilder.setView(editNewData);
        
        dialogBuilder.setNegativeButton("Abbrechen", new DialogInterface.OnClickListener() {
			
			public void onClick(DialogInterface dialog, int which) {
				dialog.cancel();
			}
		});
        
        dialogBuilder.setPositiveButton("Speichern", new DialogInterface.OnClickListener() {
			
			public void onClick(DialogInterface dialog, int which) {
				dialog.dismiss();
				Toast saveToast = Toast.makeText(context, "Läuft", Toast.LENGTH_SHORT); 
                                //Toast ist zu Testzwecken um zu sehen, ob die Reaktion auf den Buttonklick überhaupt funktioniert.
				saveToast.show();
			}
		});

Ich hoffe, du kannst mir da weiterhelfen.

Schade, dass es zu manchen Dingen bei Android keine Tutorials bzw. how-to's gibt. Habe deswegen ab und zu Probleme und das hält auf, leider -.-
 
Zuletzt bearbeitet:
Also... keine Sorge das kriegen wir hin ;)

Ich glaube du musst mir den Aufbau deiner App noch ein wenig genauer erklären.
So wie ich deinen Code sehe, würde ich vermuten, dass du zwei ListViews in der Activity anzeigst, wobei du in der einen die Namen und in der anderen die Telefonummern anzeigst, da du zwei Adapter verwendest.
Falls du doch nur eine ListView verwendest, dann liegt es daran, dass du zur Anzeige einen Standardadapter (adapter) und nicht den PhoneArrayAdapter (listAdapter) verwendest.

Zu deinem PhoneArrayAdapter:
Der ArrayAdapter<T> besitzt bereits eine Variable zur Haltung der Daten. Also entweder du verwendest den Aufruf super(context, R.layout.address_mail_phone_list_item, phoneNumbersStringList) und rufst ein Datenobjekt deiner Liste mit getItem(position) ab oder du verwendest nur super(context, R.layout.address_mail_phone_list_item) und hältst die Daten in deinem PhoneArrayAdapter in values und fragst ein Element mit values.get(position) ab.
Das heißt, so wie es jetzt ist hältst du die Daten doppelt.

Dann solltest du vielleicht noch wissen wie das mit dem ViewHolder richtig funktioniert:
Wenn eine ListView angezeigt wird, dann werden nur die sichtbaren Zeilen erstellt. Bei der ersten Erstellung einer Zeile ist der Paramter convertView in der Methode getView null. In diesem Fall wird die View für die Zeile mit dem LayoutInflater erstellt und alle nicht-veränderbaren Werte werden im ViewHolder gemerkt (z.B. enthaltene Views) bzw. können erstellt werden (z.B. Events).
Wenn die Liste so lang ist, dass man sie scrollen kann, wird beim runterscrollen die oberste View/Zeile wiederverwendet und nach unten geschoben. In diesem Fall ist der Parameter convertView nicht null und enthält eigentlich noch alle Daten von vorher. Mit dem ViewHolder erspart man sich die Zeit für die Abfrage von Feldern und setzt einfach nur die veränderbaren Werte (z.B. Inhalt von Views).
Also du musst holder.textInformation.setText(values.get(position)) nicht in dem (convertView==null)-Block, sondern anstelle von holder.textInformation.setText(displayItem.toString()) aufrufen.

Ich dachte eigentlich, dass du eine komplexere Klasse für deine anzuzeigenden Objekte hast und nicht nur ein String. Deshalb bin ich davon ausgegeangen, dass du eine ID für ein Datenobjekt hast und diese beim Klicken vom Change-Button mit dem Event mitgeben willst. Deshalb auch holder.getDataSetID(), aber wenn dir der String reicht ist das auch so richtig, wie du es verwendest.

Ich hoffe, dass ich dir damit erstmal wieder einen Schritt weiterhelfen konnte, aber es wäre ja nicht schlecht, wenn du mir dein Layout etwas erklären könntest.
Eventuell könnte man es mit einer ExpandableListView oder mit der ListView, einem LongClickListener und einem ContextMenu etwas schicker machen.
 
Also erstmal sei gesagt, die App wird erstmal für ein 10" Tablet entwickelt. Später werde ich dafür vll. noch einen Smartphone-Pendant schreiben, vll., vll. auch nicht. Mal schauen.

Also es gibt eine Liste an Kontakten. Wenn man einen Kontakt auswählt, erhält man genauere Infos zu diesem. Die Adressen, Mail-Adressen und Telefonnummern werden dabei in ListViews angezeigt, da es beliebig viele zu jedem Kontakt geben kann. Also sind 3 ListViews vorhanden. Dann werden zu den Kontakten noch weitere irrelevante Infos angezeigt, sowas wie Notizen und sowas halt ^^
Und die 3 Listviews enthalten dann Listeneinträge mit einer TextView (textliche Information) und zwei ImageButtons (ändern des Wertes und löschen des Wertes). Für jede ListView verwende ich einen ArrayAdapter. Alle werden aber nach der selben XML-Datei aufgebaut.

Deine programmiertechnischen Hinweise werde ich morgen oder übermorgen mal mit einpflegen.

Achso, okay, ich hatte die Informationen zum ImageHolder im Internet anders interpretiert, danke für die Richtigstellung.

Ich könnte für die anzuzeigenden Objekte auch komplexere Klassen nutzen, nur sehe ich hierfür keinen Nutzen bei meiner Anwendung, da ja nur ein String + die beiden Buttons angezeigt werden sollen. Aber sollte ich irgendwann das nochmal anpassen müssen, dann werde ich mich an deine Hinweise erinnern und würde dann dies mit der ID machen.

Ein LongClickListener wäre als Ersatz für den Bearbeiten-Button eine Möglichkeit, ja. Der einfache Klick soll später dann einen Anruf bzw. das Schreiben einer Mail ermöglichen.
 
Zuletzt bearbeitet:
Bin derzeit im Urlaub und werde mich erst im neuen Jahr wieder ransetzen. ;)

Gebe dir dann aber gerne nochmal ein Feedback, wenn dies erlaubt ist :)
 
Zurück
Oben