2014-01-06 17:18:29
1. Phonebook中新建/编辑联系人的UI不是用xml文件写的,它是随着帐号类型的改变来加载不同的UI,比如SIM联系人,只有Name、Phone Number,如果是USIM,或许还有第二个号码、Email,但是本地联系人除了包含这些,还有Nickname,Website等,所以帐号如何定义以及UI如何加载就变得很复杂。
2. 帐号类型(以AdnAccountType SIM帐号为例)
继承关系:AdnAccountType.java --> BaseAccountType.java --> AccountType.java,在AccountType中有两个很重要的方法,如下:
1 public ArrayList<DataKind> getSortedDataKinds() { 2 Collections.sort(mKinds, sWeightComparator); 3 return mKinds; 4 } 5 6 public DataKind addKind(DataKind kind) throws DefinitionException { 7 kind.resourcePackageName = this.resourcePackageName; 8 this.mKinds.add(kind); 9 this.mMimeKinds.put(kind.mimeType, kind); 10 return kind; 11 }
addKind()方法添加kind到mKinds,getSortedDataKinds()取出来所有的kind,这两个方法的使用将在后面介绍。关于BaseAccountType,这个类是所有帐号类的父类,下面看AdnAccountType:
1 public AdnAccountType(Context context, String resPackageName, String accountType) { 2 this.accountType = accountType; 3 //this.resPackageName = null; 4 this.syncAdapterPackageName = resPackageName; 5 6 try { 7 addDataKindStructuredName(context); 8 addDataKindPhone(context); 9 10 int simType = SimUtil.getIccType(context, AdnHelper.getSlotNumber(accountType)); 11 if (simType == IccProviderConstant.USIM_TYPE 12 || simType == IccProviderConstant.CSIM_TYPE) { 13 addDataKindEmail(context); 14 } 15 16 mIsInitialized = true; 17 } catch (DefinitionException e) { 18 SpbLog.e(TAG, "Problem building account type", e); 19 } 20 } 21 22 @Override 23 protected DataKind addDataKindStructuredName(Context context) throws DefinitionException { 24 DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE, 25 R.string.nameLabelsGroup, -1, true, R.layout.structured_name_editor_view)); 26 27 kind.fieldList = Lists.newArrayList(); 28 kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.nameLabelsGroup, 29 FLAGS_PERSON_NAME).setNeedFocus(true)); 30 31 return kind; 32 } 33 34 @Override 35 protected DataKind addDataKindPhone(Context context) throws DefinitionException { 36 DataKind kind = super.addDataKindPhone(context); 37 38 kind.iconAltRes = R.drawable.ic_message_icon; 39 kind.actionHeader = new PhoneActionInflater(); 40 kind.actionAltHeader = new PhoneActionAltInflater(); 41 kind.actionBody = new SimpleInflater(Phone.NUMBER); 42 43 kind.typeColumn = Phone.TYPE; 44 kind.typeList = Lists.newArrayList(); 45 kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE)); 46 47 kind.fieldList = Lists.newArrayList(); 48 kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE)); 49 50 return kind; 51 }
可以看到,SIM卡帐号添加了Name和Phone number两种数据的UI,这符合SIM联系人只能添加名称和号码的规则,如果SIM卡是USIM或者CSIM,那么还可以添加Email信息,看if判断语句就知道了。在addDataKindStructuredName()方法里面,调用了前面介绍的addKind()方法,将一个DataKind对象添加进去,同时为这个DataKind对象的fieldList添加了EditField,这个fieldList包含的是要显示的View,其实就是phone number或者Email条目包含的子条目,这些item会在后面具体介绍。下面看一下较复杂的LocalAccountType:
1 public LocalAccountType(Context context, String resPackageName) { 2 this.accountType = SpbIntents.ACCOUNT_TYPE; 3 this.resourcePackageName = null; 4 this.syncAdapterPackageName = resPackageName; 5 6 try { 7 addDataKindStructuredName(context); 8 addDataKindNickname(context); 9 addDataKindPhone(context); 10 addDataKindEmail(context); 11 addDataKindStructuredPostal(context); 12 addDataKindIm(context); 13 addDataKindOrganization(context); 14 addDataKindPhoto(context); 15 addDataKindNote(context); 16 addDataKindWebsite(context); 17 addDataKindEvent(context); 18 addDataKindSipAddress(context); 19 addDataKindGroupMembership(context); 20 21 mIsInitialized = true; 22 } catch (DefinitionException e) { 23 SpbLog.e(TAG, "Problem building account type", e); 24 } 25 } 26 27 @Override 28 protected DataKind addDataKindPhone(Context context) throws DefinitionException { 29 final DataKind kind = super.addDataKindPhone(context); 30 31 kind.typeColumn = Phone.TYPE; 32 kind.typeList = Lists.newArrayList(); 33 kind.typeList.add(buildPhoneType(Phone.TYPE_HOME)); 34 kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE)); 35 kind.typeList.add(buildPhoneType(Phone.TYPE_WORK)); 36 kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true)); 37 kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true)); 38 kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true)); 39 kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER)); 40 kind.typeList.add(buildPhoneType(Phone.TYPE_CUSTOM).setSecondary(true).setCustomColumn( 41 Phone.LABEL)); 42 43 kind.fieldList = Lists.newArrayList(); 44 kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE)); 45 46 return kind; 47 }
可以看到,本地帐号包含更多的信息,而且光Phone number就包含了好多类型,如HOME,MOBILE,FAX等。
addDataKindStructuredName添加的是姓名相关的UI,都包含在在kind.fieldList中,每一个EditField基本可以认为是一个EditTest,如图:
而addDataKindPhone添加的是PhoneNumber相关的UI,kind.fieldList只包含一个EditField,可以看见只有一个EditText,但是kind.typeList添加了很多东西,可以通过点击Type Spinner来看到,共八个,和代码一致,如下图:
3. 上面分析了一个帐号类型类包含的东西,包含了一些DataKind,而每一个DataKind又有kind.typeList,kind.fieldList以及其他的属性。所以一个DataKind代表一个类型的信息,如Name,Phone number,Email等,但是Name里面包含很多具体信息,如prefix,suffix,first name等,Phone number里面包含一个EditText,但是有很多种号码类型等等,这样构成了一个完整的帐号类,而在加载UI的时候,就是根据当前的帐号来添加帐号里面包含的UI。具体如何加载后面分析。
4. 上面的分析涉及到两个辅助类:DataKind和EditField,下面分别看一下这两个类:
1 public final class DataKind { 2 3 public String resourcePackageName; 4 public String mimeType; 5 public int titleRes; 6 public int iconAltRes; 7 public int iconAltDescriptionRes; 8 public int weight; 9 public boolean secondary; 10 public boolean editable; 11 public StringInflater actionHeader; 12 public StringInflater actionAltHeader; 13 public StringInflater actionBody; 14 public boolean actionBodySocial = false; 15 public String typeColumn; 16 public int typeOverallMax; 17 public List<EditType> typeList; 18 public List<EditField> fieldList; 19 public ContentValues defaultValues; 20 public final int editorLayoutResourceId; 21 public SimpleDateFormat dateFormatWithoutYear; 22 public SimpleDateFormat dateFormatWithYear; 23 24 public int maxLinesForDisplay; 25 26 public DataKind() { 27 editorLayoutResourceId = R.layout.text_fields_editor_view; 28 maxLinesForDisplay = 1; 29 } 30 31 public DataKind(String mimeType, int titleRes, int weight, boolean editable, 32 int editorLayoutResourceId) { 33 this.mimeType = mimeType; 34 this.titleRes = titleRes; 35 this.weight = weight; 36 this.editable = editable; 37 this.typeOverallMax = -1; 38 this.editorLayoutResourceId = editorLayoutResourceId; 39 maxLinesForDisplay = 1; 40 } 41 }
由上可见,DataKind就是一个封装信息的类,只起到封装信息的作用。
1 public static final class EditField { 2 public String column; 3 public int titleRes; 4 public int inputType; 5 public int minLines; 6 public boolean optional; 7 public boolean shortForm; 8 public boolean longForm; 9 public boolean needFocus = false; 10 public boolean editable = true; 11 12 public InputFilter[] inputFilters; 13 14 public EditField(String column, int titleRes) { 15 this.column = column; 16 this.titleRes = titleRes; 17 } 18 19 public EditField(String column, int titleRes, int inputType) { 20 this(column, titleRes); 21 this.inputType = inputType; 22 } 23 24 public EditField setOptional(boolean optional) { 25 this.optional = optional; 26 return this; 27 } 28 public EditField setShortForm(boolean shortForm) { 29 this.shortForm = shortForm; 30 return this; 31 } 32 33 public EditField setLongForm(boolean longForm) { 34 this.longForm = longForm; 35 return this; 36 } 37 38 public EditField setMinLines(int minLines) { 39 this.minLines = minLines; 40 return this; 41 } 42 43 public boolean isMultiLine() { 44 return (inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) != 0; 45 } 46 47 public EditField setNeedFocus(boolean needFocus){ 48 this.needFocus = needFocus; 49 return this; 50 } 51 public EditField setEditable(boolean editable) { 52 this.editable = editable; 53 return this; 54 } 55 }
EditField与DataKind类似,封装了一些信息,至于这些信息是如何被解析的,在真正加载UI的时候再分析。