本文將www.bangchui.org網路中的《android手把手教你開發launcher》系列文章進行了整理。這篇文章對lancher的準系統點的實現做了簡單介紹,看完後會對lancher有比較深刻的認識。
1、launcher最簡單一實例
launcher,也就是android的傳統型應用程式。是android2.3的launcher應用程式:
接下來我們要開發一個自己的launcher,使其替代系統的預設launcher。
怎樣使我們的應用程式成為一個launcher?
下面我們就建立一個叫做MyHome的工程,具體步驟略。建立完工程後整個目錄結構如:
現在我們的AndroidManifest.xml檔案這樣的:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.bangchui.myhome" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MyHome" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
請注意<intent-filter>
</intent-filter>裡面的內容。
下面我們在其中添加上以下兩行:
<category android:name="android.intent.category.HOME" /><category android:name="android.intent.category.DEFAULT" />
此時AndroidManifest.xml檔案是這樣:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.bangchui.myhome" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MyHome" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> </application> </manifest>
此時運行程式,我們看不到任何特別之處。當按下home鍵時(模擬器上按下home會調出案頭應用),程式
我們看到了,我們開發的Myhome跟Launcher出現在了一起。
重啟模擬器,我們看到我們自己的程式已經可以作為home來運行了。
ok。 第一步完成了:把我們的應用程式作為home。
總結一下:要把我們的應用程式作為home,只需要在AndroidManifest.xml中添加:
<category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" />
2、列出安裝的應用程式
列出已經安裝的應用程式是作為launcher必不可少的功能。下面我們就講解怎樣將應用程式列出來。程式運行後的樣子如下:
一. 修改main.xml,在其中添加一個GridView用來顯示應用程式列表。
修改後如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <GridView android:layout_width="match_parent" android:id="@+id/apps_list" android:numColumns="4" android:layout_height="wrap_content"> </GridView></LinearLayout>
二 . 通過PackageManager的api 查詢已經安裝的apk
我們寫一個叫做loadApps的方法將活得的應用程式列表放到private List<ResolveInfo> mApps; 中,如下:
private void loadApps() { Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); mApps = getPackageManager().queryIntentActivities(mainIntent, 0); }
三. 實現用於顯示Gridview的Adapter,使其顯示獲得的應用程式列表
最後整個Activity的代碼如下
package org.bangchui.myhome; import java.util.List; import android.app.Activity; import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.GridView; import android.widget.ImageView; public class MyHome extends Activity { GridView mGrid; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); loadApps(); setContentView(R.layout.main); mGrid = (GridView) findViewById(R.id.apps_list); mGrid.setAdapter(new AppsAdapter()); } private List<ResolveInfo> mApps; private void loadApps() { Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); mApps = getPackageManager().queryIntentActivities(mainIntent, 0); } public class AppsAdapter extends BaseAdapter { public AppsAdapter() { } public View getView(int position, View convertView, ViewGroup parent) { ImageView i; if (convertView == null) { i = new ImageView(MyHome.this); i.setScaleType(ImageView.ScaleType.FIT_CENTER); i.setLayoutParams(new GridView.LayoutParams(50, 50)); } else { i = (ImageView) convertView; } ResolveInfo info = mApps.get(position); i.setImageDrawable(info.activityInfo.loadIcon(getPackageManager())); return i; } public final int getCount() { return mApps.size(); } public final Object getItem(int position) { return mApps.get(position); } public final long getItemId(int position) { return position; } } }
3、啟動安裝的應用程式
1. 監聽GridView的onItemClick事件
設定一個監聽器是為了當gridView的某項被點擊時,會有一個回呼函數通知我們。
我們調用mGrid.setOnItemClickListener(listener); 設定一個監聽器
mGrid.setOnItemClickListener(listener)中的listener是一個介面,其類型為:android.widget.AdapterView.OnItemClickListener,如所示:
下面我們new一個android.widget.AdapterView.OnItemClickListener類型的對象作為參數。我們直接使用eclipde的自動補全功能來完成OnItemClickListener 的定義:
private OnItemClickListener listener = new OnItemClickListener() {@Override public void onItemClick(AdapterView<?> parent, View view, int position,long id) {}};
介面OnItemClickListener 中有一個方法叫做onItemClick,我們實現它即可。下面我對onItemClick的幾個參數略作說明:
parent 略
view 被點擊的view
position 被點擊項的位置
id 被點擊項的id
2.啟動被點擊應用的activity
一般來講,我們根據position即可知道被點擊的項目是哪一項了。現在我們根據被點擊的項目,取出對應的應用程式資料(主要是其中的主activity),然後啟動activity。用下面代碼實現:
@Override public void onItemClick(AdapterView<?> parent, View view, int position,long id) { ResolveInfo info = mApps.get(position); //該應用的包名 String pkg = info.activityInfo.packageName; //應用的主activity類 String cls = info.activityInfo.name; ComponentName componet = new ComponentName(pkg, cls); Intent i = new Intent(); i.setComponent(componet); startActivity(i); }
例如,我們點擊計算機時,啟動了計算機,如:
現在整個類代碼如下:
package org.bangchui.myhome;import java.util.List;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.pm.ResolveInfo;import android.os.Bundle;import android.view.View;import android.view.ViewGroup;import android.widget.AdapterView;import android.widget.BaseAdapter;import android.widget.GridView;import android.widget.ImageView;import android.widget.AdapterView.OnItemClickListener;public class MyHome extends Activity { private List<ResolveInfo> mApps; GridView mGrid; private OnItemClickListener listener = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position,long id) { ResolveInfo info = mApps.get(position); //該應用的包名 String pkg = info.activityInfo.packageName; //應用的主activity類 String cls = info.activityInfo.name; ComponentName componet = new ComponentName(pkg, cls); Intent i = new Intent(); i.setComponent(componet); startActivity(i); } }; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); loadApps(); setContentView(R.layout.main); mGrid = (GridView) findViewById(R.id.apps_list); mGrid.setAdapter(new AppsAdapter()); mGrid.setOnItemClickListener(listener); } private void loadApps() { Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); mApps = getPackageManager().queryIntentActivities(mainIntent, 0); } public class AppsAdapter extends BaseAdapter { public AppsAdapter() { } public View getView(int position, View convertView, ViewGroup parent) { ImageView i; if (convertView == null) { i = new ImageView(MyHome.this); i.setScaleType(ImageView.ScaleType.FIT_CENTER); i.setLayoutParams(new GridView.LayoutParams(50, 50)); } else { i = (ImageView) convertView; } ResolveInfo info = mApps.get(position); i.setImageDrawable(info.activityInfo.loadIcon(getPackageManager())); return i; } public final int getCount() { return mApps.size(); } public final Object getItem(int position) { return mApps.get(position); } public final long getItemId(int position) { return position; } }}
4、顯示widget
我們要達到這樣的效果:點擊“add widget” 後彈出widget列表,之後選擇一個widget後顯示在介面上,如下:
1. 擷取widget資訊
擷取widget其實非常簡單,我們只需要發送一個請求到系統,系統就會開啟widget的列表,然後我們選擇一個即可。代碼如下:
void addWidget() { int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK); pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); // start the pick activity startActivityForResult(pickIntent, [b]REQUEST_PICK_APPWIDGET[/b]); }
2. 添加widget的view到layout中
當選擇一個widget後會通過onActivityResult 通知到activity,widget的資訊被包含在 Intent data中,詳情看代碼注釋
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // The pattern used here is that a user PICKs a specific application, // which, depending on the target, might need to CREATE the actual // target. // For example, the user would PICK_SHORTCUT for "Music playlist", and // we // launch over to the Music app to actually CREATE_SHORTCUT. if (resultCode == RESULT_OK) { switch (requestCode) { case REQUEST_PICK_APPWIDGET: addAppWidget(data); break; case REQUEST_CREATE_APPWIDGET: completeAddAppWidget(data); break; } } } void addAppWidget(Intent data) { // TODO: catch bad widget exception when sent int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId); //widget 包含設定資訊不為空白,則啟動widget的設定介面 if (appWidget.configure != null) { // Launch over to configure widget, if needed Intent intent = new Intent( AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); intent.setComponent(appWidget.configure); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET); } else { // widget 包含設定資訊為空白,直接添加widget到layout中 // Otherwise just add it onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data); } } void startActivityForResultSafely(Intent intent, int requestCode) { try { startActivityForResult(intent, requestCode); } catch (ActivityNotFoundException e) { Toast.makeText(this, "activity_not_found", Toast.LENGTH_SHORT).show(); } catch (SecurityException e) { Toast.makeText(this, "activity_not_found", Toast.LENGTH_SHORT).show(); } } /** * 添加widget資訊到layout中 * * @param data 包含了widget的資訊 */ private void completeAddAppWidget(Intent data) { Bundle extras = data.getExtras(); int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); Log.d(TAG, "dumping extras content=" + extras.toString()); AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); // Perform actual inflation because we're live synchronized (mLock) { //擷取顯示widget的view mHostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); mHostView.setAppWidget(appWidgetId, appWidgetInfo); //將擷取的view添加早layout中 LayoutParams lp = new LinearLayout.LayoutParams(appWidgetInfo.minWidth, appWidgetInfo.minHeight); mainLayout.addView(mHostView, lp); mHostView.requestLayout(); } }
5、顯示和設定壁紙
顯示壁紙也是launcher必不可少的功能,下面我們看看如何讓我們開發的launcher來顯示壁紙。
建立一個叫做ShowWallpaper的工程,具體步驟略。
一. 顯示壁紙
要在我們的activity裡顯示一個壁紙非常簡單(包括動態壁紙也如此),我們只需要定義一個theme使其繼承自android:Theme.Wallpaper,然後在activity中使用這個theme就ok了。
在res/valuse下面增加一個xml檔案,其名稱為styles.xml ,內容如下:
<resources> <style name="Theme" parent="android:Theme.Wallpaper"> <!-- windowNoTitle設定為true,去掉標題列 --> <item name="android:windowNoTitle">true</item> </style> </resources>
此時整個工程的結果如下:
下面在AndroidManifest.xml中使用這個theme,如下代碼所示:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.test" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".ShowWallpaper" android:theme="@style/Theme" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
好了,運行程式,可以看到壁紙的顯示效果了:(顯示的是預設定的動態壁紙:星系)
用代碼設定壁紙也是非常地簡單的事,我們只需要向系統發送一個“佈建要求”就足夠了,其它的事情系統處理。
用下面代碼錶示:
public void onSetWallpaper(View view) { //產生一個設定壁紙的請求 final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER); Intent chooser = Intent.createChooser(pickWallpaper,"chooser_wallpaper"); //發送設定壁紙的請求 startActivity(chooser); }
為了調用上面這段代碼,我們在xml中添加一個button,並設定回呼函數,如:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="setWallpaper" android:onClick="onSetWallpaper" /></LinearLayout>
最後運行代碼,步驟如所示:
設定壁紙後:
參考資料:
android手把手教你開發launcher(一)
android手把手教你開發launcher(二)
android手把手教你開發launcher(三 )
android手把手教你開發launcher(四 )
android手把手教你開發launcher(五 )