把之前做的通话记录部分做一个总结,通话记录在整个联系人应用中占一个fragment,主要是显示了近段时间的所有通话和未接来电,这部分不是我写的不做重点介绍,主要说一下点击进入已保存联系人和未保存联系人的详细通话界面,还有未接来电的个数图标显示。
通话记录显示界面是CalllogGroupFragment,它继承了ListFragment更好的显示,实现了2个接口:implements CallLogQueryHandler.Listener, 进入这个接口看到:
/** Listener to completion of various queries. */
public interface Listener {
/**
* Called when {@link CallLogQueryHandler#fetchCalls(int)}complete.
*/
void onCallsFetched(Cursor combinedCursor);
}
字面义很明显的是说,查询完成就 调用这个方法,具体做什么我们自己在fragment实现:
@Override
public void onCallsFetched(Cursor cursor) {
if (getActivity() == null || getActivity().isFinishing()) {
return;
}
mAdapter.setLoading(false);
/*****************/
mAdapter.changeCursor(cursor);
// This will update the state of the "Clear call log" menu item.
getActivity().invalidateOptionsMenu();
if (mScrollToTop) {
final ListView listView = getListView();
// The smooth-scroll animation happens over a fixed time per
iod.
// As a result, if it scrolls through a large portion of
the list,
// each frame will jump so far from the previous one that
the user
// will not experience the illusion of downward motion.
Instead,
// if we're not already near the top of the list, we
instantly jump
// near the top, and animate from there.
if (listView.getFirstVisiblePosition() > 5) {
listView.setSelection(5);
}
// Workaround for framework issue: the smooth-scroll doesn't
// occur if setSelection() is called immediately before.
mHandler.post(new Runnable() {
@Override
public void run() {
if (getActivity() == null || getActivity()
.isFinishing()) {
return;
}
listView.smoothScrollToPosition(0);
}
});
mScrollToTop = false;
}
mCallLogFetched = true;
destroyEmptyLoaderIfAl
}
就是更新listview显示数据 ,这个加载过程用的是AsyncQueryHandler,感觉这个代码有点复杂,而且android3.0之后有Loader可以更好的异步加载数据库,所以这里不对加载过程详细研究。loader写出来的更简洁推荐loader!然后是listview的点击事件其实封装到CallLogGroupAdapter这个adapter里面,而这个通话记录又是分为保存的联系人,为保存的联系人,单个或者几条在一起的群组,提取他的号码还是相当麻烦。包括通话记录的时间排序同一号码合并都放在CallLogGroupAdapter里面实现。
接下来说一下未接来电个数的图标显示,类似于最新版QQ消息个数图标。我用的github上面的一个自定义控件叫BadgeView用法很简单:
mBadge=new BadgeView(this, mTabhost.getTabWidget(),1);
if(mBadge!=null){
updateBadge();
}
private void updateBadge(){
int count = getMissCallCount();
mBadge.setText(count+"");
if(count>0 ){
mBadge.show();
}else{
mBadge.hide();
}
}
是哪个面 两段代码就是new 出控件,第二个参数就是 背景view的实例 第三个参数就是在第几个tab上显示,此处是第2个名称“最近通话”,然后最开始的时候的就用这样的代码写最近通话的tab标签:
mTabhost.addTab(createTab(mTabhost,TAB_TAG_CALLLOG,R.string.lb_calllog,R.drawable.
tabwidget_icon_calllog), CalllogGroupFragment.class, null);
看上去没什么问题,最后运行报错 大致是说对象转型出错,然后看了BadgeView的源码: 我估计是必须要传一个ViewGroup进去,从另一个角度想, 怎么会让一个view覆盖在一个view上面呢???
private void applyTo(View target) {
LayoutParams lp = target.getLayoutParams();
ViewParent parent = target.getParent();
FrameLayout container = new FrameLayout(context);
if (target instanceof TabWidget) {
// set target to the relevant tab child container
target = ((TabWidget) target).getChildTabViewAt
(targetTabIndex);
this.target = target;
((ViewGroup) target).addView(container,
new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
this.setVisibility(View.GONE);
container.addView(this);
} else {
// TODO verify that parent is indeed a ViewGroup
ViewGroup group = (ViewGroup) parent;
int index = group.indexOfChild(target);
group.removeView(target);
group.addView(container, index, lp);
container.addView(target);
this.setVisibility(View.GONE);
container.addView(this);
group.invalidate();
}
}
后来老大帮我 改了一段 代码,把之前的添加标签页改成:
//====start=====
FrameLayout fly=new FrameLayout(this);
TextView indicator = new TextView(this);
indicator.setText(getResources().getString(R.string.lb_calllog));
indicator.setGravity(Gravity.CENTER);
indicator.setBackgroundResource(R.drawable.tabwidget_background_color);
indicator.setCompoundDrawablesWithIntrinsicBounds(0,
R.drawable.tabwidget_icon_calllog, 0, 0);
setPadding(indicator);
fly.addView(indicator);
TabSpec calllogTab = mTabhost.newTabSpec(TAB_TAG_CALLLOG).setIndicator(fly);
//====end=====
这个在外层加了一个 FrameLayout就OK了,这是因为createTab方法是这样的:可以看到就是一个TextView:
private TabSpec createTab(FragmentTabHost tabhost,String tag,int titleRes,
int iconRes){
TextView indicator = new TextView(this);
indicator.setText(titleRes);
indicator.setGravity(Gravity.CENTER);
indicator.setBackgroundResource(R.drawable.tabwidget_background_color);
indicator.setCompoundDrawablesWithIntrinsicBounds(0, iconRes, 0, 0);
setPadding(indicator);
return tabhost.newTabSpec(tag).setIndicator(indicator);
}
那么这个问题就解决了,后来这个未接来电 有些bug:比如在另外的界面不能及时更新未接来电个数,那么这个就应该把更新个数分别写在主activity和当前fragment。 还有在当前界面有未接来电不能及时更新,在onResume()更新一下就好了。
最后部分就是联系人详情了,它分为已保存的联系人和陌生号码的详情,核心类就是一个AsyncTask读取数据库的通话记录,复杂的地方其实是对数据的封装 对记录的排序 时间格式的定制比如“今天” “昨天” “星期几”等等。
总之从这个项目,看到了google工程师代码的严谨与优化,包括异步操作数据库,每一条数据的封装,adapter里面点击条目号码的获取等等,自己在方面还有所欠缺。