一.概述
二.源碼分析
以前裝應用的時候有些應用會在桌面上生成兩個圖標,這兩個圖標有些是同一個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