转自http://blog.sina.com.cn/s/blog_9f233c070101b2u1.html
上文已经说过拨号界面的分页是怎么实现的,下面我们分三次来分享拨号页面内的每一个页面的具体实现,我们先从拨号盘开始说起。
上文通过在ViewPager的适配器对象中,发现过一下三行代码
private DialpadFragment mDialpadFragment;
private CallLogFragment mCallLogFragment;
private PhoneFavoriteFragment mPhoneFavoriteFragment;
第一行的DialpadFragment就是拨号盘界面
第二行的CallLogFragment就是通话记录页面
第三行的PhoneFavoriteFragment就是收藏界面
本文主要分享“拨号盘界面”,即DialpadFragment.java
1
首先我们先来看看拨号盘的显示样式,4.0中拨号盘的默认显示效果如下图
给我的感觉是整个盘面分成四个部分,
第一部分: title即tab相应的图标
第二部分: editText输入部分
第三部分:拨号盘主界面
第四部分:拨号盘底部控制栏
2. 每一部分的具体实现
2.2 editText输入部分
此时我们应该注意一下,DialpadFragment类的到底是什么东西?
class DialpadFragment extends Fragment
原来是Fragment,下面我转载了一个经典的Fragment生命周期图片,下图2
来了,原来是dialpad_fragment.xml文件
<LinearLayout
我将上面的文件,表示的意义用图的形式画出
图中的青色区域代表的就是digits_container,黄色区域代表的是digits,即输入框,红色区域代表的是overflow_menu按钮。
我想您看过上面的布局文件,就会问输入框应该是EditText,而上文的为什么是DigitsEditText?
很明显它们之间是继承关系。下面我们来看啊可能这个DigitsEditText有什么特点?、
代码中对其进行了虚拟键盘的设置,焦点的变换,触摸等几个操作。
对此需要说明的一点是,布局文件中明明有overflow_menu,但是为什么没有显示出来呢?
原来是在onCreateView中对其可见性进行了设置,设置代码如下:
final View overflowMenuButton = fragmentView.findViewById(R.id.overflow_menu);
在这句话上有这样一句注释
Soft menu button should appear only when there's no hardware menu button.
说出了该overflow按钮的显示时间,再次不多废话。
2.3 拨号键盘
拨号键盘对应于dialpad.xml的布局文件
该布局采用了典型的table布局,有兴趣的朋友可以看一下
下面紧取一个按键,进行一下细节的描述,就拿1这个按键为例
<ImageButton android:id="@+id/one" style="@style/DialtactsDialpadButtonSt
首先为该ImageButton设置了一个id ,然后为其设置了style
我们先进入style看看都为ImageButton做了些什么,
<style name="DialtactsDialpadButtonSt
</style>
最引人注意的是android:background 和android:soundEffectsEnabled
android:background代表着该ImageButton的背景色
android:soundEffectsEnabled设置点击或触摸时是否有声音效果
回过来,我们再来看看ImageButton的前景
android:src="@drawable/dial_num_1"
很显然,这个一个图片嘛,有啥好看的?您不仅会这样想。
实际上这个是一个图片吗?不是,也是,更确切的说是一个图片组,下面我们来看看这个图片组到底有什么神秘的
<selector xmlns:android="http://schemas.android.com/apk/res/android">
</selector>
哈哈,原来是个selector,通过其中的条件可以添加点击,触摸等等效果,这个在我们平时开发的过程中还是比较常用的,朋友们一定要记住哦!
2.4拨号盘底部控制栏
通过dialpad_additional_buttons.xml文件查看到,
三个按钮2个分割线的横向布局排列,这个页面没有什么神秘的地方,就不在浪费笔墨了
有兴趣的朋友一看就明白。
最后希望朋友们按照我的思路自己跟一遍代码,这样您将会得到更大的收获,小弟才疏学浅,有肯定地方不到位,希望大家指正!
如果我的一点点总结,对您的有一丁点帮助,那么我将会感到莫大的欣慰!
在Android4.0中,Contacts 拨号界面的分页方式是怎么样的呢?是通过传统的TabHost+tab的方式吗?
不是的,而是通过ActionBar + tab + view Pager的方式实现的。
具体的实现详情,请见下面的详细剖析。
在DialtactsActivity类中,通过向ActionBar中添加Tab的方式实现分页,每页显示的内容,则通过ViewPager对象设置的Adapter对象来设置。
在onCreate()方法中
顺序调用
setupDialer();
setupCallLog();
setupFavorites();
方法加载将三哥tab加载到ActionBar中
下面以setupDialer 为例,逐行描述一下其加载过程
private void setupDialer() {
final Tab tab = getActionBar().newTab();
新建一个Tab页
tab.setContentDescription(R.string.dialerIconLabel);
为该tab也设置描述,基本上没意义,也不显示,只是存储一些临时的数据
tab.setTabListener(mTabListener);
设置监听器,当点击该tab页时触发该事件
tab.setIcon(R.drawable.ic_tab_dialer);
给该tab也添加显示的图标
getActionBar().addTab(tab);
将该tab页添加到ActionBar中
其它几个方法的具体实现也类似。
那么,您想想, Activity 多次调用getActionBar().addTab(tab) 添加tab都是添加到一个ActionBar中了吗?有此疑问的朋友请点击
那么创建三个tab就可以了吗?
不是的, DialtactsActivity中又对ActionBar进行了下面的处理
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
getActionBar().setDisplayShowTitleEnabl
getActionBar().setDisplayShowHomeEnable
第一句将ActionBar设置为“Tab导航”模式
第二句将ActionBar设置为标题不可见
第三句将ActionBar设置为显示主界面按钮不可用
是不是感觉缺了点什么?tab 页内显示的内容呢?
先别急,我们继续往下走
上文提到tab.setTabListener(mTabListener),现在我们来看看这个监听器到底做了什么
private final TabListener mTabListener = new TabListener() {
….
看到了吗?
将tab和ViewPager联系到一起了。
tab.getPosition()获得当前tab的位置返回int值
mViewPager.getCurrentItem()返回滑动页当前的item编号,返回int值
mViewPager.setCurrentItem(tab.getPosition(), true); 设置viewPager的平滑滚动到tab设置的位置
当tab的位置和ViewPager的当前页不等时,重新设置viewpager的当前页
看样子就是这了,tab页的内容肯定就是通过mViewPager来设置的。
下面我们继续跟进mViewPager
private ViewPager mViewPager;
首先声明mViewPager 为类的私有成员
在onCreate()方法中被初始化
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
mViewPager.setOnPageChangeListener(mPageChangeListener);
第一句
mViewPager = (ViewPager) findViewById(R.id.pager);
我们来看类的布局文件dialtacts_activity.xml文件
在该文件中有一处
<com.android.contacts.activities.DialtactsViewPager
原来mViewPager 就是布局中的pager啊
com.android.contacts.activities.DialtactsViewPager
好像不是传统的ViewPager,难道DialtactsViewPager继承了DialtactsViewPager?
答案是正确的
public class DialtactsViewPager extends ViewPager
好,我们接下来继续看看viewPager对象往下都做了些什么
第二句mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
为ViewPager对象设置了一个Adapter
我们来看看Adapter的具体内容
public class ViewPagerAdapter extends FragmentPagerAdapter {
哦,我明白了,原来在Adapter里面建立了三种Fragment对象
然后通过当前的选中位置来返回当前页是哪个fragment对象,在fragment对象里面设置页面的具体内容。
既然Adapter设置好了,已经可以滑动了,也就是已经可以实现切换效果了,google接下来还要干什么了呢?
mViewPager.setOnPageChangeListener(mPageChangeListener);
private Fragment getFragmentAt(int position) {
}
通过上面的操作,了解到PageChangeListener 的主要作用是在页面滑动过程中,menu菜单键有可见到不可见的。
最后希望朋友们按照我的思路自己跟一遍代码,这样您将会得到更大的收获,小弟才疏学浅,有肯定地方不到位,希望大家指正!
如果我的一点点总结,对您的有一丁点帮助,那么我将会感到莫大的欣慰!
看完了拨号盘界面的实现后,大家可能会感觉到,挺简单的,没什么复杂的,只是在onCreateView方法中加载了一个layout,然后就全都看到了,so easy!
那么,我们接下来就分享一下稍微复杂一点的CallLog界面,希望大家看完后,依然会如此说!
下面我们先来看看通话记录的默认显示,下图1
通话记录页面的实现
现在我们直接来看onCreateView方法
在该方法第一句
跟进call_log_fragment.xml
发现
<ListView android:id="@android:id/list"
估计该listView就是要显示的列表
跟进代码…
奇怪了,CallLogFragment代码中怎么就没有调用list的呢?
如果这条线断了,就不知道往下该咋弄了啊! 急~
原来是这样子的,CallLogFragment继承自ListFragment而在ListFragment中的ensureList方法中进行了初始化,具体的语句如下:
View rawListView = root.findViewById(android.R.id.list);
mList = (ListView)rawListView;
rawListView获得的list的名字是android.R.id.list,而我们的布局文件中list的名字是@android:id/list,没错了吧。
而在我们的CallLogFragment中只要调用getListView即可获得listView了
为了避免您的怀疑,我们在看一下ListFragment的getListView方法:
public ListView getListView() {
既然找到了list,接下来我们要看看数据的绑定了,
一提到数据的绑定,立即就会想到Adapter,下面我们看看Android源码是怎么写的
在onViewCreated方法中,我发现了如下的几句话
String currentCountryIso = ContactsUtils.getCurrentCountryIso(getActivity());
mAdapter = new CallLogAdapter(getActivity(), this, new ContactInfoHelper(getActivity()
, currentCountryIso), mVoiceMailNumber);
setListAdapter(mAdapter);
第二句创建了一个CallLogAdapter对象,然后调用setListAdapter方法,将CallLogAdapter对象设置过去,
不用说setListAdapter方法肯定是ListFragment的方法,看代码
public void setListAdapter(ListAdapter adapter) {
}
代码中对我们来说最重要的一句
mList对应于我们布局文件中的list,没错了吧,看样一切的秘密都在这个adapter中了,继续跟进adapter
该Adapter怎么会没有getView呢?
我们看CallLogAdapter的父类,GroupingListAdapter,在该方法中有getView方法,
public View getView(int position, View convertView, ViewGroup parent) {
…
原来是调用了newStandAloneView,newGroupView,newChildView方法,来创建view的
那么我们接下来继续回到CallLogAdapter中查看上述三个方法所加载的布局文件
call_log_list_item.xml
呵呵,一看该布局,不会有错了,就是该list的分项
Ok,简单吧