2014-01-07 10:46:30 将百度空间里的东西移过来。

在前面的文章中我们分析了UI的加载,其中提到了一个重要的对象:RawContactDeltaList mState,我前面说过这个对象很重要,和联系人保存有关,现在就分析一下联系人到底是怎么保存的。

1. 千里追踪mState

ContactEditorFragment类 bindEditorsForNewContact()方法中创建了一个RawContactDelta对象insert,代码如下:

 private void bindEditorsForNewContact(AccountWithDataSet newAccount,
final AccountType newAccountType, RawContactDelta oldState,
AccountType oldAccountType) { final RawContact rawContact = new RawContact(mContext);
if (newAccount != null) {
rawContact.setAccount(newAccount);
} else {
rawContact.setAccountToLocalContact();
} RawContactDelta insert = new RawContactDelta(
ValuesDelta.fromAfter(rawContact.getValues())); if (mState == null) {
// Create state if none exists yet
mState = RawContactDeltaList.fromSingle(insert);
} else {
// Add contact onto end of existing state
mState.add(insert);
}
}

mState是一个RawContactDeltaList对象,而insert是一个RawContactDelta,上面代码中mState.add(insert),接着看,在bindEditors()方法中:

 RawContactDelta rawContactDelta = getFirstVisibleContact();
if (rawContactDelta != null) {
editor = createContactEditorView(rawContactDelta);
} private RawContactDelta getFirstVisibleContact() {
for (final RawContactDelta rawContactDelta : mState) {
if (!rawContactDelta.isVisible()) continue;
return rawContactDelta;
}
return null;
}

我们发现,createContactEditorView(rawContactDelta)中的rawContactDelta就是mState中的第一个对象,也就是前面创建的insert,那么insert究竟什么呢?

insert = (Uri=content://com.android.contacts/raw_contacts,
Values={
IdColumn=_id, FromTemplate=false, data_set=null, _id=-2,
account_name=Phone contacts,
account_type=com.android.localcontacts, },
Entries={})

可以看到,此时的insert只不过是一个只包含account信息,Entries为空的RawContactDelta对象,这个对象被当作参数传给createContactEditorView(rawContactDelta)方法,该方法中有:

 editor.setState(rawContactDelta, type, mViewIdGenerator, isEditingUserProfile());

我们前面分析过,editor是一个自定义的BaseRawContactEditorView,参数如下:

rawContactDelta:传进来的RawContactDelta对象;

type:账户类型,此处为LocalAccountType;

isEditingUserProfile():是否是要编辑“我自己”条目;

此时rawContactDelta对象进入BaseRawContactEditorView类,该类又调用了父类RawContactCommonEditorView的super.setState(state, type, vig, isProfile);该方法有一个重要的else语句块,前面分析过,如下:

 else {
// Otherwise use generic section-based editors
if (kind.fieldList == null) continue;
final KindSectionView section = (KindSectionView)mInflater.inflate(R.layout.item_kind_section, mFields, false);
section.setEnabled(isEnabled());
section.setState(kind, state, false, vig);
mFields.addView(section);
}

rawContactDelta又被当作参数传到KindSectionView, 通过代码section.setState(kind, state, false, vig);进入KindSectionView类,在rebuildFromState()方法中:

 if (hasEntries) {
for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) {
// Skip entries that aren't visible
if (!entry.isVisible())
continue;
if (isEmptyNoop(entry))
continue; createEditorView(entry);
}
}

可以看到,在这里mState.getMimeEntries(mKind.mimeType),根mKind.mimeType从mState中取出entry,然后把entry传到reateEditorView()方法中,有如下代码:

 private View createEditorView(ValuesDelta entry) {
final View view;
try {
view = mInflater.inflate(mKind.editorLayoutResourceId, mEditors, false);
} catch (Exception e) {
} view.setEnabled(isEnabled()); if (view instanceof Editor) {
Editor editor = (Editor) view;
editor.setDeletable(true);
editor.setValues(mKind, entry, mState, mReadOnly, mViewIdGenerator);
editor.setEditorListener(this);
}
mEditors.addView(view);
return view;
}

发现rawContactDelta以及根据mimetype生成的entry,又被当作参数传送,通过editor.setValues()方法,前面分析过editor是TextFieldsEditorView,那么看它的setValue()方法:

 int fieldCount = kind.fieldList.size();
mFieldEditTexts = new EditText[fieldCount];
for (int index = 0; index < fieldCount; index++) {
final EditField field = kind.fieldList.get(index);
final EditText fieldView = createFieldView(field.column);
...

这个方法应该很熟悉了,就是根据kind中fieldlist加载EditText的那个方法,同时还给生成的EditText注册了一个TextChangedListener, 如下:

 fieldView.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
// Trigger event for newly changed value
onFieldChanged(column, s.toString());
}
});

至于传进来的rawContactDelta和entry对象,调用了父类的setValues()方法,并将它们分别赋给了LabeledEditorView的mState和mEntry对象。既然给EditText对象添加了TextChangedListener,那么我们可以想到,应该是EditText中的内容发生改变后触发设置的监听方法,看这个监听方法:

 @Override
public void onFieldChanged(String column,
String value) {
if (!isFieldChanged(column, value)) {
return;
}
saveValue(column, value);
notifyEditorListener();
}

onFieldChanged()方法是在LabeledEditorView类里,和mState,mEntry对象在同一个类,我们看他的saveValue()方法:

 protected void saveValue(String column, String value) {
mEntry.put(column, value);
}

我们发现,最终把用户输入的信息放到了mEntry中,既然mEntry是从mState中生成的,那么这些值也就保存到mState,我们在saveValue()方法中打Log,如下:

 protected void saveValue(String column, String value) {
Log.d("David", "column = " + column);
Log.d("David", "value = " + value);
mEntry.put(column, value);
Log.d("David", "mState = " + mState);
}

如上图,我只在Name里面输入了“D”,我们看log截图:

column就是BaseAccountType中“StructuredName.GIVEN_NAME”,data2和数据库中的field是对应的,关于数据库,以后再分析;

 kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given,
FLAGS_PERSON_NAME).setNeedFocus(true));

value表示的是用户输入的值;

mState是从ContactEditorFragment中传进来的RawContactDelta对象,取自RawContactDeltaList mState,这个mState(ContactEditorFragment中的)对象非常重要,因为当用户保存联系人时,会用到它。截图中倒数第二行,mimetype=vnd.android.cursor.item/name, data2=D,也就是说,用户输入的联系人信息是保存在对应的mimetype,比如这个是Name,下面我同时输入姓名和号码,再看一下log:

其中我输入的Name=David,number=18611975588.

上面我们千里追mState,以及弄清楚了用户输入的数据是怎么组织的,只是因为当用户点击保存Button时,要用到这个对象,以及该对象包含的值。

Android Phonebook编写联系人UI加载及联系人保存流程(五)的更多相关文章

  1. Android Phonebook编写联系人UI加载及联系人保存流程(一)

    2014-01-06 17:05:11 将百度空间里的东西移过来. 本文适合ROM定制做Phonebook的童鞋看,其他人飘过即可- Phonebook添加/编辑联系人UI加载及保存联系人流程,是一系 ...

  2. Android Phonebook编写联系人UI加载及联系人保存流程(三)

    2014-01-07 09:54:13  将百度空间里的东西移过来. 本文从点击“添加联系人”Button开始,分析新建联系人页面UI是如何加载,以及新的联系人信息是如何保存的,借此,我们一探Phon ...

  3. Android Phonebook编写联系人UI加载及联系人保存流程(二)

    2014-01-06 17:18:29 1. Phonebook中新建/编辑联系人的UI不是用xml文件写的,它是随着帐号类型的改变来加载不同的UI,比如SIM联系人,只有Name.Phone Num ...

  4. Android Phonebook编写联系人UI加载及联系人保存流程(四)

    2014-01-07 10:23:22 将百度空间里的东西移过来. 5. KindSectionView KindSectionView是何方神圣呢?它又是怎么怎么和一个DataKind,以及一个Ra ...

  5. Android Phonebook编写联系人UI加载及联系人保存流程(六)

    2014-01-07 11:18:08 将百度空间里的东西移过来. 1. Save contact 我们前面已经写了四篇文章,做了大量的铺垫,总算到了这一步,见证奇迹的时刻终于到了. 用户添加了所有需 ...

  6. Android ViewPager Fragment使用懒加载提升性能

     Android ViewPager Fragment使用懒加载提升性能 Fragment在如今的Android开发中越来越普遍,但是当ViewPager结合Fragment时候,由于Androi ...

  7. Android三种基本的加载网络图片方式(转)

    Android三种基本的加载网络图片方式,包括普通加载网络方式.用ImageLoader加载图片.用Volley加载图片. 1. [代码]普通加载网络方式 ? 1 2 3 4 5 6 7 8 9 10 ...

  8. Android开发中如何解决加载大图片时内存溢出的问题

    Android开发中如何解决加载大图片时内存溢出的问题    在Android开发过程中,我们经常会遇到加载的图片过大导致内存溢出的问题,其实类似这样的问题已经屡见不鲜了,下面将一些好的解决方案分享给 ...

  9. Android引入高速缓存的异步加载全分辨率

    Android引进高速缓存的异步加载全分辨率 为什么要缓存 通过图像缩放,我们这样做是对的异步加载优化的大图,但现在的App这不仅是一款高清大图.图.动不动就是图文混排.以图代文,假设这些图片都载入到 ...

随机推荐

  1. Python学习笔记4—列表

    列表定义 >>> a=['] >>> type(a) <type 'list'> >>> bool(a) #列表有值,则为true T ...

  2. hdu 5713(状态压缩DP)

    要进行两次dp, 第一个,dp[i],1<=i<=(1<<n) 其中用i的二进制形式表示已选择的点. dp[i] 用来保存i中的点构成一个连通块,边集多少种可能. 转移方程: ...

  3. 转:C 函数调用栈

    第一篇: 转自:http://kingj.iteye.com/blog/1555017 本文转自  http://blog.csdn.net/eno_rez/article/details/21586 ...

  4. final简介

    final简介 final用来修饰方法和属性表示特殊的意义.修饰方法时表示方法不能被重写:修饰属性时表示属性不能被改变,这里属性又分为对象和基本类型,修饰基本类型表示基本类型赋值以后不能再被赋值,修饰 ...

  5. python模块以及导入出现ImportError: No module named 'xxx'问题

    python中,每个py文件被称之为模块,每个具有__init__.py文件的目录被称为包.只要模块或者包所在的目录在sys.path中,就可以使用import 模块或import 包来使用如果你要使 ...

  6. Spring MVC 基础注解之@RequestMapping、@Controller、(二)

    我现在学的是spring4.2 今天主要学习了Spring MVC注解 引入注解可以减少我们的代码量,优化我们的代码. @Controller:用于标识是处理器类: @RequestMapping:请 ...

  7. openid4java 使用记录[转载]

    [文章来源:http://r-j-r-a5438-163-com.iteye.com/blog/611351] 在项目中使用了openid4java进行开发,在开发过程中碰到过一些问题,在网上也找了很 ...

  8. MySQL内核深度优化

    版权声明:本文由简怀兵原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/179 来源:腾云阁 https://www.qclo ...

  9. wiki-editor语法

    1.==标题== 2.::换行缩进 3.[[文件:example.png]] 图片 4.** 名字|名字 链接

  10. struts2 拦截器的注册在strut.xml中

    根据需要:需要在struts.xml中注册拦截器为以下内容  根据自己的业务需要更改参数 <!DOCTYPE struts PUBLIC "-//Apache Software Fou ...