一.概述
二.源码分析
以前装应用的时候有些应用会在桌面上生成两个图标,这两个图标有些是同一个Activity的入口,有些是另外一个Activity的入口,这样的效果是怎么实现的呢?在看Android原生DeskClock程序的时候看到了这个功能的实现.使用的是activity-alias:
1).语法格式
<activity-alias android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permission="string"
android:targetActivity="string" >
. . .
</activity-alias>
2).DeskClock中应用
从下面的配置可以看出这是同一个activity(DeskClock)的两个入口,并且这两个入口的名字图标都一样,这样做有什么意义呢?可以看到activity-alias中标记了一个名为android.intent.category.DESK_DOCK的category,这个是在android设备插上桌面Dock底 座的时候才会触发alias入口.
<activity android:name="DeskClock"
android:label="@string/app_label"
android:theme="@style/DeskClock"
android:icon="@mipmap/ic_launcher_alarmclock"
android:launchMode="singleTask"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity-alias android:name="DockClock"
android:targetActivity="DeskClock"
android:label="@string/app_label"
android:theme="@style/DeskClock"
android:icon="@mipmap/ic_launcher_alarmclock"
android:launchMode="singleTask"
android:enabled="@bool/config_dockAppEnabled"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.DESK_DOCK" />
</intent-filter>
</activity-alias>
activity-alias通过指定targetActivity来决定入口相连接的activity,给该程序更改一个不同的label(ClockAlias)和icon(菊花)并且替换掉Dock底座的category,如下部代码配置所示.
<activity-alias android:name="DockClock"
android:targetActivity="DeskClock"
android:label="@string/app_second_label"
android:theme="@style/DeskClock"
android:icon="@mipmap/entrance"
android:launchMode="singleTask"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
这样修改完成配置之后就可以实现在android设备上双入口图标了,点击两个图标都可以进入到DeskClock的程序里面,具体 效果如下图所示
if (mTabsAdapter == null) {
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.desk_clock_pager);
// Keep all four tabs to minimize jank.
mViewPager.setOffscreenPageLimit(3);
mTabsAdapter = new TabsAdapter(this, mViewPager);
createTabs(mSelectedTab);
}
setContentView(mViewPager);
mActionBar.setSelectedNavigationItem(mSelectedTab);
主页中主要的页面切换等大部分逻辑都在自定义的FragmentPagerAdapter中,这里主要分析下适配器。TabsAdapter在构造的时候可以获取到DeskClock的context,actionbar,viewpager并绑定该适配器,绑定页面变化的监听.
public TabsAdapter(Activity activity, ViewPager pager) {
super(activity.getFragmentManager());
mContext = activity;
mMainActionBar = activity.getActionBar();
mPager = pager;
mPager.setAdapter(this);
mPager.setOnPageChangeListener(this);
}
final class TabInfo {
private final Class<?> clss;
private final Bundle args;
TabInfo(Class<?> _class, int position) {
clss = _class;
args = new Bundle();
args.putInt(KEY_TAB_POSITION, position);
}
public int getPosition() {
return args.getInt(KEY_TAB_POSITION, 0);
}
}
public void addTab(ActionBar.Tab tab, Class<?> clss, int position) {
TabInfo info = new TabInfo(clss, position);
tab.setTag(info);
tab.setTabListener(this);
mTabs.add(info);
mMainActionBar.addTab(tab);
notifyDataSetChanged();
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
TabInfo info = (TabInfo)tab.getTag();
int position = info.getPosition();
mPager.setCurrentItem(getRtlPosition(position));
}
public void onPageSelected(int position) {
// Set the page before doing the menu so that onCreateOptionsMenu knows what page it is.
mMainActionBar.setSelectedNavigationItem(getRtlPosition(position));
notifyPageChanged(position);
// Only show the overflow menu for alarm and world clock.
if (mMenu != null) {
// Make sure the menu's been initialized.
if (position == ALARM_TAB_INDEX || position == CLOCK_TAB_INDEX) {
mMenu.setGroupVisible(R.id.menu_items, true);
onCreateOptionsMenu(mMenu);
} else {
mMenu.setGroupVisible(R.id.menu_items, false);
}
}
}
private boolean isRtl() {
return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) ==
View.LAYOUT_DIRECTION_RTL;
}
private int getRtlPosition(int position) {
if (isRtl()) {
switch (position) {
case TIMER_TAB_INDEX:
return RTL_TIMER_TAB_INDEX;
case CLOCK_TAB_INDEX:
return RTL_CLOCK_TAB_INDEX;
case STOPWATCH_TAB_INDEX:
return RTL_STOPWATCH_TAB_INDEX;
case ALARM_TAB_INDEX:
return RTL_ALARM_TAB_INDEX;
default:
break;
}
}
return position;
}
TabsAdapter在加载不同的fragment的时候也是同理的,通过positon取得TagInfo的数据,再根据Fragment的instantiate方法以ClassLoader的方式实例跟positon相对应的Fragment.
public Fragment getItem(int position) {
TabInfo info = mTabs.get(getRtlPosition(position));
DeskClockFragment f = (DeskClockFragment) Fragment.instantiate(
mContext, info.clss.getName(), info.args);
return f;
}
public void registerPageChangedListener(DeskClockFragment frag) {
String tag = frag.getTag();
if (mFragmentTags.contains(tag)) {
Log.wtf(LOG_TAG, "Trying to add an existing fragment " + tag);
} else {
mFragmentTags.add(frag.getTag());
}
// Since registering a listener by the fragment is done sometimes after the page
// was already changed, make sure the fragment gets the current page
frag.onPageChanged(mMainActionBar.getSelectedNavigationIndex());
}
public void unregisterPageChangedListener(DeskClockFragment frag) {
mFragmentTags.remove(frag.getTag());
}
private void notifyPageChanged(int newPage) {
for (String tag : mFragmentTags) {
final FragmentManager fm = getFragmentManager();
DeskClockFragment f = (DeskClockFragment) fm.findFragmentByTag(tag);
if (f != null) {
f.onPageChanged(newPage);
}
}
}
三.总结
这一篇博客主要介绍了DeskClock程序中主UI部分的逻辑,主要分析了两点(多入口配置和ViewPager,ActionBar,适配器之间切换和数据绑定)现在四大功能部分还没有涉及,后续的系列就要开始介绍四大功能.
转载请注明出处:http://blog.csdn.net/l2show/article/details/46722999