MTKRecipientEditTextView分析

MTKRecipientEditTextView很明显就是拿原生的RecipientEditTextView修改而成,加入了不少功能,代码量直接翻倍,6263行。原理和原生的差不多,本文只整理新增的代码。

新增的两个大功能

构造方法中会调用configFeatures。

        /// 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;
        }
    }
处理变化时会分为三种情况(删除、新加和更新)分别处理,后续不再继续分析,这部分代码是够长的 大笑


对外通知chips数据变化

Choreographer详细可见 Android Choreographer 源码分析,使用它可以在下次同步信号的时候触发回调。vsync的初衷是同步显示器和系统本身UI显示的频率,防止画面的撕裂,使用Choreographer也是为了画面的显示顺滑。

    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

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);
    }

createSelectedChip

注意这个返回的是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;
                            }
                        }
                    }

bringPointIntoView

重写了bringPointIntoView,这个方法可以让多行的TextView滚动到指定位置,mtk添加了强制和禁止使用该方法的逻辑
    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);
        }
    }

chip显示调整

相关的函数如下:

private void tryToAdjustChips() //联系人添加或者删除的时候使用

private void replaceChipOnSameTextRange(DrawableRecipientChip currentChip, int newChipWidth)  //替换同一个chip,但是宽度有变化

replaceChipOnSameTextRange只处理第一个chip,如果宽度有限制的话,会缩短第一个chip的宽度并显示省略号,以容纳后续的chip显示

AsyncTask

新增了4个新的AsyncTask
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> //预读多个联系人的头像


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值