当前位置:   article > 正文

MTKRecipientEditTextView分析

mtkrecipientedittextview

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

新增的两个大功能

构造方法中会调用configFeatures。

  1. /// M: for chip watcher. @{
  2. configFeatures(context);
  3. /// @}
添加了两个新功能

  1. private void configFeatures(Context context) {
  2. if ("com.android.mms".equals(context.getPackageName()) ||
  3. "com.mediatek.rcs.message".equals(context.getPackageName())) { //短信和融合短信应用的话启动新的特性
  4. mFeatureSet |= F_CHIP_AUTO_UPDATE | F_CHIP_WATCHER;
  5. }
  6. if ((mFeatureSet & F_CHIP_AUTO_UPDATE) != 0) {
  7. if (sContactObserver == null) {
  8. sContactObserver = new MTKContactObserver(getContext()); //联系人更新
  9. }
  10. }
  11. if ((mFeatureSet & F_CHIP_WATCHER) != 0) {
  12. mLastStringChanged = false;
  13. mChoreographer = Choreographer.getInstance();
  14. mChoreographer.postCallback(
  15. Choreographer.CALLBACK_INPUT, notifyChipChangedRunnable, null); // 依据垂直同步信号通知chip数据变化
  16. isRegisterVSync = true;
  17. }
  18. }

联系人更新

添加了对联系人数据库变化的监听扥Observer

  static private MTKContactObserver sContactObserver = null; //mtk新增类,监听联系人数据库变化
还有回调
  1. private MTKContactObserver.ContactListener mContactListener = new MTKContactObserver.ContactListener() {
  2. @Override
  3. public void onContactChange(Set s) {
  4. Log.d("client", s.toString());
  5. handleContactChange(s);
  6. }
  7. };
在onAttachedToWindow和onDetachedFromWindow中完成回调的注册和卸载
  1. private void handleContactChange(Set s) {
  2. ...
  3. i = s.iterator();
  4. e = (MTKContactObserver.DirtyContactEvent) i.next();
  5. switch(e.eventType) {
  6. case MTKContactObserver.DirtyContactEvent.DELETE :
  7. postHandleContactDelete(deletedIDs);
  8. break;
  9. case MTKContactObserver.DirtyContactEvent.ADD :
  10. postHandleContactAdd(chips);
  11. break;
  12. case MTKContactObserver.DirtyContactEvent.UPDATE :
  13. postHandleContactUpdate(chips);
  14. break;
  15. }
  16. }
处理变化时会分为三种情况(删除、新加和更新)分别处理,后续不再继续分析,这部分代码是够长的 大笑


对外通知chips数据变化

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

  1. private Runnable notifyChipChangedRunnable = new Runnable() {
  2. @Override
  3. public void run() {
  4. if (changedChipAddresses.size() != 0 || mLastStringChanged == true) {
  5. notifyChipChanged();
  6. ...
  7. }
  8. ...
  9. }
  10. };
notifyChipChanged最终会触发已注册的回调,回调是ChipWatcher

  1. public interface ChipWatcher {
  2. public void onChipChanged(ArrayList<RecipientEntry> allChips, ArrayList<String> changedChipAddresses, String lastString);
  3. }
  4. private ArrayList<ChipWatcher> mChipChangedListeners;
  5. public void addChipChangedListener(ChipWatcher watcher) { //注册
  6. if (null == mChipChangedListeners) {
  7. mChipChangedListeners = new ArrayList<ChipWatcher>();
  8. }
  9. mChipChangedListeners.add(watcher);
  10. }
  11. public void removeChipChangedListener(ChipWatcher watcher) { //卸载
  12. if (mChipChangedListeners != null) {
  13. int index = mChipChangedListeners.indexOf(watcher);
  14. if (mChipChangedListeners.indexOf(watcher) >= 0) {
  15. mChipChangedListeners.remove(index);
  16. }
  17. }
  18. }
除了在构造方法中,还有registerVSync,该方法是在chip数据有变化的时候调用,让通知事件在同步信号到来时触发

  1. private void registerVSync() {
  2. if (false == isRegisterVSync && mChoreographer != null) {
  3. mChoreographer.postCallback(
  4. Choreographer.CALLBACK_INPUT, notifyChipChangedRunnable, null);
  5. isRegisterVSync = true;
  6. }
  7. }


其它的变化

其它的变化比较零碎,没法系统整理

配置变化事件处理

  1. protected void onConfigurationChanged(Configuration newConfig) {
  2. if (isPhoneQuery()) {
  3. registerGlobalLayoutListener();
  4. }
  5. ...
  6. }

该方法中主要处理了横屏和竖屏的变化

  1. private void registerGlobalLayoutListener() {
  2. ViewTreeObserver viewTreeObs = getViewTreeObserver();
  3. if (mGlobalLayoutListener == null) {
  4. mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
  5. @Override
  6. public void onGlobalLayout() {
  7. ...
  8. boolean isPortrait = (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
  9. if (isPortrait) {
  10. rotateToPortrait();
  11. } else {
  12. rotateToLandscape();
  13. }
  14. requestLayout();
  15. ...
  16. }
  17. };
  18. viewTreeObs.addOnGlobalLayoutListener(mGlobalLayoutListener);
  19. }
  20. }

中文分隔符

  1. private static final char COMMIT_CHAR_CHINESE_COMMA = '\uFF0C'; /// M: Support chinese comma as seperator
  2. private static final char COMMIT_CHAR_CHINESE_SEMICOLON = '\uFF1B'; /// M: Support chinese semicolon as seperator
中文的逗号和分号也能作为分隔符了,妈妈不怕我打错半角还是全角符号了...

onRestoreInstanceState

onRestoreInstanceState中加入了恢复chips的方法。

  1. public void onRestoreInstanceState(Parcelable state) {
  2. ...
  3. String text = getText().toString();
  4. ...
  5. MTKRecipientList recipientList = new MTKRecipientList(); //mtk新加的类,就是个List容器
  6. ...
  7. while ((tokenEnd = mTokenizer.findTokenEnd(text, tokenStart)) < text.length()) {
  8. String destination = text.substring(tokenStart, tokenEnd);
  9. tokenStart = tokenEnd + 2;
  10. recipientList.addRecipient(tokenizeName(destination), isPhoneNumber(destination) ? destination : tokenizeAddress(destination));
  11. x++;
  12. }
  13. appendList(recipientList); //依据列表恢复UI
  14. ...
  15. }
  1. public void appendList(MTKRecipientList recipientList) {
  2. ...
  3. for (int x = 0; x < recipientCnt; x++) {
  4. MTKRecipient recipient = recipientList.getRecipient(x);
  5. String text = recipient.getFormatString();
  6. ...
  7. if (!TextUtils.isEmpty(displayString)
  8. && TextUtils.getTrimmedLength(displayString) > 0) {
  9. mPendingChipsCount++;
  10. mPendingChips.add(text.toString()); //使用google原生的成员
  11. }
  12. }
  13. ...
  14. }
  15. ...
  16. if (mPendingChipsCount > 0) {
  17. postHandlePendingChips(); //就是使用google原生的方法恢复UI
  18. }
  19. mHandler.post(mAddTextWatcher);
  20. }

createSelectedChip

注意这个返回的是Bitmap,名字命名的有点问题

   private Bitmap createSelectedChip(RecipientEntry contact, TextPaint paint) 
  1. private Bitmap createUnselectedChip(RecipientEntry contact, TextPaint paint,
  2.             boolean leaveBlankIconSpacer) 

这样就选中和未选中的chip显示上区别就很大了


宽松号码匹配

commitChip方法中,加入了号码宽松匹配的逻辑,

  1. for (int itemCnt = 0; itemCnt < adapterCount; itemCnt++) {
  2. RecipientEntry entry = (RecipientEntry) getAdapter().getItem(itemCnt);
  3. String displayName = entry.getDisplayName().toLowerCase();
  4. String destination = entry.getDestination();
  5. if (entry.getDestinationKind() == RecipientEntry.ENTRY_KIND_PHONE) {
  6. String currentNumber = PhoneNumberUtils.normalizeNumber(text);
  7. String queryNumber = PhoneNumberUtils.normalizeNumber(destination);
  8. if (PhoneNumberUtils.compare(currentNumber, queryNumber)) {
  9. printDebugLog(TAG, "[commitChip] match normalized destination. submit item: " + itemCnt);
  10. submitItemAtPosition(itemCnt);
  11. dismissDropDown();
  12. return true;
  13. }
  14. }
  15. }

bringPointIntoView

重写了bringPointIntoView,这个方法可以让多行的TextView滚动到指定位置,mtk添加了强制和禁止使用该方法的逻辑
  1. public boolean bringPointIntoView(int offset) {
  2. Log.d(TAG, "bringPointIntoView = " + offset);
  3. if (mForceEnableBringPointIntoView) {
  4. /// M: This case is for during expand or handlePendingChips
  5. /// force to scroll to botton since and temporary disable the chip touching functionality
  6. return super.bringPointIntoView(offset);
  7. } else if (mDisableBringPointIntoView || mSelectedChip != null) {
  8. return false;
  9. } else {
  10. return super.bringPointIntoView(offset);
  11. }
  12. }

chip显示调整

相关的函数如下:

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

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

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

AsyncTask

新增了4个新的AsyncTask
  1. private class PhoneNumberQueryAndReplacementTask extends AsyncTask<RecipientEntry, Void, Void> //commitChip中使用,匹配列表为0个时依据号码创建可能的chip
  2. private class DuplicateContactReplacementTask extends AsyncTask<Object, Void, Void> //删除重复联系人
  3. private class DeleteContactTask extends AsyncTask<List<Long>, Object, HashMap<DrawableRecipientChip, DrawableRecipientChip>> //处理数据库删除联系人的情况
  4. private class PreloadPhotoTask extends AsyncTask<Collection<RecipientEntry>, Void, Void> //预读多个联系人的头像


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/529327
推荐阅读
相关标签
  

闽ICP备14008679号