Android7.0 拨号盘应用源码分析(一) 界面浅析
发布日期:2021-06-30 21:23:28 浏览次数:2 分类:技术文章

本文共 14776 字,大约阅读时间需要 49 分钟。

前言

android拨号盘的源码目录在package/app/Dialer

自7.0以后Incallui的源码直接放到了Dialer目录下,虽然在7.0以前incallui有自己独立的目录,但实际编译过程中只是作为链接库最后还是被编译到Dialer的apk里

博主这里只取Dialer相关的源码并导入AS中,并稍作调整兼容至L

源码目录结构如下:

先理一理各个工程的依赖关系

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

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

com.android.contacts.common又依赖于

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

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

github下载链接:

1.1拨号盘概览

先来看看几张原图

1.2 DialtactsActivity

主activity为DialtactsActivity

 

com.android.dialer.DialtactsActivitypublic class DialtactsActivity extends TransactionSafeActivity 。。。{    // Fragment containing the dialpad that slides into view    protected 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里的主要实现(部分内容省略)

 

@Overrideprotected 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添加管理器ActionBarController    mActionBarController = 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);   // 添加ListsFragment    getFragmentManager().beginTransaction()        .add(R.id.dialtacts_frame, new ListsFragment(), TAG_FAVORITES_FRAGMENT)        .commit();  // 初始化单例对象DialerDatabaseHelper   mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this);  SmartDialPrefix.initializeNanpSettings(this);}

 

1.3 ListsFragment

ListsFragment是主fragment,结构如下

 

public class ListsFragment extends Fragment{     private ViewPager mViewPager;    private ViewPagerTabs mViewPagerTabs;      // 自定义TAB标签,继承自HorizontalScrollView    private ViewPagerAdapter mViewPagerAdapter;    // 拖拽常用联系人时悬浮视图    private RemoveView mRemoveView;    private View mRemoveViewContent;    // 常用联系人fragment    private SpeedDialFragment mSpeedDialFragment;    // 最近通话记录fragment    private CallLogFragment mHistoryFragment;    // 联系人列表fragment    private AllContactsFragment mAllContactsFragment;    // Voicemail列表fragment    private CallLogFragment mVoicemailFragment;   @Override    public 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页,国内运营商暂不支持

SpeedDialFragment显示常用联系人列表

 

public class SpeedDialFragment extends Fragment ...{    // 显示数据的GridView列表    private PhoneFavoriteListView mListView;       // 源数据BaseAdapter    private PhoneFavoritesTileAdapter mContactTileAdapter;  // 查询源数据的LoaderCallbacks  private class ContactTileLoaderListener implements LoaderManager.LoaderCallbacks
{ @Override public CursorLoader onCreateLoader(int id, Bundle args) { if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onCreateLoader."); return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity()); } @Override public void onLoadFinished(Loader
loader, Cursor data) { if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoadFinished"); mContactTileAdapter.setContactCursor(data); setEmptyViewVisibility(mContactTileAdapter.getCount() == 0); } @Override public void onLoaderReset(Loader
loader) { if (DEBUG) Log.d(TAG, "ContactTileLoaderListener#onLoaderReset. "); } }}

 

使用LoadManager方式获取cursor数据,查询ContactsProvider数据库的data表

 

com.android.contacts.common.ContactTileLoaderFactorypublic static CursorLoader createStrequentPhoneOnlyLoader(Context context) {     Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon()          .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();     return new CursorLoader(context, uri, COLUMNS_PHONE_ONLY, null, null, null);}

 

数据来源包括收藏的联系人以及有通话记录的联系人

1.4 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  @Override    public 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 keypad        View 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();        }    }}

 

当处于通话状态时显示如下

1.5SmartDialSearchFragment 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{    @Override    protected 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;    }    @Override    public Loader
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{  @Override    protected 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
extends Fragment{ private T mAdapter; // 模板adapter private View mView; private ListView mListView; private ContactPhotoManager mPhotoManager; // 头像管理 protected abstract View inflateView(LayoutInflater inflater, ViewGroup container); protected abstract T createListAdapter(); // 子类中实现具体adapter @Override // 子类可重写获取数据的Loader public Loader
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,具体细节就不在这里详述了

1.6小结

最后附上Dialer里主要类图

转载地址:https://liwangjiang.blog.csdn.net/article/details/89633410 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android7.0 Phone应用源码分析(二) phone来电流程分析
下一篇:Android电话系统之概述篇

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年04月29日 01时16分58秒