标签:总结 hot ora blank adapter 通过 token content 高亮
RecipientEditTextView是Android原生短信和电子邮件中用到的控件,代码位于frameworks/opt/chips(mtk代码中有对其修改,位于frameworks/ex/chips),会编译成libchips的jar包,app在编译时把它作为静态库编译。
如图所示,其中有“+10”字样的所在行就是RecipientEditTextView控件。每个号码有对应联系人的话会显示相应头像和名称,图像为一个圆角矩形,代码中对应的数据机构为一个chip。10表示已有十个chip(其实从代码中可以看出10本身也是一个chip,名称为mMoreChip),没有焦点的时候会收缩显示,点击获取焦点后会展开显示全部chip(但是有数量限制的,默认数量限制是50),可以滑动查看所有chip。控件获取焦点后再次点击某个chip,效果如下:
当chip对应联系人有多个号码的时候,可以选择并替换当前号码。chip中头像消失,显示删除按键,点击后删除该chip(该特性是mtk所加的功能,google原生此功能不在图示位置)。如果该chip无对应联系人的时,该chip会显示为文本编辑状态,并且自动移动到最后一个chip的位置,这个是因为基类MultiAutoCompleteTextView只能在最后的位置进行编辑操作。
总结下RecipientEditTextView,它其实本质就是textview,文本内容是以分隔符(逗号或者引号)分隔的一系列联系方式(一般是号码,也可以是邮件地址),拿常用的电子邮件收件地址栏对比很容易理解。它的最大作用是UI的显示,显示每个联系方式为图片方式,并提供了附加的一些操作,例如pc上foxmail收件人地址一栏不是原原本本的邮件地址,而是会显示为邮件地址对应联系人的名称,点击后可以查看联系人详情,编辑和查看往来邮件等。RecipientEditTextView就是要提供类似于foxmail收件人地址栏的UI显示效果和功能。
void setSelected(boolean selected); //设置当前chip是否选中 CharSequence getDisplay(); //获取显示内容,一般就是联系人名称 long getContactId(); //联系人数据库id RecipientEntry getEntry(); //联系人信息 CharSequence getOriginalText(); //原始的字符串内容,一般就是号码
Rect getBounds(); //获取图片显示区域 void draw(Canvas canvas); //图片绘制方法
public class InvisibleRecipientChip extends ReplacementSpan implements DrawableRecipientChip实现了DrawableRecipientChip接口
@Override public void draw(final Canvas canvas, final CharSequence text, final int start, final int end, final float x, final int top, final int y, final int bottom, final Paint paint) { // Do nothing. } @Override public Rect getBounds() { return new Rect(0, 0, 0, 0); }不绘制任何东西,这个在chip过多或者控件失去焦点显示为收缩状态的时候使用。
public SimpleRecipientChip(final RecipientEntry entry) { mDisplay = entry.getDisplayName(); mValue = entry.getDestination().trim(); mContactId = entry.getContactId(); mDirectoryId = entry.getDirectoryId(); mLookupKey = entry.getLookupKey(); mDataId = entry.getDataId(); mEntry = entry; }构造函数中依据RecipientEntry填充各个字段
protected Drawable mDrawable; public ReplacementDrawableSpan(Drawable drawable) { super(); mDrawable = drawable; } @Override public void draw(Canvas canvas, CharSequence charSequence, int start, int end, float x, int top, int y, int bottom, Paint paint) { canvas.save(); int transY = (bottom - mDrawable.getBounds().bottom + top) / 2; canvas.translate(x, transY); mDrawable.draw(canvas); canvas.restore(); } protected Rect getBounds() { return mDrawable.getBounds(); }最重要的是实现了图片的绘制,该图片会替换对应文字。
public class VisibleRecipientChip extends ReplacementDrawableSpan implements DrawableRecipientChip类似InvisibleRecipientChip,不过这个是真正负责显示chip的类
@Override public Rect getBounds() { return super.getBounds(); } @Override public void draw(final Canvas canvas) { mDrawable.draw(canvas); }DrawableRecipientChip的两个方法都有实现,getBounds就是直接调用基类ReplacementDrawableSpan的方法。
public void setAccount(Account account);该文件只有一个接口,设置账户。
public static boolean supportsChipsUi() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; }只有一个方法,判断是否系统版本是否支持该控件。
public static final int PHOTO_CACHE_SIZE = 20; //缓存数常量 void populatePhotoBytesAsync(RecipientEntry entry, PhotoManagerCallback callback); //唯一的方法,从异步获取照片,然后回调PhotoManagerCallback interface PhotoManagerCallback { //三个回调方法 void onPhotoBytesPopulated(); //如果在缓冲中,则直接返回,不用走异步获取的流程 void onPhotoBytesAsynchronouslyPopulated(); //异步返回结果 void onPhotoBytesAsyncLoadFailed(); //获取失败 }
static abstract class Query { private final String[] mProjection; //查询时使用的projection private final Uri mContentFilterUri; //查询使用的filter uri private final Uri mContentUri; //查询时使用的uri public static final int NAME = 0; // String 使用cursor时的常量,名字 public static final int DESTINATION = 1; // String 号码或者邮件地址 public static final int DESTINATION_TYPE = 2; // int 号码类型int值 public static final int DESTINATION_LABEL = 3; // String 号码类型字符串 public static final int CONTACT_ID = 4; // long 联系人Id public static final int DATA_ID = 5; // long 号码对应data id public static final int PHOTO_THUMBNAIL_URI = 6; // String 小头像uri public static final int DISPLAY_NAME_SOURCE = 7; // int 名字source public static final int LOOKUP_KEY = 8; // String lookup值,用于查询联系人 public static final int MIME_TYPE = 9; // String mime类型 public Query(String[] projection, Uri contentFilter, Uri content) { mProjection = projection; mContentFilterUri = contentFilter; mContentUri = content; } ... public abstract CharSequence getTypeLabel(Resources res, int type, CharSequence label); }定义了Query类,常量字段含义见联系人存储ContactsProvider表分析,并且初始化了两个实例:
public static final Query PHONE = new Query(new String[] {...}, Phone.CONTENT_FILTER_URI, Phone.CONTENT_URI) { @Override public CharSequence getTypeLabel(Resources res, int type, CharSequence label) { return Phone.getTypeLabel(res, type, label); } }; public static final Query EMAIL...一个用于查询号码对应联系人,一个用于查询邮件地址对应联系人。
protected RecipientEntry(int entryType, String displayName, String destination, int destinationType, String destinationLabel, long contactId, Long directoryId, long dataId, Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid, String lookupKey) { mEntryType = entryType; mIsFirstLevel = isFirstLevel; mDisplayName = displayName; mDestination = destination; mDestinationType = destinationType; mDestinationLabel = destinationLabel; mContactId = contactId; mDirectoryId = directoryId; mDataId = dataId; mPhotoThumbnailUri = photoThumbnailUri; mPhotoBytes = null; mIsDivider = false; mIsValid = isValid; mLookupKey = lookupKey; }对应成员含义基本对应对应Query中的常量,该类中有几个construct开头的方法以便生成RecipientEntry:
public static RecipientEntry constructFakeEntry(final String address, final boolean isValid) { //fake表示虚假的联系人,其实只有address有意义
final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address);
final String tokenizedAddress = tokens.length > 0 ? tokens[0].getAddress() : address;
return new RecipientEntry(ENTRY_TYPE_PERSON, tokenizedAddress, tokenizedAddress,
INVALID_DESTINATION_TYPE, null, INVALID_CONTACT, null /* directoryId */,
INVALID_CONTACT, null, true, isValid, null /* lookupKey */);
}
public static RecipientEntry constructFakePhoneEntry(final String phoneNumber, //生成address为号码的虚假RecipientEntry
final boolean isValid) {
...
}
public static RecipientEntry constructGeneratedEntry(String display, String address,
boolean isValid) { //依据名字和address生成RecipientEntry,但是ContactsProvider中并无对应的数据,只有邮件会使用
...
}
public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource,
String destination, int destinationType, String destinationLabel, long contactId,
Long directoryId, long dataId, Uri photoThumbnailUri, boolean isValid,
String lookupKey) {
...
} //该方法和后续的方法是给MultiAutoCompleteTextView过滤联系人的时候使用的,top是下拉列表第一条数据对应的RecipientEntry,second是后续条目对应的
public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource,
String destination, int destinationType, String destinationLabel, long contactId,
Long directoryId, long dataId, String thumbnailUriAsString, boolean isValid,
String lookupKey) {
...
}
public static RecipientEntry constructSecondLevelEntry(String displayName,
int displayNameSource, String destination, int destinationType,
String destinationLabel, long contactId, Long directoryId, long dataId,
String thumbnailUriAsString, boolean isValid, String lookupKey) {
...
}
TopLevel和SecondLevel从代码中看只会导致mIsDivider的值不同,top中赋值为true,second中赋值为false,但是代码中并无任何地方使用这个值,所以目前这两个方法是完全一样的效果。class SingleRecipientArrayAdapter extends ArrayAdapter<RecipientEntry>
public class RecipientAlternatesAdapter extends CursorAdapter该adapter依然是给单击chip后弹出的dialog使用,被RecipientEditTextView中的showAlternates方法使用,这个可以有多个条目,点击后替换当前的chip的号码或邮件地址(所以名称中有alternate字样)。
public interface RecipientMatchCallback { public void matchesFound(Map<String, RecipientEntry> results); /** * Called with all addresses that could not be resolved to valid recipients. */ public void matchesNotFound(Set<String> unfoundAddresses); } public static void getMatchingRecipients(Context context, BaseRecipientAdapter adapter, ArrayList<String> inAddresses, Account account, RecipientMatchCallback callback) { ... }getMatchingRecipients是更新chip数据的主要方法,inAddresses是控件中的address列表,依据address查询数据库最终生成对应的RecipientEntry,回调callback得到的结果是以address为key,RecipientEntry为value的Map对象。
public class BaseRecipientAdapter extends BaseAdapter implements Filterable, AccountSpecifier, PhotoManager.PhotoManagerCallbackMultiAutoCompleteTextView匹配列表布局使用
@Override public Filter getFilter() { return new DefaultFilter(); }过滤器是自定义的新类,查询数据库并返回RecipientEntry,生成的结果在三个成员中
private LinkedHashMap<Long, List<RecipientEntry>> mEntryMap; //最原始的数据 private List<RecipientEntry> mNonAggregatedEntries; //不在ContactsProvider中存储的数据 private Set<String> mExistingDestinations; //号码集合,去重mNonAggregatedEntries这个很难理解,因为我从来没有见过有国内有应用实现DirectoryProvider(见联系人存储ContactsProvider表分析),google系列的应用国内又无法使用。这种数据来源于其它app,例如google talk,可以通过Android联系人的标准查询查询数据。国内的qq等数据都是自成一体,不会共享出来的。
@Override public View getView(int position, View convertView, ViewGroup parent) { ... return mDropdownChipLayouter.bindView(convertView, parent, entry, position, AdapterType.BASE_RECIPIENT, constraint); }
标签:总结 hot ora blank adapter 通过 token content 高亮
原文地址:http://blog.csdn.net/firedancer0089/article/details/70207359