1.1 原生界面
Phone应用的UI设计直接关系到后面的去来电流程,因此有必要对Phone的UI做一定的介绍。下面是Android6.0的原生拨号界面:
1.2 应用界面
Phone的UI入口可以认为是DialtactsActivity.java的onCreate()。
@Override protected void onCreate(Bundle savedInstanceState) { Trace.beginSection(TAG + " onCreate"); super.onCreate(savedInstanceState);
mFirstLaunch = true;//首次launch标志
final Resources resources = getResources(); mActionBarHeight = resources.getDimensionPixelSize(R.dimen.action_bar_height_large);//获取高度
Trace.beginSection(TAG + " setContentView"); setContentView(R.layout.dialtacts_activity);//获得布局文件 Trace.endSection(); getWindow().setBackgroundDrawable(null); //设置视图,actionBar即手机顶部的常驻空间,供页面的切换或标识 Trace.beginSection(TAG + " setup Views"); final ActionBar actionBar = getActionBar(); actionBar.setCustomView(R.layout.search_edittext);//放入“搜索编辑框” actionBar.setDisplayShowCustomEnabled(true); actionBar.setBackgroundDrawable(null); //搜索编辑框设计,首先同构ViewID找到控件 SearchEditTextLayout searchEditTextLayout = (SearchEditTextLayout) actionBar.getCustomView().findViewById(R.id.search_view_container); //猜测为输入时的智能提示 searchEditTextLayout.setPreImeKeyListener(mSearchEditTextLayoutListener); //为这个搜索编辑框绑定控制器 mActionBarController = new ActionBarController(this, searchEditTextLayout); //找到编辑控件 mSearchView = (EditText) searchEditTextLayout.findViewById(R.id.search_view); //监听编辑框文字输入 mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener); //声控输入按钮 mVoiceSearchButton = searchEditTextLayout.findViewById(R.id.voice_search_button); //放大镜,位于文本编辑控件的左侧,并绑定Listener searchEditTextLayout.findViewById(R.id.search_magnifying_glass) .setOnClickListener(mSearchViewOnClickListener); //绑定开始查找按钮 searchEditTextLayout.findViewById(R.id.search_box_start_search) .setOnClickListener(mSearchViewOnClickListener); searchEditTextLayout.setOnClickListener(mSearchViewOnClickListener); //为searchEditTextLayout绑定callBack searchEditTextLayout.setCallback(new SearchEditTextLayout.Callback() { //返回上一次搜索 @Override public void onBackButtonClicked() { onBackPressed(); }
@Override public void onSearchViewClicked() { //Floating action button (FAB) 是一个带有环状阴影的圆形按键,位于 UI 之上,用//于显示常用的操作,比如添加新条目、编写邮件等。 // 当键盘显示时隐藏FAB mFloatingActionButtonController.scaleOut(); } });
mIsLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; //获取floatingActionButton的容器 final View floatingActionButtonContainer = findViewById( R.id.floating_action_button_container); //获取floatingActionButton ImageButton floatingActionButton = (ImageButton) findViewById(R.id.floating_action_button); //监听floatingActionButton floatingActionButton.setOnClickListener(this); //floatingActionButton控制,由于FAB是浮动的因此需要限定范围,并加以控制 mFloatingActionButtonController = new FloatingActionButtonController(this, floatingActionButtonContainer, floatingActionButton); //通过监听者都是this,猜测应该就是floatingActionButton ImageButton optionsMenuButton = (ImageButton) searchEditTextLayout.findViewById(R.id.dialtacts_options_menu_button); optionsMenuButton.setOnClickListener(this); //为搜索添加设置菜单 mOverflowMenu = buildOptionsMenu(searchEditTextLayout); //绑定监听者 optionsMenuButton.setOnTouchListener(mOverflowMenu.getDragToOpenListener());
//此处猜测为常用联系人 // Add the favorites fragment but only if savedInstanceState is null. Otherwise the // fragment manager is responsible for recreating it. if (savedInstanceState == null) { getFragmentManager().beginTransaction() .add(R.id.dialtacts_frame, new ListsFragment(), TAG_FAVORITES_FRAGMENT) .commit(); } else { mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY); mInRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI); mInDialpadSearch = savedInstanceState.getBoolean(KEY_IN_DIALPAD_SEARCH_UI); mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH); mShowDialpadOnResume = savedInstanceState.getBoolean(KEY_IS_DIALPAD_SHOWN); mActionBarController.restoreInstanceState(savedInstanceState); }
final boolean isLayoutRtl = DialerUtils.isRtl(); //处理划屏动画加载,界面切换 if (mIsLandscape) {//左右滑屏为切换 mSlideIn = AnimationUtils.loadAnimation(this, isLayoutRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right); mSlideOut = AnimationUtils.loadAnimation(this, isLayoutRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right); } else {//上下滑屏为常驻返回等菜单显示与隐藏 mSlideIn = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom); mSlideOut = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom); }
mSlideIn.setInterpolator(AnimUtils.EASE_IN); mSlideOut.setInterpolator(AnimUtils.EASE_OUT); //绑定滑屏监听者,这样才能实现功能 mSlideIn.setAnimationListener(mSlideInListener); mSlideOut.setAnimationListener(mSlideOutListener); //拖拽处理 mParentLayout = (FrameLayout) findViewById(R.id.dialtacts_mainlayout); mParentLayout.setOnDragListener(new LayoutOnDragListener()); floatingActionButtonContainer.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { final ViewTreeObserver observer = floatingActionButtonContainer.getViewTreeObserver(); if (!observer.isAlive()) { return; } observer.removeOnGlobalLayoutListener(this); int screenWidth = mParentLayout.getWidth(); mFloatingActionButtonController.setScreenWidth(screenWidth); mFloatingActionButtonController.align( getFabAlignment(), false /* animate */); } });
Trace.endSection();
Trace.beginSection(TAG + " initialize smart dialing"); mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this); SmartDialPrefix.initializeNanpSettings(this); Trace.endSection(); Trace.endSection(); } |
以下为其布局XML
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/dialtacts_mainlayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:focusable="true" android:focusableInTouchMode="true" android:clipChildren="false" android:background="@color/background_dialer_light">
<FrameLayout android:id="@+id/dialtacts_container" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false"> <!-- The main contacts grid --> <FrameLayout android:layout_height="match_parent" android:layout_width="match_parent" android:id="@+id/dialtacts_frame" android:clipChildren="false" /> </FrameLayout>
<FrameLayout android:id="@+id/floating_action_button_container" android:background="@drawable/fab_blue" android:layout_width="@dimen/floating_action_button_width" android:layout_height="@dimen/floating_action_button_height" android:layout_marginBottom="@dimen/floating_action_button_margin_bottom" android:layout_gravity="center_horizontal|bottom">
<ImageButton android:id="@+id/floating_action_button" android:background="@drawable/floating_action_button" android:layout_width="match_parent" android:layout_height="match_parent" android:contentDescription="@string/action_menu_dialpad_button" android:src="@drawable/fab_ic_dial"/>
</FrameLayout>
<!-- Host container for the contact tile drag shadow --> <FrameLayout android:id="@+id/activity_overlay" android:layout_height="match_parent" android:layout_width="match_parent"> <ImageView android:id="@+id/contact_tile_drag_shadow_overlay" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" android:importantForAccessibility="no" /> </FrameLayout> </FrameLayout> |
1.3 拨号视图
拨号视图加载DialpadFragment .onCreateView():
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { Trace.beginSection(TAG + " onCreateView"); Trace.beginSection(TAG + " inflate view"); //来自百度:在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于//findViewById()。不同点是LayoutInflater是用来找res/layout/下的xml布局文件,//并且实例化;而findViewById()是找xml布局文件下的具体widget控件(如Button、//TextView等)。具体作用: 1、对于一个没有被载入或者想要动态载入的界面,都需要使用//LayoutInflater.inflate()来载入; final View fragmentView = inflater.inflate(R.layout.dialpad_fragment, container, false); Trace.endSection(); Trace.beginSection(TAG + " buildLayer"); fragmentView.buildLayer(); Trace.endSection();
Trace.beginSection(TAG + " setup views");
mDialpadView = (DialpadView) fragmentView.findViewById(R.id.dialpad_view); mDialpadView.setCanDigitsBeEdited(true); mDigits = mDialpadView.getDigits(); mDigits.setKeyListener(UnicodeDialerKeyListener.INSTANCE); mDigits.setOnClickListener(this);//将自己设置为拨号按钮的监听者 mDigits.setOnKeyListener(this); //设置按键事件监听者 mDigits.setOnLongClickListener(this); mDigits.addTextChangedListener(this); mDigits.setElegantTextHeight(false); PhoneNumberFormatter.setPhoneNumberFormattingTextWatcher(getActivity(), mDigits); // 检查键盘的存在 View oneButton = fragmentView.findViewById(R.id.one); if (oneButton != null) {//如果存在设定监听者 configureKeypadListeners(fragmentView); } //del键设置 mDelete = mDialpadView.getDeleteButton();
if (mDelete != null) { mDelete.setOnClickListener(this); mDelete.setOnLongClickListener(this); } //空格键设置 mSpacer = fragmentView.findViewById(R.id.spacer); mSpacer.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (isDigitsEmpty()) { if (getActivity() != null) { return ((HostInterface) getActivity()).onDialpadSpacerTouchWithEmptyQuery(); } return true; } return false; } });
mDigits.setCursorVisible(false);
// Set up the "dialpad chooser" UI; see showDialpadChooser(). mDialpadChooser = (ListView) fragmentView.findViewById(R.id.dialpadChooser); mDialpadChooser.setOnItemClickListener(this); //FAB设计 final View floatingActionButtonContainer = fragmentView.findViewById(R.id.dialpad_floating_action_button_container); final ImageButton floatingActionButton = (ImageButton) fragmentView.findViewById(R.id.dialpad_floating_action_button); floatingActionButton.setOnClickListener(this); mFloatingActionButtonController = new FloatingActionButtonController(getActivity(), floatingActionButtonContainer, floatingActionButton); Trace.endSection(); Trace.endSection(); return fragmentView; } |