拨号盘概览

先来看看几张原图

工程依赖关系

com.android.dialer是主工程依赖于

com.android.contacts.common工程和com.android.phone.common工程

com.android.contacts.common又依赖于

com.android.phone.common工程和com.android.common工程

另外一些support包也作为链接工程被引入,以上代码均取自google源码

关键类

DialtactsActivity

com.android.dialer.DialtactsActivity
public class DialtactsActivity extends TransactionSafeActivity 。。。{// Fragment containing the dialpad that slides into viewprotected DialpadFragment mDialpadFragment;// Fragment for searching phone numbers using the alphanumeric keyboard.private RegularSearchFragment mRegularSearchFragment;// Fragment for searching phone numbers using the dialpad.private SmartDialSearchFragment mSmartDialSearchFragment;// Fragment containing the speed dial list, call history list, and all contacts list.    private ListsFragment mListsFragment;private DialerDatabaseHelper mDialerDatabaseHelper;private FloatingActionButtonController mFloatingActionButtonController;...... ............ .....}

主要有以下几个关键的成员变量

com.android.dialer.dialpad .DialpadFragment // 拨号盘fragment

com.android.dialer.list.RegularSearchFragment // 联系人搜索fragment

com.android.dialer.list.SmartDialSearchFragment // 拨号搜索fragment

com.android.dialer.list.ListsFragment // TAB页fragment,包含快速联系人,最近通话记录,联系人列表三个子fragment

com.android.dialer.database.DialerDatabaseHelper // 拨号搜索数据库SQLiteOpenHelper对象

com.android.contacts.common.widget.FloatingActionButtonController // 悬浮按钮控制器

再看看onCreate里的主要实现

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.dialtacts_activity);final ActionBar actionBar = getSupportActionBar();actionBar.setCustomView(R.layout.search_edittext);// 给actionbar设置自定义view (SearchEditTextLayout)SearchEditTextLayout searchEditTextLayout = (SearchEditTextLayout) actionBar.getCustomView().findViewById(R.id.search_view_container);// 给SearchEditTextLayout添加管理器ActionBarControllermActionBarController = new ActionBarController(this, searchEditTextLayout);final View floatingActionButtonContainer = findViewById(R.id.floating_action_button_container);ImageButton floatingActionButton = (ImageButton) findViewById(R.id.floating_action_button);floatingActionButton.setOnClickListener(this);// 用FloatingActionButtonController管理悬浮按钮mFloatingActionButtonController = new FloatingActionButtonController(this,floatingActionButtonContainer, floatingActionButton);// 添加ListsFragmentgetFragmentManager().beginTransaction().add(R.id.dialtacts_frame, new ListsFragment(), TAG_FAVORITES_FRAGMENT).commit();// 初始化单例对象DialerDatabaseHelpermDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this);SmartDialPrefix.initializeNanpSettings(this);}

ListsFragment

ListsFragment是主fragment,结构如下:

public class ListsFragment extends Fragment{private ViewPager mViewPager;private ViewPagerTabs mViewPagerTabs;  // 自定义TAB标签,继承自HorizontalScrollViewprivate ViewPagerAdapter mViewPagerAdapter;// 拖拽常用联系人时悬浮视图private RemoveView mRemoveView;private View mRemoveViewContent;// 常用联系人fragmentprivate SpeedDialFragment mSpeedDialFragment;// 最近通话记录fragmentprivate CallLogFragment mHistoryFragment;// 联系人列表fragmentprivate AllContactsFragment mAllContactsFragment;// Voicemail列表fragmentprivate CallLogFragment mVoicemailFragment;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {final View parentView = inflater.inflate(R.layout.lists_fragment, container, false);mViewPager = (ViewPager) parentView.findViewById(R.id.lists_pager);mViewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());mViewPager.setAdapter(mViewPagerAdapter);mViewPager.setOffscreenPageLimit(TAB_COUNT_WITH_VOICEMAIL - 1);mViewPager.setOnPageChangeListener(this);showTab(TAB_INDEX_SPEED_DIAL);...... ......  ...... ......mViewPagerTabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header);mViewPagerTabs.configureTabIcons(mTabIcons);mViewPagerTabs.setViewPager(mViewPager);addOnPageChangeListener(mViewPagerTabs);mRemoveView = (RemoveView) parentView.findViewById(R.id.remove_view);mRemoveViewContent = parentView.findViewById(R.id.remove_view_content);return parentView;}
}

ListsFragment最多可以显示四个fragment,有个VisualVoicemailCallLogFragment显示一种特定的通话记录(提供视频语音邮件服务)

类型为Calls.VOICEMAIL_TYPE,需要运营商支持,只有存在该类通话记录才会显示该TAB页,国内运营商暂不支持

OldSpeedDialFragment显示常用联系人列表

// 查询源数据的LoaderCallbacks
private static final class ContactTileLoaderListenerimplements LoaderManager.LoaderCallbacks<Cursor> {private final OldSpeedDialFragment fragment;// 源数据BaseAdapterprivate final PhoneFavoritesTileAdapter adapter;ContactTileLoaderListener(OldSpeedDialFragment fragment, PhoneFavoritesTileAdapter adapter) {this.fragment = fragment;this.adapter = adapter;}@Overridepublic CursorLoader onCreateLoader(int id, Bundle args) {return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(fragment.getContext());}@Overridepublic void onLoadFinished(Loader<Cursor> loader, Cursor data) {adapter.setContactCursor(data);fragment.setEmptyViewVisibility(adapter.getCount() == 0);FragmentUtils.getParentUnsafe(fragment, HostInterface.class).setHasFrequents(adapter.getNumFrequents() > 0);}@Overridepublic void onLoaderReset(Loader<Cursor> loader) {}}

DialpadFragment

DialpadFragment显示拨号盘fragment

在DialtactsActivity中添加如下

private void showDialpadFragment(boolean animate) {if (mDialpadFragment == null) {mDialpadFragment = new DialpadFragment();ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT);} else {ft.show(mDialpadFragment);}}

第一次显示时动态添加进去,后续动态控制显示隐藏

public class DialpadFragment extends Fragment{private DialpadView mDialpadView; // 拨号数字面板(包括输入号码框)private EditText mDigits;          // 输入号码框private ToneGenerator mToneGenerator; // DTMF音播放器private ListView mDialpadChooser;     // 通话状态时显示的视图private DialpadChooserAdapter mDialpadChooserAdapter;// 通话状态时显示的视图adapter@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {// 横竖屏加载不同的布局final View fragmentView = inflater.inflate(R.layout.dialpad_fragment, container,false);fragmentView.buildLayer();mDialpadView = (DialpadView) fragmentView.findViewById(R.id.dialpad_view);mDialpadView.setCanDigitsBeEdited(true);mDigits = mDialpadView.getDigits();...... ........... ......PhoneNumberFormatter.setPhoneNumberFormattingTextWatcher(getActivity(), mDigits);  // 格式化输入框中的号码// Check for the presence of the keypadView oneButton = fragmentView.findViewById(R.id.one);if (oneButton != null) {  // 绑定各个数字按键onPress事件configureKeypadListeners(fragmentView);}...... ............ ......mDialpadChooser = (ListView) fragmentView.findViewById(R.id.dialpadChooser);mDialpadChooser.setOnItemClickListener(this);...... ..... ...... ......return fragmentView;}}

横屏和竖屏所加载的拨号面板布局是不一样的

DialpadView是个自定义视图,主要用于显示数字按键和输入号码框

public class DialpadView extends LinearLayout {private EditText mDigits;     // 输入号码框private ImageButton mDelete; // 删除按钮private void setupKeypad() {...... ............ ......DialpadKeyButton dialpadKey;TextView numberView;TextView lettersView;...... ............ ......for (int i = 0; i < mButtonIds.length; i++) {dialpadKey = (DialpadKeyButton) findViewById(mButtonIds[i]);numberView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_number);lettersView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_letters);...... ............ ......final RippleDrawable rippleBackground = (RippleDrawable)getDrawableCompat(getContext(), R.drawable.btn_dialpad_key);if (mRippleColor != null) {rippleBackground.setColor(mRippleColor);            }numberView.setText(numberString);numberView.setElegantTextHeight(false);dialpadKey.setContentDescription(numberContentDescription);dialpadKey.setBackground(rippleBackground); // 设置数字按键水波纹背景色if (lettersView != null) {lettersView.setText(resources.getString(letterIds[i]));}}...... ............ ......}
public void animateShow() {  // 显示拨号面板时各个数字按键的动画效果...... ............ ......for (int i = 0; i < mButtonIds.length; i++) {...... ............ ......ViewPropertyAnimator animator = dialpadKey.animate();if (mIsLandscape) {// Landscape orientation requires translation along the X axis.// For RTL locales, ensure we translate negative on the X axis.dialpadKey.setTranslationX((mIsRtl ? -1 : 1) * mTranslateDistance);animator.translationX(0);} else {// Portrait orientation requires translation along the Y axis.dialpadKey.setTranslationY(mTranslateDistance);animator.translationY(0);}animator.setInterpolator(AnimUtils.EASE_OUT_EASE_IN).setStartDelay(delay).setDuration(duration).setListener(showListener).start();}}
}

SmartDialSearchFragment RegularSearchFragment

SmartDialSearchFragment显示拨号搜索结果fragment(在拨号面板输入数字时显示)

RegularSearchFragment显示联系人搜索结果fragment(在actionbar输入框输入字符时显示)

在DialtactsActivity中进入或退出搜索模式时动态添加移除

private void enterSearchUi(boolean smartDialSearch, String query, boolean animate) {...... ............ ......if (fragment == null) {if (smartDialSearch) {fragment = new SmartDialSearchFragment();} else {fragment = ObjectFactory.newRegularSearchFragment();...... ............ ......}transaction.add(R.id.dialtacts_frame, fragment, tag);} else {transaction.show(fragment);}...... ............ ......}private void exitSearchUi() {...... ............ ......final FragmentTransaction transaction = getFragmentManager().beginTransaction();if (mSmartDialSearchFragment != null) {transaction.remove(mSmartDialSearchFragment);}if (mRegularSearchFragment != null) {transaction.remove(mRegularSearchFragment);}transaction.commit();mListsFragment.getView().animate().alpha(1).withLayer();...... ............ ......mActionBarController.onSearchUiExited();}

拨号搜素只能通过拨号面板的输入数字,支持T9搜索,但是原生不支持拼音检索

public class SmartDialSearchFragment extends SearchFragment{@Overrideprotected ContactEntryListAdapter createListAdapter() {SmartDialNumberListAdapter adapter = new SmartDialNumberListAdapter(getActivity());adapter.setUseCallableUri(super.usesCallableUri());adapter.setQuickContactEnabled(true);// Set adapter's query string to restore previous instance state.adapter.setQueryString(getQueryString());adapter.setListener(this);return adapter;}@Overridepublic Loader<Cursor> onCreateLoader(int id, Bundle args) {// Smart dialing does not support Directory Load, falls back to normal search instead.if (id == getDirectoryLoaderId()) {return super.onCreateLoader(id, args);} else {final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();SmartDialCursorLoader loader = new SmartDialCursorLoader(super.getContext());adapter.configureLoader(loader);return loader;}}
}

联系人搜索则通过软键盘输入,不过不支持T9搜索

public class RegularSearchFragment extends SearchFragment{@Overrideprotected ContactEntryListAdapter createListAdapter() {RegularSearchListAdapter adapter = new RegularSearchListAdapter(getActivity());adapter.setDisplayPhotos(true);adapter.setUseCallableUri(usesCallableUri());adapter.setListener(this);return adapter;}
}

从类关系图上可以得知两个fragment和对应的adapter都继承于同一个父类,最终都派生自ContactsCommon工程里的模板类ContactEntryListFragment

public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter>extends Fragment{private T mAdapter;          // 模板adapterprivate View mView;private ListView mListView;private ContactPhotoManager mPhotoManager;  // 头像管理protected abstract View inflateView(LayoutInflater inflater, ViewGroup container);protected abstract T createListAdapter();        // 子类中实现具体adapter@Override   // 子类可重写获取数据的Loaderpublic Loader<Cursor> onCreateLoader(int id, Bundle args) { if (id == DIRECTORY_LOADER_ID) {DirectoryListLoader loader = new DirectoryListLoader(mContext);loader.setDirectorySearchMode(mAdapter.getDirectorySearchMode());loader.setLocalInvisibleDirectoryEnabled(ContactEntryListAdapter.LOCAL_INVISIBLE_DIRECTORY_ENABLED);return loader;} else {CursorLoader loader = createCursorLoader(mContext);long directoryId = args != null && args.containsKey(DIRECTORY_ID_ARG_KEY)? args.getLong(DIRECTORY_ID_ARG_KEY): Directory.DEFAULT;mAdapter.configureLoader(loader, directoryId);return loader;}}}

ContactEntryListFragment内部封装了很多操作,绑定了ContactEntryListAdapter,具体细节就不在这里详述了。
最后附上Dialer主要类图:

Android 拨号盘应用源码分析相关推荐

  1. CTS(11)---android自动化测试CTS源码分析之一

    android自动化测试CTS源码分析之一 1, 概述 CTS(Compatibility Test Suite)全名兼容性测试,主要目的就是让Android设备开发商能够开发出兼容性更好的andro ...

  2. android agps,Android应用开发Android GPS ——AGPS源码分析及配置

    本文将带你了解Android应用开发Android GPS --AGPS源码分析及配置,希望本文对大家学Android有所帮助. " Android Framework GPS --AGPS ...

  3. android gps源码分析,Android编程之Android GPS ——AGPS源码分析及配置

    本文主要介绍了Android编程的Android GPS --AGPS源码分析及配置,通过具体的分析以及源码,向大家展示了这些,希望对大家学习Android编程有所帮助. 1:冷启动指令: locat ...

  4. android(cm11)状态栏源码分析(一)

    版权声明:您好,转载请留下本人博客的地址,谢谢 https://blog.csdn.net/hongbochen1223/article/details/50216563 (一):写在前面 最近由于工 ...

  5. Android 7.0 源码分析项目一期竣工啦

    从 Android 入行开始,因为工作需求和解决疑难bug的原因陆陆续续的看过一些源码,但都不成系统,从2016年年底开始,在Github上建了一个Android Open Source Projec ...

  6. Android之HandlerThread源码分析和简单使用(主线程和子线程通信、子线程和子线程通信)

    1.先熟悉handler方式实现主线程和子线程互相通信方式,子线程和子线程的通信方式 如果不熟悉或者忘记了,请参考我的这篇博客     Android之用Handler实现主线程和子线程互相通信以及子 ...

  7. Android之AsyncTask源码分析(第五篇:execute方法只能执行一次的原因)

    (注意:本文基于API 28的源码分析,API 29上或其他平台的源码略有不同) 前言 当你调用AsyncTask对象的execute()方法时,突然发生崩溃--内心充满不解:java.lang.Il ...

  8. Android——RIL 机制源码分析

    Android 电话系统框架介绍 在android系统中rild运行在AP上,AP上的应用通过rild发送AT指令给BP,BP接收到信息后又通过rild传送给AP.AP与BP之间有两种通信方式: 1. ...

  9. Android屏幕刷新——源码分析

    Android屏幕刷新原理--源码分析 文章目录 Android屏幕刷新原理--源码分析 概述 VSync信号 三级缓冲 源码分析 消息队列的同步屏障 参考资料 概述 Android系统每16ms(一 ...

  10. Android -- 消息处理机制源码分析(Looper,Handler,Message)

    android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因 ...

最新文章

  1. linux安装成桌面
  2. Spring 的优秀工具类盘点
  3. 【C++深度剖析教程9】初探C++标准库
  4. Java基础知识(一)
  5. 运维在浏览器搜索linux日志,Graylog日志管理系统---搜索查询方法使用简介
  6. 移动磁盘故障,如何在Mac修复?
  7. nyoj 114某种排序(水 大数+优化大数)
  8. android 调出键盘表情_keyboard dialog 仿微博表情键盘输入框,keyboarddialog
  9. 大麦网抢票程序(一)之大麦网网站分析
  10. Window10 系统休眠或睡眠状态恢复-占用磁盘空间 100% 问题以及解决方法
  11. Scrapy中的item和pipline
  12. CSS3 制作旋转的大风车
  13. Linux-uboot-学习笔记(10):移植三星官方uboot
  14. C# 连接数据库,查询数据
  15. 三星Galaxy S20:打开手势并更改导航栏按钮顺序
  16. HTML5+CSS3小实例:手风琴式加载动画
  17. 在Linux(unix)中,以波浪线“~”开始的文件名
  18. 每日安全简讯20160722
  19. 0-9数字CNN识别项目
  20. 51单片机静态数码管原理及代码

热门文章

  1. 这交互炸了(三):闪屏页是像云一样消失的
  2. pythonturtle魔法阵_python turtle 库绘制简单魔法阵
  3. 教育专家妙论生命法则
  4. 〖Python WEB 自动化测试实战篇⑤〗- selenium 元素定位详解 - (八大元素定位方式)
  5. HTL6033是一款专用于3串锂电池或聚合物电池的保护芯片
  6. 中移物联网模块M8321P调试记录
  7. 使用python操作新浪微博和一些想法
  8. CentOS 7拨号上网(ADSL PPPoE)
  9. 【win 10】win 10:远程连接 Windows 服务器工具下载、安装和使用:PowerShell server Putty —— win 10 之间通过 ssh 登录
  10. Win11使用PCVR时性能问题已修复 可手动安装解决