先从Launcher.xml看view的结构:
res/launcher.xml下有
<!-- The Workspace cling must appear under the AppsCustomizePagedView below to ensure
that it is still visible during the transition to AllApps and doesn't overlay on
top of that view. -->
<com.ijinshan.browser.launcher3.ScrimView
android:id="@+id/cling_scrim"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<include layout="@layout/first_run_cling"
android:id="@+id/first_run_cling"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<include layout="@layout/workspace_cling"
android:id="@+id/workspace_cling"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<include layout="@layout/folder_cling"
android:id="@+id/folder_cling"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
ScrimView这个类是个FrameLayout的子类,它只实现了Insettable接口,并没有扩展任何元素,可以认为它就是个FrameLayout,暂时不明白它的用意
其它几个include其实都是com.ijinshan.browser.launcher3.Cling类,作者通过这一个类包不同的子view来实现同一种能力且显示效果不同的目的,值得学习.
这里有三个帮助的具体实现:
1.first_run_cling
2.workspace_cling
3.folder_cling
我们可以通过Cling.java这个类为线索去看系统是怎么实现帮助提示的:
1.首先是一些个dismissed:
static final String FIRST_RUN_CLING_DISMISSED_KEY = "cling_gel.first_run.dismissed";
static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
static final String FOLDER_CLING_DISMISSED_KEY = "cling_gel.folder.dismissed";
它的目的很明显,一旦被关掉的帮助以后不打开了,通过这些个key把它们存在SharedPreferences里头。
public void showFirstRunCling() {
if (isClingsEnabled() &&
!mSharedPrefs.getBoolean(Cling.FIRST_RUN_CLING_DISMISSED_KEY, false) &&
!skipCustomClingIfNoAccounts() ) {
// If we're not using the default workspace layout, replace workspace cling
// with a custom workspace cling (usually specified in an overlay)
// For now, only do this on tablets
if (!DISABLE_CUSTOM_CLINGS) {
if (mSharedPrefs.getInt(LauncherProvider.DEFAULT_WORKSPACE_RESOURCE_ID, 0) != 0 &&
getResources().getBoolean(R.bool.config_useCustomClings)) {
// Use a custom cling
View cling = findViewById(R.id.workspace_cling);
ViewGroup clingParent = (ViewGroup) cling.getParent();
int clingIndex = clingParent.indexOfChild(cling);
clingParent.removeViewAt(clingIndex);
View customCling = mInflater.inflate(R.layout.custom_workspace_cling, clingParent, false);
clingParent.addView(customCling, clingIndex);
customCling.setId(R.id.workspace_cling);
}
}
Cling cling = (Cling) findViewById(R.id.first_run_cling);
if (cling != null) {
// String sbHintStr = getFirstRunClingSearchBarHint();
String ccHintStr = getFirstRunCustomContentHint();
// if (!sbHintStr.isEmpty()) {
// TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
// sbHint.setText(sbHintStr);
// sbHint.setVisibility(View.VISIBLE);
// }
setCustomContentHintVisibility(cling, ccHintStr, true, false);
}
initCling(R.id.first_run_cling, 0, false, true);
} else {
removeCling(R.id.first_run_cling);
}
}
private void dismissCling(final Cling cling, final Runnable postAnimationCb,
final String flag, int duration, boolean restoreNavBarVisibilty) {
// To catch cases where siblings of top-level views are made invisible, just check whether
// the cling is directly set to GONE before dismissing it.
if (cling != null && cling.getVisibility() != View.GONE) {
final Runnable cleanUpClingCb = new Runnable() {
public void run() {
cling.cleanup();
// We should update the shared preferences on a background thread
new Thread("dismissClingThread") {
public void run() {
SharedPreferences.Editor editor = mSharedPrefs.edit();
editor.putBoolean(flag, true);
editor.commit();
}
}.start();
if (postAnimationCb != null) {
postAnimationCb.run();
}
}
};
if (duration <= 0) {
cleanUpClingCb.run();
} else {
cling.hide(duration, cleanUpClingCb);
}
mHideFromAccessibilityHelper.restoreImportantForAccessibility(mDragLayer);
if (restoreNavBarVisibilty) {
cling.setSystemUiVisibility(cling.getSystemUiVisibility() &
~View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
}
}
上述代码会去判断dismiss过,如果dismiss过,就不会再显示了.
接下来是这些:
private static String FIRST_RUN_PORTRAIT = "first_run_portrait";
private static String FIRST_RUN_LANDSCAPE = "first_run_landscape";
private static String WORKSPACE_PORTRAIT = "workspace_portrait";
private static String WORKSPACE_LANDSCAPE = "workspace_landscape";
private static String WORKSPACE_LARGE = "workspace_large";
private static String WORKSPACE_CUSTOM = "workspace_custom";
private static String FOLDER_PORTRAIT = "folder_portrait";
private static String FOLDER_LANDSCAPE = "folder_landscape";
private static String FOLDER_LARGE = "folder_large";
这些东西和:drawIdentifier有关,在attrs里定义它为string,在first_run_cling.xml,workspace_cling.xml,folder_cling.xml里都有定义:launcher:drawIdentifier
不难理解它是用来区分Cling类里,不同的实现的一个标识。Cling就如同一个躯壳,它里面包哪种类型的显示效果,它就显示啥,但这个躯壳它也清楚它自己包的是啥,就是通过这个变量mDrawIdentifier,是字xml
告诉它的,这个躯壳知道自己包的是啥之后,就好做一些细节上的处理.
我个人感觉这个Cling.java写的不够好,它有点违反设计模式里的单一职责。它居然是各种帮助的躯壳,就不应该自己又去知道里面的东西是啥,又根据里头的东西不一样又实现不一样的细节。其中有一段代码我非
常讨厌,就是我接下来要说的:
private int[] mTouchDownPt = new int[2];
private Drawable mFocusedHotseatApp;
private ComponentName mFocusedHotseatAppComponent;
private Rect mFocusedHotseatAppBounds;
//这个方法显然是FirstRunWorkspaceCling相关的东西
void setFocusedHotseatApp(int drawableId, int appRank, ComponentName cn, String title,
String description) {
....
}
这怎么又有hotSeat相关的代码呢?居然已经是一个躯壳就应该做它里面共有的事情,细节的不一样交给包裹里的东西实现,我是这么认为的.
Cling的初始化之init();
1.first_run_cling:Launcher.onCreate()->Launcher.showFirstRunCling()->Launcher.initCling();
2.workspace_cling:Cling.onClick()->Launcher.dismissFirstRunCling()->showFirstRunWorkspaceCling()->Launcher.initCling()
3.folder_cling:Folder.animateOpen->Launcher.showFirstRunFoldersCling()->Launcher.initCling();
可以看出,都是在要显示的时候初始化
if (isClingsEnabled() &&
!mSharedPrefs.getBoolean(Cling.FOLDER_CLING_DISMISSED_KEY, false)) {
Cling cling = initCling(R.id.folder_cling, R.id.cling_scrim,
true, true);
return cling;
} else {
removeCling(R.id.folder_cling);
return null;
}
如果发显示不该显示就remove()这个细节很赞!
后面dispatchDraw就是根据它包的不同东西去显示一些不同细节,我想说的是,为啥不能交给它们的子view实现。