我開發一個聊天應用程序,我的適配器(BaseAdapter),持有聊天日誌,在服務,導致變量,讓我的IRC資源在服務。當活動綁定時,我的聊天片段從服務中獲取適配器。 起初,我試圖從適配器本身通知DataSetChanged(),然後我發現我需要從主UI線程調用它。所以我發送廣播片段來通知內部的適配器,片段使用主UI線程。 現在,即時通訊這個問題導致即時通訊提供列表之外的UI線程(從服務),並通知主線程。但它在Service上需要這個適配器。 我該怎麼辦?適配器的內容已更改,但ListView未收到通知。 (喂主列主線)
這是我的適配器。每次對話都有一個。該適配器將保持服務:
public class Conversa extends BaseAdapter {
private Context context;
public ArrayList<String> log;
private String target;
public Conversa(Context context, String target) {
this.context = context;
this.target = target;
log = new ArrayList<String>();
}
@Override
public int getCount() {
return log.size();
}
@Override
public Object getItem(int pos) {
return log.get(pos);
}
@Override
public long getItemId(int arg0) {
return 0;
}
@Override
public View getView(int pos, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.msg_layout, parent, false);
}
TextView msg = (TextView) convertView.findViewById(R.id.msg);
msg.setText(log.get(pos));
return convertView;
}
public ArrayList<String> getLog() {
return log;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
}
這是我ChatFragment:
public class ChatFragment extends Fragment {
private String target;
private ListView listview;
private EditText edittext;
private Conversa conversa;
private MainActivity activity;
private BroadcastReceiver BR_update_list;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
target = getArguments().getString(IRCService.EXTRA_TARGET);
activity = (MainActivity) getActivity();
conversa = activity.service.getConversa(target);
registerUpdateReceiver();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setHasOptionsMenu(true);
View view;
view = inflater.inflate(R.layout.chat_layout, container, false);
edittext = (EditText) view.findViewById(R.id.chatinput);
listview = (ListView) view.findViewById(R.id.chatlist);
listview.setAdapter(conversa);
// EM CASO DE RETORNO DO SERVICE EM BACKGROUND, ROLAR A LISTA ATEH O FIM
if (!conversa.getLog().isEmpty()) {
scrollMyListViewToBottom();
}
// LISTENER QUE RECEBERA O "ENVIAR" DO TECLADO DO ANDROID
edittext.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
boolean handled = false;
if (actionId == EditorInfo.IME_ACTION_SEND) {
// ESCONDE O TECLADO APOS ENVIAR
InputMethodManager in = (InputMethodManager) getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
in.hideSoftInputFromWindow(
edittext.getApplicationWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
sendMessage(target, edittext.getText().toString());
// ALTERA O FLAG
handled = true;
}
return handled;
}
});
return view;
}
@Override
public void onDestroy() {
activity.unregisterReceiver(BR_update_list);
super.onDestroy();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (target.equals(IRCService.CANAL)) {
inflater.inflate(R.menu.canal_menu, menu);
} else {
inflater.inflate(R.menu.pvt_menu, menu);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_closepvt:
activity.removePVTTab(conversa.getTarget());
return true;
case R.id.menu_desconectar:
activity.service.IRCdisconnect();
return true;
case R.id.menu_mudarnick:
// TODO IMPLEMENTAR MUDANÇA DE NICK
return true;
}
return false;
}
private void registerUpdateReceiver() {
BR_update_list = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
conversa.notifyDataSetChanged();
}
};
//a categoria sera o que definira qual fragment devera ser atualizado
IntentFilter filter = new IntentFilter(IRCService.ACTION_RECEIVE_MSG);
filter.addCategory(target);
activity.registerReceiver(BR_update_list, filter);
}
public void sendMessage(String target, String msg) {
activity.service.sendMessage(target, msg);
// APAGA O EDITTEXT
edittext.setText("");
}
private void scrollMyListViewToBottom() {
listview.post(new Runnable() {
@Override
public void run() {
// Select the last row so it will scroll into view...
listview.setSelection(conversa.getCount() - 1);
}
});
}
public void setQuote(String nick) {
((MainActivity) getActivity()).drawerlayout.closeDrawers();
edittext.setText("");
edittext.append(nick + ": ");
edittext.requestFocus();
}
}
這只是我收到服務消息的方法和適配器變量:
private List<Conversa> pvts;
private Conversa canal;
private UserList userList;
public void receivePVTMessage(String target, String nick, String msg) {
Conversa pvt = getConversa(target);
if (pvt == null) {
addPVTConversa(target);
pvt = getConversa(target);
}
pvt.log.add(Colors.removeFormattingAndColors("<" + nick + "> " + msg));
// é dessa forma que o fragment fará o notifydatasetchanged na UI
// thread, a categoria define qual fragment sera atualizado.
Intent it = new Intent(IRCService.ACTION_RECEIVE_MSG);
it.addCategory(target);
sendBroadcast(it);
}
/**
* Metodo usado para adicionar mensagens de usuarios na janela do chat
*/
public void receiveChatMessage(String nick, String msg) {
canal.log
.add(Colors.removeFormattingAndColors("<" + nick + "> " + msg));
// é dessa forma que o fragment fará o notifydatasetchanged na UI
// thread, a categoria define qual fragment sera atualizado.
Intent it = new Intent(IRCService.ACTION_RECEIVE_MSG);
it.addCategory(IRCService.CANAL);
sendBroadcast(it);
}
/**
* Metodo usado para adicionar mensagens gerais na janela do chat
*/
public void receiveChatMessage(String msg) {
canal.log.add(Colors.removeFormattingAndColors(msg));
// é dessa forma que o fragment fará o notifydatasetchanged na UI
// thread, a categoria define qual fragment sera atualizado.
Intent it = new Intent(IRCService.ACTION_RECEIVE_MSG);
it.addCategory(IRCService.CANAL);
sendBroadcast(it);
}
同樣的事情發生在我的消息日誌列表和我的用戶列表(尼克列表)上。用戶列表適配器也在服務中,並且我有一個listfragment來顯示它; 在這兩種情況下,我從服務更新arraylist,並通過sendBroadcast()通知適配器和接收包含適配器的片段;
很難說沒有任何代碼。 – cyroxis
雖然它可能工作,但我會說你正在使用危險的結構。理想的情況是服務會發布消息,然後在您的活動中,您會收到此消息並填寫適配器(您可以使用已編碼的廣播消息發送新消息數據而不是無效請求)。無論如何,你得到的錯誤意味着適配器內容已經改變,而你還沒有調用notifyDataSetChanged():ListView檢測到它自己的內容已經改變'後門'。這打破了ListView內部。 – rupps
只要我更新列表,我每次都發送廣播通知數據。問題是關於多線程。但即使活動已關閉,我仍需要收到消息。該服務必須收到消息。適配器必須在使用中。 –