这两天在做一个短信群发器,在用ListActivity和CursorAdapter罗列联系人并记录用户输入时遇到了一些麻烦,解决过程颇为繁琐,深深感到Android API之广、杂的同时也让自己能静下心来谨慎编码仔细调试广泛查阅,现将最终成果和各种问题以及相应解决方法一一罗列,作为这两日的工作总结的同时,希望能给各位带来一些帮助。
先说明下这个模块要实现的功能吧,此模块主要为群发短信的地址输入模块,用户可以在EditText中直接输入地址,也可以从联系人中pick多个电话号码。
遇到的主要问题有:
1.为了实现两种输入方式(直接EditText输入和联系人选择输入),创建了一个从联系人选择电话号码的ListActivity,为了能在地址编辑Activity和ListActivity之间切换而不丢失已经编辑了的电话号码,就得创建一种数据结构来保存这些电话号码,我这里使用的是一个LinkedList(具体结构后面再阐述),但是怎么样才能保证每次切换都能正确保存地址数据而且EditText内容与地址数据结构保持一致呢?!解决方法是在每次Activity切换时都先进行一次解析或提取过程:从EditText切换到ListActivity之前读取EditText的字符串内容并分析获得地址数据保存到LinkedList中,从ListActivity切换到Activity之前解析LinkedList内容获取要在EditText中显示的字符串内容。
2.有些联系人的号码不止一个,那么应该如何对待?这里我选择的方式和我的Android 2.3系统自带的短信发送器不一样,系统自带的在从联系人获取号码时,如果联系人的号码多于1个,会弹出一个对话框让用户选择一个号码,而我选择的时对话框让用户选择多个号码。
3.对话框与主Activity之间的通信问题,如何让对话框保存用户选择给Activity使用?这里我的解决方式是用静态变量和final变量,从内部类中调用这些变量。
4.用户从对话框选择号码后,如何更新ListActivity的显示内容?我的解决方法是用Handler,对话框给Handler发送消息,Handler使ListActivity更新界面,更新方法为调用Cursor的requery方法。
还有几个比较小的问题,比如联系人数据库的内容,ListActivity中CheckBox的绘制等,就不一一列举了。
现附上我的代码(代码上还有很多清晰的改动以及调试痕迹,呵呵):
a.短信编辑Activity
/* * this Activity is created for sending message * users would edit sms message body in the EditText message , and destinations in the EditText receivers * there can be more than one destinations , and they are arranged at a strict layout * the phone number of every destination would be a String of digits , and they are separated by ',' * and the spaces in the string are all ignored * a valid destinations description is like this : 1234 ,1254,1 3 52,6528, * */ package com.tobacco.activity; import java.util.ArrayList; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import com.tobacco.pro.R; import com.tobacco.sms.SMSSender; public class EditMessAct extends Activity implements OnClickListener{ private Button findContacts; private Button send; private EditText receivers; private EditText message; private ArrayList<String> dest; private static final char SEPARATE=','; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.editmessage); init(); } private void init(){ findContacts=(Button)findViewById(R.id.contacts); send=(Button)findViewById(R.id.send); receivers=(EditText)findViewById(R.id.receivers); message=(EditText)findViewById(R.id.messagetext); findContacts.setOnClickListener(this); send.setOnClickListener(this); dest=new ArrayList<String>(); } @Override public void onClick(View v) { // TODO Auto-generated method stub Intent i; if(v==findContacts){ refreshDest(); i=new Intent(this,ListContactAct.class); this.startActivityForResult(i,1); } else if(v==send){ refreshDest(); String t=message.getText().toString(); new SMSSender(t,dest).send(); } } @Override protected void onActivityResult(int request,int result,Intent intent){ super.onActivityResult(request,result,intent); int t=ListContactAct.selectedReceiver.size(); int i=0; int k=0; int p,j; boolean mark; //String str=""; for(i=0;i<t;i++){ //str=str+ListContactAct.selectedReceiver.get(i).phoneNumber+";"; p=ListContactAct.selectedReceiver.get(i).phoneNumber.size(); for(k=0;k<p;k++){ mark=true; for(j=0;j<dest.size();j++){ if(ListContactAct.selectedReceiver.get(i).phoneNumber.get(k).equals(dest.get(j))){ mark=false; } } if(mark){ dest.add(ListContactAct.selectedReceiver.get(i).phoneNumber.get(k)); } } } refreshText(); //receivers.setText(str+receivers.getText().toString()); } //refresh the EditText receivers by the ArrayList dest private void refreshText(){ String t=""; int k=dest.size(); int i; for(i=0;i<k;i++){ t+=dest.get(i); t+=SEPARATE; } receivers.setText(t); } //refresh the ArrayList dest by the EditText receivers and return information of the analyze result private int refreshDest(){ ArrayList<String> tempDest=new ArrayList<String>(); String f=receivers.getText().toString(); int k=f.length(); String number=""; for(int i=0;i<k;i++){ if(f.charAt(i)==SEPARATE){ if(number.length()>0){ boolean tempMark=true; for(int m=0;m<tempDest.size();m++){ if(tempDest.get(m).equals(number)){ tempMark=false; break; } } if(tempMark){ tempDest.add(number); } } number=""; } else if(f.charAt(i)>='0'&&f.charAt(i)<='9'){ number+=f.charAt(i); } } if(number.length()>0){ boolean tempMark=true; for(int m=0;m<tempDest.size();m++){ if(tempDest.get(m).equals(number)){ tempMark=false; break; } } if(tempMark){ tempDest.add(number); } } dest=tempDest; return 0; } }
package com.tobacco.activity; //import java.util.ArrayList; import java.util.ArrayList; import java.util.LinkedList; import android.app.AlertDialog; import android.app.ListActivity; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.provider.ContactsContract; import android.provider.Contacts.People.Phones; import android.util.Log; import android.view.View; import android.widget.CheckBox; import android.widget.ListView; import com.tobacco.adapter.ListContactsAdapter; import com.tobacco.pro.R; import com.tobacco.types.Receiver; @SuppressWarnings("deprecation") public class ListContactAct extends ListActivity{ private Cursor cursor; public static LinkedList<Receiver> selectedReceiver; public static LinkedList<Integer> selectedPos; private int idColumn; private int displayNameColumn; public static Handler hand; //private Cursor phones=null; //private ArrayList<String> pNumber; @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); //pNumber=new ArrayList<String>(); selectedReceiver =new LinkedList<Receiver>(); selectedPos=new LinkedList<Integer>(); //cursor=getContentResolver().query(People.CONTENT_URI,null,null,null,null); //the cursor get by the above command has no column of has_phone_number cursor= managedQuery(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); debugColumnInfo(); startManagingCursor(cursor); idColumn=cursor.getColumnIndex(ContactsContract.Contacts._ID); displayNameColumn=cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); ListContactsAdapter adapter = new ListContactsAdapter( this, R.layout.listcontact, cursor, new String[] {Phones.DISPLAY_NAME}, new int[] {R.id.people}); this.setListAdapter(adapter); hand=new Handler(){ @Override public void handleMessage(Message msg){ Bundle bundle=msg.getData(); int pos=bundle.getInt("position"); if(pos!=-1){ cursor.requery(); } } }; } @Override protected void onPause(){ super.onPause(); Intent intent=new Intent(); intent.putExtra("date",1); setResult(1,intent); finish(); } @Override protected void onListItemClick (ListView l, View v, int position, long id){ //Toast.makeText(this,""+position+"+"+id,Toast.LENGTH_SHORT).show(); CheckBox cbx = (CheckBox) v.findViewById(R.id.check); String contactId=null; String displayName=null; int hasPhone; final int phoneCount; int t=0; int q=0; int i; cursor.moveToPosition(position); contactId=cursor.getString(idColumn); displayName = cursor.getString(displayNameColumn); while(q<selectedReceiver.size()&&!selectedReceiver.get(q).id.equals(contactId)){ q++; } Integer intg=new Integer(position); while(t<selectedPos.size()&&!selectedPos.get(t).equals(intg)){ t++; } if(t<selectedPos.size()){ if(cbx.isChecked()){ cbx.setChecked(false); } selectedReceiver.remove(q); selectedPos.remove(t); //the people has been added to the send list already /*phoneCount=cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)); if(phoneCount>1){ a }*/ } else{ //the people has not been added to the send list yet hasPhone=Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))); //note:the variable hasPhone ,as the number stored in column has_phone_number is not represent the count of //phone numbers the people has,but the mark that if the people has at least one phone number ,1 has or 0 not //Toast.makeText(this,"phoneCount:"+phoneCount,Toast.LENGTH_SHORT).show(); if(hasPhone>0){ final Cursor phones = getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null); //Toast.makeText(this,"phoneCount22:"+phones.getCount(),Toast.LENGTH_SHORT).show(); phoneCount=phones.getCount(); final String[] pNums=new String[phoneCount]; //pNumber=new ArrayList<String>(); if (phones.moveToFirst()){ i=0; do{ //pNumber.add(phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))); //pNumber is a ArrayList instance to record the phone numbers of the people that was clicked pNums[i]=phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); i++; }while (i<phoneCount&&phones.moveToNext()); } //Toast.makeText(this,""+pNumber.get(0),Toast.LENGTH_SHORT).show(); if(phoneCount==1){ if(!cbx.isChecked()){ cbx.setChecked(true); } selectedReceiver.add(new Receiver(contactId,displayName,pNums[0])); Integer in=new Integer(position); selectedPos.add(in); } else if(phoneCount>1){ final boolean[] mark=new boolean[phoneCount]; final String name=displayName; final int p=position; final String conId=contactId; for(int w=0;w<phoneCount;w++){ mark[w]=false; } AlertDialog ad=new AlertDialog.Builder(this) .setTitle("select phone number") .setMultiChoiceItems(pNums,mark,new DialogInterface.OnMultiChoiceClickListener(){ public void onClick(DialogInterface dialog,int which,boolean isChecked){ } }) .setPositiveButton("okay",new DialogInterface.OnClickListener(){ public void onClick(DialogInterface dialog,int which){ ArrayList<String> a=new ArrayList<String>(); for(int u=0;u<mark.length;u++){ if(mark[u]){ a.add(pNums[u]); } } ListContactAct.selectedReceiver.add(new Receiver(conId,name,a)); Integer in=new Integer(p); ListContactAct.selectedPos.add(in); Bundle bundle=new Bundle(); bundle.putInt("position",p); Message msg=Message.obtain(); msg.setData(bundle); ListContactAct.hand.sendMessage(msg); } }).setNegativeButton("cancel",null).create(); ad.show(); } } } super.onListItemClick(l, v, position, id); } private void debugColumnInfo(){ int count=cursor.getColumnCount(); int t=0; String tag="DebugColumn"; for(;t<count;t++){ Log.d(tag,cursor.getColumnName(t)); } } }
package com.tobacco.adapter; import com.tobacco.activity.ListContactAct; import com.tobacco.pro.R; import android.content.Context; import android.database.Cursor; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.SimpleCursorAdapter; public class ListContactsAdapter extends SimpleCursorAdapter{ public ListContactsAdapter(Context context, int layout, Cursor c,String[] from, int[] to) { super(context, layout, c, from, to); } @Override public View getView(int position, View convertView, ViewGroup parent){ View rowView=super.getView(position,convertView,parent); int t=0; Integer in=new Integer(position); while(ListContactAct.selectedPos.size()>t&&!ListContactAct.selectedPos.get(t).equals(in)){ t++; } CheckBox cbx=(CheckBox)(rowView.findViewById(R.id.check)); if(t<ListContactAct.selectedPos.size()){ cbx.setChecked(true); } else { cbx.setChecked(false); } return rowView; } }