赞
踩
MTKRecipientEditTextView很明显就是拿原生的RecipientEditTextView修改而成,加入了不少功能,代码量直接翻倍,6263行。原理和原生的差不多,本文只整理新增的代码。
- /// M: for chip watcher. @{
- configFeatures(context);
- /// @}
添加了两个新功能
- private void configFeatures(Context context) {
- if ("com.android.mms".equals(context.getPackageName()) ||
- "com.mediatek.rcs.message".equals(context.getPackageName())) { //短信和融合短信应用的话启动新的特性
- mFeatureSet |= F_CHIP_AUTO_UPDATE | F_CHIP_WATCHER;
- }
- if ((mFeatureSet & F_CHIP_AUTO_UPDATE) != 0) {
- if (sContactObserver == null) {
- sContactObserver = new MTKContactObserver(getContext()); //联系人更新
- }
- }
- if ((mFeatureSet & F_CHIP_WATCHER) != 0) {
- mLastStringChanged = false;
- mChoreographer = Choreographer.getInstance();
- mChoreographer.postCallback(
- Choreographer.CALLBACK_INPUT, notifyChipChangedRunnable, null); // 依据垂直同步信号通知chip数据变化
- isRegisterVSync = true;
- }
- }
添加了对联系人数据库变化的监听扥Observer
static private MTKContactObserver sContactObserver = null; //mtk新增类,监听联系人数据库变化
还有回调
- private MTKContactObserver.ContactListener mContactListener = new MTKContactObserver.ContactListener() {
- @Override
- public void onContactChange(Set s) {
- Log.d("client", s.toString());
- handleContactChange(s);
- }
- };
在onAttachedToWindow和onDetachedFromWindow中完成回调的注册和卸载
- private void handleContactChange(Set s) {
- ...
- i = s.iterator();
- e = (MTKContactObserver.DirtyContactEvent) i.next();
- switch(e.eventType) {
- case MTKContactObserver.DirtyContactEvent.DELETE :
- postHandleContactDelete(deletedIDs);
- break;
- case MTKContactObserver.DirtyContactEvent.ADD :
- postHandleContactAdd(chips);
- break;
- case MTKContactObserver.DirtyContactEvent.UPDATE :
- postHandleContactUpdate(chips);
- break;
- }
- }
处理变化时会分为三种情况(删除、新加和更新)分别处理,后续不再继续分析,这部分代码是够长的
- private Runnable notifyChipChangedRunnable = new Runnable() {
- @Override
- public void run() {
- if (changedChipAddresses.size() != 0 || mLastStringChanged == true) {
- notifyChipChanged();
- ...
- }
- ...
- }
- };
notifyChipChanged最终会触发已注册的回调,回调是ChipWatcher
- public interface ChipWatcher {
-
- public void onChipChanged(ArrayList<RecipientEntry> allChips, ArrayList<String> changedChipAddresses, String lastString);
- }
- private ArrayList<ChipWatcher> mChipChangedListeners;
-
- public void addChipChangedListener(ChipWatcher watcher) { //注册
- if (null == mChipChangedListeners) {
- mChipChangedListeners = new ArrayList<ChipWatcher>();
- }
-
- mChipChangedListeners.add(watcher);
- }
-
- public void removeChipChangedListener(ChipWatcher watcher) { //卸载
- if (mChipChangedListeners != null) {
- int index = mChipChangedListeners.indexOf(watcher);
- if (mChipChangedListeners.indexOf(watcher) >= 0) {
- mChipChangedListeners.remove(index);
- }
- }
- }
除了在构造方法中,还有registerVSync,该方法是在chip数据有变化的时候调用,让通知事件在同步信号到来时触发
- private void registerVSync() {
- if (false == isRegisterVSync && mChoreographer != null) {
- mChoreographer.postCallback(
- Choreographer.CALLBACK_INPUT, notifyChipChangedRunnable, null);
- isRegisterVSync = true;
- }
- }
其它的变化比较零碎,没法系统整理
- protected void onConfigurationChanged(Configuration newConfig) {
-
- if (isPhoneQuery()) {
- registerGlobalLayoutListener();
- }
- ...
- }
该方法中主要处理了横屏和竖屏的变化
- private void registerGlobalLayoutListener() {
- ViewTreeObserver viewTreeObs = getViewTreeObserver();
- if (mGlobalLayoutListener == null) {
- mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- ...
- boolean isPortrait = (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
- if (isPortrait) {
- rotateToPortrait();
- } else {
- rotateToLandscape();
- }
- requestLayout();
- ...
- }
- };
- viewTreeObs.addOnGlobalLayoutListener(mGlobalLayoutListener);
- }
- }
- private static final char COMMIT_CHAR_CHINESE_COMMA = '\uFF0C'; /// M: Support chinese comma as seperator
-
- private static final char COMMIT_CHAR_CHINESE_SEMICOLON = '\uFF1B'; /// M: Support chinese semicolon as seperator
中文的逗号和分号也能作为分隔符了,妈妈不怕我打错半角还是全角符号了...
onRestoreInstanceState中加入了恢复chips的方法。
- public void onRestoreInstanceState(Parcelable state) {
- ...
- String text = getText().toString();
- ...
- MTKRecipientList recipientList = new MTKRecipientList(); //mtk新加的类,就是个List容器
- ...
- while ((tokenEnd = mTokenizer.findTokenEnd(text, tokenStart)) < text.length()) {
- String destination = text.substring(tokenStart, tokenEnd);
- tokenStart = tokenEnd + 2;
- recipientList.addRecipient(tokenizeName(destination), isPhoneNumber(destination) ? destination : tokenizeAddress(destination));
- x++;
- }
-
- appendList(recipientList); //依据列表恢复UI
- ...
- }
- public void appendList(MTKRecipientList recipientList) {
- ...
- for (int x = 0; x < recipientCnt; x++) {
- MTKRecipient recipient = recipientList.getRecipient(x);
- String text = recipient.getFormatString();
- ...
- if (!TextUtils.isEmpty(displayString)
- && TextUtils.getTrimmedLength(displayString) > 0) {
- mPendingChipsCount++;
- mPendingChips.add(text.toString()); //使用google原生的成员
- }
- }
- ...
- }
- ...
- if (mPendingChipsCount > 0) {
- postHandlePendingChips(); //就是使用google原生的方法恢复UI
- }
- mHandler.post(mAddTextWatcher);
- }
注意这个返回的是Bitmap,名字命名的有点问题
private Bitmap createSelectedChip(RecipientEntry contact, TextPaint paint)
- private Bitmap createUnselectedChip(RecipientEntry contact, TextPaint paint,
- boolean leaveBlankIconSpacer)
这样就选中和未选中的chip显示上区别就很大了
commitChip方法中,加入了号码宽松匹配的逻辑,
- for (int itemCnt = 0; itemCnt < adapterCount; itemCnt++) {
- RecipientEntry entry = (RecipientEntry) getAdapter().getItem(itemCnt);
- String displayName = entry.getDisplayName().toLowerCase();
- String destination = entry.getDestination();
- if (entry.getDestinationKind() == RecipientEntry.ENTRY_KIND_PHONE) {
- String currentNumber = PhoneNumberUtils.normalizeNumber(text);
- String queryNumber = PhoneNumberUtils.normalizeNumber(destination);
- if (PhoneNumberUtils.compare(currentNumber, queryNumber)) {
- printDebugLog(TAG, "[commitChip] match normalized destination. submit item: " + itemCnt);
- submitItemAtPosition(itemCnt);
- dismissDropDown();
- return true;
- }
- }
- }
- public boolean bringPointIntoView(int offset) {
- Log.d(TAG, "bringPointIntoView = " + offset);
- if (mForceEnableBringPointIntoView) {
- /// M: This case is for during expand or handlePendingChips
- /// force to scroll to botton since and temporary disable the chip touching functionality
- return super.bringPointIntoView(offset);
- } else if (mDisableBringPointIntoView || mSelectedChip != null) {
- return false;
- } else {
- return super.bringPointIntoView(offset);
- }
- }
相关的函数如下:
private void tryToAdjustChips() //联系人添加或者删除的时候使用
private void replaceChipOnSameTextRange(DrawableRecipientChip currentChip, int newChipWidth) //替换同一个chip,但是宽度有变化
replaceChipOnSameTextRange只处理第一个chip,如果宽度有限制的话,会缩短第一个chip的宽度并显示省略号,以容纳后续的chip显示
- private class PhoneNumberQueryAndReplacementTask extends AsyncTask<RecipientEntry, Void, Void> //commitChip中使用,匹配列表为0个时依据号码创建可能的chip
- private class DuplicateContactReplacementTask extends AsyncTask<Object, Void, Void> //删除重复联系人
- private class DeleteContactTask extends AsyncTask<List<Long>, Object, HashMap<DrawableRecipientChip, DrawableRecipientChip>> //处理数据库删除联系人的情况
- private class PreloadPhotoTask extends AsyncTask<Collection<RecipientEntry>, Void, Void> //预读多个联系人的头像
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。