2016-04-13 11 views
0

Als Teil meiner Android-App integriere ich eine sehr einfache benutzerdefinierte Gruppenchat-Funktion. Dies ist nur eine Proof-of-Concept-Funktion. Mir ist bewusst, dass dies vorübergehend ist und dass meine Chats verschwinden, wenn ich meine App neu starte. Meine App verwendet Fragmente und einen ViewPager, um Tabs in meiner App zu erstellen.Warum aktualisiert mein benutzerdefinierter Android ArrayAdapter seine ListView nicht, wenn dem Array neue Daten hinzugefügt werden?

Was ich diese Funktion unterstützen, ist eine LinkedList, die Message-Objekte (Sendezeit, Name des Absenders, Nachricht) enthält. Wann immer ein Chat gesendet oder empfangen wird, wird er der LinkedList hinzugefügt. Zu Beginn meiner MainActivity.java erkläre ich die Liste

public static LinkedList<Message> chatList; 

Im onCreate Methode initialisieren ich die Liste

chatList = new LinkedList<Message>(); 

ich einen Listener-Thread haben, der im Hintergrund läuft, die für eingehende horcht Mitteilungen. Ich habe die Chatliste in diesem Thread in seinem Konstruktor

MyListener myListener = new MyListener(MainActivity.this, chatList); 
Thread listenerThread = new Thread(myListener); 
listenerThread.start(); 

Jedes Mal, wenn der Hörer Thread eine Nachricht erhält, steckt sie in die LinkedList

chatList.add(new_message_object); 

die Chats anzuzeigen, verwende ich eine Listview, die gesichert ist von einem benutzerdefinierten ArrayAdapter, in dem ich die getView() -Methode überschrieben habe. Der ArrayAdapter ruft das Array über die toArray() -Methode meiner LinkedList ab und zeigt die Chats auf dem Bildschirm an. Dieser Prozess fast funktioniert. Wann immer Chats empfangen werden, ist die LinkedList erfolgreich ausgefüllt. Das Problem besteht darin, dass das ListView die Chats sofort aktualisiert und anzeigt. Wenn ich in meiner App zu einem neuen Fragment/Tab wechsle, dann wechsle zurück zu meinem Chat Tab, dann wird die Liste aufgefüllt; aber ich muss das tun, wann immer ich neue Chats sehen möchte.

Dies gilt auch für alle Chats, die ich lokal (d. H. In meiner Chat-Funktion) eingib. Es wird weiterhin zur LinkedList hinzugefügt, aber die ListView wird nicht aktualisiert.

Ich möchte nicht ein SwipeRefreshLayout verwenden, ich würde lieber die Liste selbst aktualisieren. Ich habe eine Methode in der benutzerdefinierten ArrayAdapter

public void refreshList(){ 
    this.notifyDataSetChanged(); 
} 

ich das nennen, wenn eine neue Nachricht Objekt in die LinkedList hinzugefügt wird, aber es wird die Liste nicht aktualisieren.

Also, was mache ich falsch? Wie ich schon sagte, würde ich das ListView-Update lieber selbst vornehmen als ein richtiges Chat-Programm.

Dank

EDIT:

Wie gewünscht, hier ist meine Gewohnheit ArrayAdapter

public class CustomArrayAdapter extends ArrayAdapter { 

    Context context; 
    int resource; 
    Object[] objects; 

    public CustomArrayAdapter(Context context, int resource, Object[] objects) { 
     super(context, resource, objects); 
     this.context = context; 
     this.resource = resource; 
     this.objects = objects; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     View row = convertView; 
     MessageHolder messageHolder = null; 

     if(row == null){ 
      LayoutInflater inflater = ((Activity)context).getLayoutInflater(); 
      row = inflater.inflate(resource, parent, false); 
      messageHolder = new MessageHolder(); 
      messageHolder.chat_information = (TextView) row.findViewById(R.id.chat_information); 
      messageHolder.chat_message = (TextView) row.findViewById(R.id.chat_message); 

      row.setTag(messageHolder); 
     } else { 
      messageHolder = (MessageHolder) row.getTag(); 
     } 

     Message item = (Message) objects[position]; 
     messageHolder.chat_information.setText(item.getSenderName() + Constants.NEWLINE + item.getSendTime()); 
     messageHolder.chat_message.setText(item.getMessageText()); 

     return row; 
    } 

    public void refreshList(){ 
     this.notifyDataSetChanged(); 
    } 

    private class MessageHolder { 
     TextView chat_information; 
     TextView chat_message; 
    } 
} 

EDIT 2: mein Chat.java Fragment

Ich füge.

public class Chat extends Fragment { 

    private View rootView; 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     rootView = inflater.inflate(R.layout.chat, container, false); 
     return rootView; 
    } 

    @Override 
    public void onViewCreated(View rootView, Bundle savedInstanceState) { 
     super.onViewCreated(rootView, savedInstanceState); 
     displayChats(); 
    } 

    @Override 
    public View getView() { 
     Button sendChatButton = (Button) rootView.findViewById(R.id.text_send); 
     sendChatButton.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       Date rightNow = new Date(); 
       SimpleDateFormat timeSDF = new SimpleDateFormat(Constants.SIMPLE_TIME); 
       SimpleDateFormat dateSDF = new SimpleDateFormat(Constants.SIMPLE_DATE); 
       SharedPreferences myAppPreferences = getContext().getSharedPreferences(Constants.PREFS_NAME, getContext().MODE_PRIVATE); 
       EditText chatEntryWindow = (EditText)rootView.findViewById(R.id.chat_text_compose); 
       String message = chatEntryWindow.getText().toString(); 
       String username = myAppPreferences.getString("username", Constants.TABLET_ID); 
       Message myMessage = new Message(username, message, 0, dateSDF.format(rightNow), timeSDF.format(rightNow)); 
       CustomArrayAdapter caa = new CustomArrayAdapter(getActivity(), R.layout.outgoing_line_of_chat, MainActivity.chatList); 
       caa.add(myMessage); 
       caa.notifyDataSetChanged(); 
       new SendChat(getActivity(), message, username).execute(); 
      } 
     }); 
     return super.getView(); 
    } 

    public void displayChats(){ 
     ListView list = (ListView) rootView.findViewById(R.id.chat_text_display); 
     ArrayAdapter adapter = new CustomArrayAdapter(getActivity(), R.layout.outgoing_line_of_chat, MainActivity.chatList); 
     list.setAdapter(adapter); 
    } 
} 
+2

„Die ArrayAdapter liest die toArray() Methode meiner LinkedList und zeigt die Chats auf dem Bildschirm "- das macht für mich keinen Sinn. Ich schlage vor, dass Sie Ihre Frage editieren und Ihre 'getView()' Methode, oder vielleicht den ganzen 'ArrayAdapter' posten. Im Allgemeinen rufen Sie 'add()'/'insert()'/'remove()' auf dem 'ArrayAdapter' auf, um den Inhalt zu aktualisieren und die angehängte' AdapterView' zu aktualisieren, aber 'notifyDataSetChanged()' (nach der Manipulation der 'List' direkt) sollte den gleichen Effekt haben. – CommonsWare

+0

@CommonsWare All dies bedeutet, dass der ArrayAdapter ein Array benötigt und die LinkedList.toArray() - Methode die LinkedList in ein Array konvertiert, das vom ArrayAdapter verwendet werden kann. – Brian

+2

Ich vermute, dass Sie größere Probleme haben. Ich schlage immer noch vor, dass Sie Ihre Frage bearbeiten und Ihre Unterklasse 'ArrayAdapter' veröffentlichen. – CommonsWare

Antwort

0

Ihre CustomArrayAdapter nicht Ihren LinkedList verwenden. Es verwendet eine Object[]. Ich nehme an, von Ihrer Frage und Kommentare, dass, wenn Sie Ihre CustomArrayAdapter erstellen, rufen Sie an diesem Punkt toArray() auf der LinkedList, um Ihre Object[] zu erhalten.

An dieser Stelle ist jedoch die Object[] vollständig von der LinkedList entkoppelt. Angenommen, zu Beginn hat die Objekte. Sie rufen toArray(), und Sie erhalten eine Object[5] enthält diese 5 Message Objekte. Sie rufen später add() auf der LinkedList, um eine 6. Message hinzuzufügen. Die Object[5] ist immer noch eine Object[5] und weiß nichts darüber 6. Message. Die einzige Möglichkeit, die AdapterView zu aktualisieren ist, wenn Sie die CustomArrayAdapter vollständig ersetzen.

Also, loswerden Object[]. ArrayAdapter nimmt eine List in der Hälfte seiner Konstruktoren. So ersetzen:

public class CustomArrayAdapter extends ArrayAdapter { 

    Context context; 
    int resource; 
    Object[] objects; 

    public CustomArrayAdapter(Context context, int resource, Object[] objects) { 
     super(context, resource, objects); 
     this.context = context; 
     this.resource = resource; 
     this.objects = objects; 
    } 

mit:

public class CustomArrayAdapter extends ArrayAdapter<Message> { 

    Context context; 
    int resource; 

    public CustomArrayAdapter(Context context, int resource, List<Message> messages) { 
     super(context, resource, messages); 
     this.context = context; 
     this.resource = resource; 
    } 

Dann in getView(), ersetzen Message item = (Message) objects[position]; mit Message item=getItem(position);.

Schließlich, wenn ein neuer Message ankommt, rufen add() auf dem CustomArrayAdapter, die beide Ihre List<Message> aktualisieren und den beigefügten AdapterView aktualisieren.

+0

Das funktioniert für Chats, die ich lokal sende, aber nicht von Chats, die ich von außen bekomme; Ich muss immer noch aus und dann wieder in den Chat-Tab wechseln. Plus, jetzt bekomme ich einen IllegalStateException Fehler "der Inhalt des Adapters hat sich geändert, aber listview hat keine Benachrichtigung erhalten.stellen Sie sicher, dass der Inhalt Ihres Adapters nicht von einem Hintergrundthread geändert wird, sondern nur vom UI-Thread. "Wie ich bereits sagte, verwende ich einen Hintergrundthread, um Nachrichten von außerhalb zu empfangen. – Brian

+1

@Brian: Sie müssen' add (hinzufügen)) 'auf dem 'ArrayAdapter' im Hauptanwendungs-Thread.Wie Sie das erreichen, hängt stark von der Architektur Ihrer App ab und davon, wie Ihr Hintergrund-Thread diese' Nachricht'-Objekte an den Hauptanwendungs-Thread (z. B. Ereignisbus) liefert. – CommonsWare

+0

Dies würde eine größere Neuschreiben erfordern.Ich muss den Hintergrund-Thread verwenden, um Nachrichten zu empfangen, die vom Hauptanwendungs-Thread getrennt.Wie ich sagte, meine LinkedList wird in meinem Haupt-Thread erstellt und an meine Hintergrund-Thread in seinem Konstruktor übergeben "Es muss einen einfacheren Weg geben, dies zu tun." – Brian

1

die folgende verwenden, nachdem neue Daten in Array Adapter einsetzen:

yourAdapterName.notifyDataSetChanged(); 
+0

@brian wird dein Problem gelöst? –

+0

Hat es nicht. Ich versuchte, notifyDataSetChanged nach dem Einfügen neuer Daten aufzurufen, aber es schien nicht zu helfen. – Brian

0

wie dies der Code ändern. Dies wird nicht zu viele Änderungen vornehmen,

public class CustomArrayAdapter extends ArrayAdapter { 

    Context context; 
    int resource; 
    List objects ; 
    Activity activity ; 

    public CustomArrayAdapter(Context context, int resource, List objects) { 
     super(context, resource, objects); 
     this.context = context; 
     this.resource = resource; 
     //clone the arraylist. 
     this.objects = new ArrayList(objects); 
     activity = (Activity) context; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     View row = convertView; 
     MessageHolder messageHolder = null; 

     if (row == null) { 
      LayoutInflater inflater = LayoutInflater.from(context); 
      row = inflater.inflate(resource, parent, false); 
      messageHolder = new MessageHolder(); 
      messageHolder.chat_information = (TextView) row.findViewById(R.id.chat_information); 
      messageHolder.chat_message = (TextView) row.findViewById(R.id.chat_message); 

      row.setTag(messageHolder); 
     } else { 
      messageHolder = (MessageHolder) row.getTag(); 
     } 

     Message item = (Message) objects.get(position); 
     messageHolder.chat_information.setText(item.getSenderName() + Constants.NEWLINE + item.getSendTime()); 
     messageHolder.chat_message.setText(item.getMessageText()); 

     return row; 
    } 

    public void setObjects(List newObjects) 
    { 
     if (objects != null) 
     { 
      objects.clear(); 
      objects.addAll(newObjects); 
     } 
    } 


    public void refreshList() { 
     activity.runOnUiThread(new Runnable() { 
      @Override 
      public void run() { 
       notifyDataSetChanged(); 
      } 
     }); 

    } 

    private class MessageHolder { 
     TextView chat_information; 
     TextView chat_message; 
    } 
} 

Und man auch jede Chat-Nachricht Anruf setObjects() Verfahren mit der verknüpften Liste und rufen refreshList() Methode

+0

Ich bekomme den Fehler "java.lang.RuntimeException: Kann nicht Handler innerhalb Thread erstellen, die nicht Looper.prepare()" aufgerufen hat. – Brian

+0

Initialize Handler mit Übergabe von Looper.getMainLooper() im Konstruktor. handler = neuer Handler (Looper.getMainLooper()); – Krish

+0

Vielen Dank für Ihre Antwort, aber es hat nicht funktioniert. – Brian