经常有这样的系统程序:当第一次使用系统时,退出系统后,我们会在手机桌面上发现刚刚使用过的系统图标,也就是系统自动创建了快捷方式到手机桌面,以供下次方便快捷的使用。当然,我们也完全可以自己去为他设置快捷方式,这个相信大家都会。
由此场景引出几个问题:
问题一:
我们写程序的时候怎么样来实现类似的快捷方式创建呢?有哪些方式?
问题二:
要实现此功能需要怎么样去设计?系统是否有现成的支持可以利用?(答案是肯定的)
问题三:
如果系统支持刚功能的实现,我们就要去研究一下系统是怎么样去实现的,那应该怎么样下手去了解呢?
解决问题:
思路:首先回顾我们经常使用手机创建快捷方式的方法得知,系统提供并支持各应用快捷方式的创建,创建方式可以有:在主屏幕(HomeScreen)空白处长按弹出对话框创建;在Launcher列表中长按某个应用程序创建。所以就可以看看android本身是怎么去实现该功能的。
说明:HomeScreen本身是Launcher的一部分,所以归根结底还是Launcher的机制来创建快捷方式,所以就去分析Launcher的实现架构。
分析:
创建项目导入Launcher包,该源码在AndroidSource_GB\packages\apps\Launcher2下,导入后打开mainfest.xml文件(入口文件),找到main函数入口,如一下代码:
- <activity
- android:name="com.android.launcher2.Launcher"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:stateNotNeeded="true"
- android:theme="@style/Theme"
- android:screenOrientation="nosensor"
- android:windowSoftInputMode="stateUnspecified|adjustPan">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.MONKEY"/>
- </intent-filter>
- </activity>
<activity
android:name="com.android.launcher2.Launcher"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:theme="@style/Theme"
android:screenOrientation="nosensor"
android:windowSoftInputMode="stateUnspecified|adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
</intent-filter>
</activity>
可以发现,跟原先我们熟悉的配置不一样的地方在于intent-filter中少了<category android:name="android.intent.category.LAUNCHER" />
原因很简单,他本身就是Launcher,所以不需要;但多了<category android:name="android.intent.category.HOME" />的配置,意思就是当点击home键后,回到主界面,也就是回到launcher,这验证了我上面所说的“HomeScreen本身是Launcher的一部分”。
接下来打开Launcher.java,开始进入等待已久的Launcher源码分析。我们激动着……
分析入口当然就是onCreate生命周期开始方法,如下代码所示:
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- LauncherApplication app = ((LauncherApplication)getApplication());
- mModel = app.setLauncher(this);
- mIconCache = app.getIconCache();
- mDragController = new DragController(this);//拖动控制对象,就是按着屏幕向左向右拖动,再如长按图标可以将图标拖到其他地方放置
- mInflater = getLayoutInflater();
- mAppWidgetManager = AppWidgetManager.getInstance(this);
- mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
- mAppWidgetHost.startListening();
- if (PROFILE_STARTUP) {
- android.os.Debug.startMethodTracing(
- Environment.getExternalStorageDirectory() + "/launcher");
- }
- loadHotseats();
- checkForLocaleChange();//国际化语言发生变化的时候launcher也跟着变
- setWallpaperDimension();//设置图片尺寸大小
- setContentView(R.layout.launcher);
- setupViews();
- //注册上下文观察者,观察Content的数据变化
- /*
- * 实际上android架构存在这样的机制:
- * 用来观察ContentProvider里面的数据变化
- * 使用场景:当点击一个appwidget要添加到主界面显示的快捷方式时,实际上使用ContentProvider提供的insert方法,插了一条记录到数据库中
- * 然后利用该观察者模式去监听数据库值的改变,然后将改变的值从数据库读出来,更新到界面上来。
- */
- registerContentObservers();
- lockAllApps();//锁定位置,不允许移动
- mSavedState = savedInstanceState;
- restoreState(mSavedState);
- if (PROFILE_STARTUP) {
- android.os.Debug.stopMethodTracing();
- }
- if (!mRestoring) {
- mModel.startLoader(this, true);
- }
- // For handling default keys
- mDefaultKeySsb = new SpannableStringBuilder();
- Selection.setSelection(mDefaultKeySsb, 0);
- IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- registerReceiver(mCloseSystemDialogsReceiver, filter);
- }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LauncherApplication app = ((LauncherApplication)getApplication());
mModel = app.setLauncher(this);
mIconCache = app.getIconCache();
mDragController = new DragController(this);//拖动控制对象,就是按着屏幕向左向右拖动,再如长按图标可以将图标拖到其他地方放置
mInflater = getLayoutInflater();
mAppWidgetManager = AppWidgetManager.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
if (PROFILE_STARTUP) {
android.os.Debug.startMethodTracing(
Environment.getExternalStorageDirectory() + "/launcher");
}
loadHotseats();
checkForLocaleChange();//国际化语言发生变化的时候launcher也跟着变
setWallpaperDimension();//设置图片尺寸大小
setContentView(R.layout.launcher);
setupViews();
//注册上下文观察者,观察Content的数据变化
/*
* 实际上android架构存在这样的机制:
* 用来观察ContentProvider里面的数据变化
* 使用场景:当点击一个appwidget要添加到主界面显示的快捷方式时,实际上使用ContentProvider提供的insert方法,插了一条记录到数据库中
* 然后利用该观察者模式去监听数据库值的改变,然后将改变的值从数据库读出来,更新到界面上来。
*/
registerContentObservers();
lockAllApps();//锁定位置,不允许移动
mSavedState = savedInstanceState;
restoreState(mSavedState);
if (PROFILE_STARTUP) {
android.os.Debug.stopMethodTracing();
}
if (!mRestoring) {
mModel.startLoader(this, true);
}
// For handling default keys
mDefaultKeySsb = new SpannableStringBuilder();
Selection.setSelection(mDefaultKeySsb, 0);
IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
registerReceiver(mCloseSystemDialogsReceiver, filter);
}
代码部分主要看解析的,其他的随便看看,主要是保持对launcher的不那么神秘感就OK,接下来看个截图:
分析图:
该图就是launcher机制实现快捷方式创建的过程图,左边是长按屏幕弹出来的对话框,右边是点击左边“快捷方式”后跳转的图,即选择快捷方式图标。
分析该实现方式:
一、 分析左图:该图从表面上看是一个dialog对话框,要实现该图就要实现Dialog接口,所以就到Launcher.java查找一下,结果是有的,自己去查找(CreateShortcut)。
二、 分析右图:该图就是一个listview,将应用程序都列出来。问题是:系统怎么知道我的应用呢?是通过什么方式告诉他的呢?然后他就能将我们的应用列到这里。实际上是通过广播,在mainfest.xml配置广播,然后Launcher,让他去检索,所以我们只需要在我们的应用配置一下。后面会通过代码演示。
说明:实际上你要跟踪Launcher的Dialog事件,进一步了解其实现机制,这里省去。提示pickShortcut(),该方法里面设置回调函数,通过setResult()的方式回传值。
OK!居于以上的基础,我们就可以打造属于自己的应用程序的快捷方式创建了:
实现创建快捷方式的两种方法:
1:在主界面(HomeScreen)或者launcher中长按后,点击快捷方式添加
2:在应用程序中某个操作中添加
方式一:(分两步骤完成)
首先在mianfest.xml注册
- <activity android:name=".ShortcutSampleActivity">
- <intent-filter>
- <action android:name="android.intent.action.CREATE_SHORTCUT"></action>
- </intent-filter>
- </activity>
<activity android:name=".ShortcutSampleActivity">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT"></action>
</intent-filter>
</activity>
说明:该注册代码是为了让长按后将自己的应用程序出现在列表中,如上图。
其次在ShortcutSampleActivity.java中通过代码实现当点击了具体的某个应用程序后将图标创建到主界面中,生成快捷方式。
- public class ShortcutSampleActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // 将快捷方式显示在主界面中
- if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
- Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "LeonShortcut");
- intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
- Intent.ShortcutIconResource.fromContext(this, R.drawable.icon));
- intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(this,
- LeonLauncherDemoActivity.class));// 点击快捷方式跳转的页面
- setResult(RESULT_OK, intent);// 设置返回值
- finish();// 关窗体
- }
- }
- }
public class ShortcutSampleActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 将快捷方式显示在主界面中
if (Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "LeonShortcut");
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(this, R.drawable.icon));
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(this,
LeonLauncherDemoActivity.class));// 点击快捷方式跳转的页面
setResult(RESULT_OK, intent);// 设置返回值
finish();// 关窗体
}
}
}
方式二:(同样也是分两步骤完成)
首先在mianfest.xml注册权限
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
说明:快捷方式广播指定权限
其次通过按钮出发事件后实现快捷方式的创建,具体代码如下:
- public class LeonLauncherDemoActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- // 通过按钮直接添加快捷方式
- Button shortcutButton = (Button) this.findViewById(R.id.shortcut);
- shortcutButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "LeonShortcut");
- intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
- Intent.ShortcutIconResource.fromContext(
- LeonLauncherDemoActivity.this, R.drawable.icon));
- intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(
- LeonLauncherDemoActivity.this,
- LeonLauncherDemoActivity.class));// 点击快捷方式跳转的页面
- intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
- //通过广播的形式让launcher接收广播,然后根据action做出快捷方式的判断,从而系统自动创建快捷方式
- sendBroadcast(intent);
- }
- });
- }
- }
public class LeonLauncherDemoActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 通过按钮直接添加快捷方式
Button shortcutButton = (Button) this.findViewById(R.id.shortcut);
shortcutButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "LeonShortcut");
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(
LeonLauncherDemoActivity.this, R.drawable.icon));
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent(
LeonLauncherDemoActivity.this,
LeonLauncherDemoActivity.class));// 点击快捷方式跳转的页面
intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
//通过广播的形式让launcher接收广播,然后根据action做出快捷方式的判断,从而系统自动创建快捷方式
sendBroadcast(intent);
}
});
}
}
完成。