定製你自己的android手機桌面Launcher===一個完成Launcher開發的介紹
阿新 • • 發佈:2019-02-14
launcher也就是我們的Home,可以簡單地把它理解為一個簡化的linux GUI。作為一個GUI它首先必須完成它最本分的功能,
就是它必須能提供對所有應用程式(CATEGORY_LAUNCHER)的對映;不過作為一個 GUI,它除了做好本分之外還必須是符合大眾審美的美女(wallpaper);
另外還必須具有良好的互動性,沒有良好的互動性就像你對一位美女殷勤了半天,她卻直接對無視,那結果是比較糟糕的~~
所謂兵馬未動,糧草先行,在瞭解launcher的細節之前,我們首先需要完成對一些知識的掃盲。
當然這些知識我們都可以在SDK guide大叔那邊找到,俺可以很負責任地告訴大家,如果你把SDK guide大叔的三板斧都學會了,
APK你基本就處於無敵狀態了,絕對護甲+10000,最起碼基礎知識是夠了,其他比的就是創意了:
1、必須比較完整地瞭解APK的4個部件,尤其是Activity,現在可以簡單地理解Activity是一個應用程式的視窗。
2、必須瞭解UI的那部分內容,這部分內容是比較多的,English一般的我看得是比較抑鬱的,但如果你想設計一個符合自己審美要求的美女或者帥哥是必須得得了解的,
不需要一下能完全理解,但至少出了問題你知道去哪部分查~~
3、Resources那部分內容可以當百科全書查
4、intent那部分內容也是需要了解比較詳細的,他是和應用溝通的渠道,大家可以參考一下小斯大蝦寫的文件。
5、manifest必須瞭解,security可以看看
6、Graphic部分的內容是給需要更高品味的GUI設計提供的,雖然它可能主要用在遊戲上面,但我覺得如果要作出夠酷的GUI肯定是需要2d,3d引擎的。
7、AppWidget可以作為了解,用的時候再翻閱
各位路過的大蝦們肯定被這麼多的糧草給直接雷倒了,其實需要我們詳細掌握的是1和2,其他的都可以當作百科全書,但是如果能仔細地看透了那是最好了。
好,萬事俱備只欠東風了,我們首先來看看這個Home是在啥時候由誰來啟動的。這部分知識可以跳過,但是理解一下是好的,
你可以瞭解一個APK程序是如何懷胎十月之後誕生的。可能下面說到的詞彙有些生澀,所以建議先看看Android Anatomy and Physiology.pdf。
Linux kernel啟動以後會通過App_main程序來初始化android Runtime Java執行環境,而zygote是android的第一個程序。
所有的android的應用以及大部分系統服務都是通過zygote fork出來的子程序(我現在看到的只有native的service manager不是由zygote fork出來的)。
在system server中啟動的若干系統服務中與我們啟動程序相關的就是Acitivity Manager。
當systerm server啟動好所有服務以後,系統就進入”system ready”狀態,這個時候Activity Manager就登場了。
Activity Manager光看程式碼行就知道是一個重量級的服務,它主要管理Activity之間的跳轉,以及程序的生命週期。
當Activity Manager發現系統已經啟動好以後它就會發出一個intent:
Intent intent = new Intent(
mTopAction,
mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
通過這個category型別為home的intent,Activity Manager就會通過:
startActivityLocked(null, intent, null, null, 0, aInfo,
null, null, 0, 0, 0, false, false);
啟動Home程序了。而這個啟動Home程序的過程實際上還是去通過zygote fork出的一個子程序。
因此只要在manifest中具備這樣的intent-filter都可以在開機的時候作為Home啟動:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
多個home之間的switch會在開始的時候有個選擇,至於這個選擇好像是package manager來實現的,沒有仔細研究過。
好啦,瞭解了Lancher是如何執行的,我們再來看看整個lancher內部構造。看看一個lancher如何構造才算是個長得對得起觀眾的娃:
1、取得系統中所有安裝好的應用程式,並提供能執行這些程式的對映(形象的理解就是一個一個應用程式的小圖示)。這是Lancher的骨架,
正所謂何謂lancher是吧~~如果它不能提供應用程式的訪問,再好看也至多是一個華麗的花瓶而已,啥用米有。
2、更好一點我們就需要為這個設計良好的骨架提供一些畫皮以及一系列動畫效果,就是我們的wallpaper以及一系列的影象,
animation,graphic之類的。如果完成這部分工作,基本上我們的Home就基本成型了。
3、要使得我們的GUI更具親和性更方便使用,我們還提供一些額外的功能,比如說現在lancher實現的圖示的拖動,快捷方式等等。
這些都是仁者見仁智者見智的事情,取決你天馬行空的設計了。
總結起來一個lancher包含3個部分內容:應用程式資訊採集,事件處理,動畫。下面我們來講述一個自己的launcher的實現過程:
1、設計
從純使用者的角度來設計你的介面,你希望達到什麼樣的效果,儘量寫得詳細。尤其是應用程式資訊以如何方式的出現,
以及對它的操作一般都是一個好設計的亮點。我們現在設計一個簡單的,我們需要一個牆紙,然後在這個牆紙上面有一個條形的控制元件來顯示我們的應用程式圖示。
選擇這些圖示以後會在螢幕中間出現一張圖表示這個應用程式的功能,然後單擊這個圖就會開啟這個應用程式。
2、設計的總體實現
針對自己的設想來設計這個lancher的整體實現,如果有無法實現的內容就要及時修改設計,或者換一種設計方案。
我們這裡使用一個 FrameLayout來作為我們的Lancher的容器。然後分層,最下面一層用來放置可能需要的快捷方式以及我們的 wallpaper,
然後在wallpaper層上放一個我們自己定義的component來顯示我們的應用程式資訊。個人覺得FrameLayout是比較適合作為lancher的layout的,
它類似於photoshop的圖層這樣的控制,上面的圖層會覆蓋下面的圖層。
3、具體功能的具體實現
這裡具體到程式碼上就是設計各種java功能類了。對於wallpaper和圖示的拖拽移動這裡簡單提一下,更多的可以去看Android Lancher的實現。
wallpaper一般是註冊一個broadcastreceiver來處理系統中所有的更改背景圖片的請求,而圖示的脫拽移動則涉及到Draglayer這個類。
我們來把重點放在如何取得Android已安裝的應用程式資訊上。這裡就涉及到我們另外一個重要的service了,它就是package manager,
它負責對安裝的包進行管理。這裡涉及到一些許可權,我是直接照著android lancher的實現把它的許可權拷貝過來的:
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
下面來看看具體的實現,我們建立一個自己的控制元件,使用LinearLayout來裝載ImageSwitcher和Gallery兩個控制元件,
用 Gallery來顯示獲得的應用程式資訊,用ImageSwitcher來顯示應用程式的介紹,單擊ImageSwitcher就能開啟相應的應用程式。
public class MyLancherSwitcher extends LinearLayout implements ViewSwitcher.ViewFactory, AdapterView.OnItemSelectedListener,AdapterView.OnItemClickListener{
mImageSwitcher = new ImageSwitcher(context) ;
mGallery = new Gallery(context) ;
this.addView(mImageSwitcher, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,400)) ;
this.addView(mGallery, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, 80)) ;
}
架構選好了,下面就是如何為這個兩個控制元件提供已安裝的應用程式的資訊,首先我們取得package manager :
PackageManager manager = this.getContext().getPackageManager();
然後package manager通過intent資訊來提供相應的應用程式資訊:
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List <ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
Collections.sort(apps, new ResolveInfo.DisplayNameComparator(manager));
然後我們定義個自己的類MyAppInfo來儲存這些取得的資訊:
for (int i = 0; i < count; i++) {
MyAppInfo application = new MyAppInfo();
ResolveInfo info = apps.get(i);
application.title = info.loadLabel(manager);
application.setActivity(new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name),
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
application.icon = info.activityInfo.loadIcon(manager);
mApplications.add(application);
}
final void setActivity(ComponentName className, int launchFlags) {
intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setComponent(className);
intent.setFlags(launchFlags);
}
我們使用一個數組來儲存這些MyAppInfo資訊,並把它提供給Gallery:
private static ArrayList <MyAppInfo> mApplications;
mGallery.setAdapter(new ApplicationsAdapter(this.getContext(), mApplications)) ;
最後過載ArrayAdapter <MyAppInfo>的getView()函式對畫圖進行一些裁減就OK了,Gallery就能顯示我們的應用程式的圖片資訊了。
最後我們把Gallery中被選中的圖片的應用程式資訊傳給ImageSwitcher,併為ImageSwithcher註冊一個按鍵事件,就可以啟動應用程式了:
private OnClickListener mImageSwitcherListener = new OnClickListener(){
public void onClick(View v){
if(mAppInfo == null){}
else
v.getContext().startActivity(mAppInfo.intent);
}
} ;
這樣基本我們lancher的骨架就搞定了,不過還有一個,那就是當我們新安裝或刪除一個應用程式的時候,我們的Home必須捕獲這個intent,
並及時調整home裡面的應用程式資訊,因此我要為我們的控制元件註冊一個package的broadcast receiver :
private class ApplicationsIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
loadApplications(false);
}
}
private void registerIntentReceivers() {
filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
registerReceiver(mApplicationsReceiver, filter);
}
Ok這樣我們的lancher就基本完成了,剩下的就是為各個事件新增你需要的動畫效果,這裡就不說了。以前沒有經歷過java程式設計,
但是個人覺得 android java應用的程式設計還是相對簡單的,只是因為東西很多所以顯得有點複雜,但是基本上使用起來還是很方便的,
基本就是繼承之後過載或者實現介面,而且 Android為Ui的程式設計提供了一個更方便的方式就是使用XML,使用xml可以更直觀地來進行你的設計,
而且也方便了你以後的修改和移植。
就是它必須能提供對所有應用程式(CATEGORY_LAUNCHER)的對映;不過作為一個 GUI,它除了做好本分之外還必須是符合大眾審美的美女(wallpaper);
另外還必須具有良好的互動性,沒有良好的互動性就像你對一位美女殷勤了半天,她卻直接對無視,那結果是比較糟糕的~~
所謂兵馬未動,糧草先行,在瞭解launcher的細節之前,我們首先需要完成對一些知識的掃盲。
當然這些知識我們都可以在SDK guide大叔那邊找到,俺可以很負責任地告訴大家,如果你把SDK guide大叔的三板斧都學會了,
APK你基本就處於無敵狀態了,絕對護甲+10000,最起碼基礎知識是夠了,其他比的就是創意了:
1、必須比較完整地瞭解APK的4個部件,尤其是Activity,現在可以簡單地理解Activity是一個應用程式的視窗。
2、必須瞭解UI的那部分內容,這部分內容是比較多的,English一般的我看得是比較抑鬱的,但如果你想設計一個符合自己審美要求的美女或者帥哥是必須得得了解的,
不需要一下能完全理解,但至少出了問題你知道去哪部分查~~
3、Resources那部分內容可以當百科全書查
4、intent那部分內容也是需要了解比較詳細的,他是和應用溝通的渠道,大家可以參考一下小斯大蝦寫的文件。
5、manifest必須瞭解,security可以看看
6、Graphic部分的內容是給需要更高品味的GUI設計提供的,雖然它可能主要用在遊戲上面,但我覺得如果要作出夠酷的GUI肯定是需要2d,3d引擎的。
7、AppWidget可以作為了解,用的時候再翻閱
各位路過的大蝦們肯定被這麼多的糧草給直接雷倒了,其實需要我們詳細掌握的是1和2,其他的都可以當作百科全書,但是如果能仔細地看透了那是最好了。
好,萬事俱備只欠東風了,我們首先來看看這個Home是在啥時候由誰來啟動的。這部分知識可以跳過,但是理解一下是好的,
你可以瞭解一個APK程序是如何懷胎十月之後誕生的。可能下面說到的詞彙有些生澀,所以建議先看看Android Anatomy and Physiology.pdf。
Linux kernel啟動以後會通過App_main程序來初始化android Runtime Java執行環境,而zygote是android的第一個程序。
所有的android的應用以及大部分系統服務都是通過zygote fork出來的子程序(我現在看到的只有native的service manager不是由zygote fork出來的)。
在system server中啟動的若干系統服務中與我們啟動程序相關的就是Acitivity Manager。
當systerm server啟動好所有服務以後,系統就進入”system ready”狀態,這個時候Activity Manager就登場了。
Activity Manager光看程式碼行就知道是一個重量級的服務,它主要管理Activity之間的跳轉,以及程序的生命週期。
當Activity Manager發現系統已經啟動好以後它就會發出一個intent:
Intent intent = new Intent(
mTopAction,
mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
通過這個category型別為home的intent,Activity Manager就會通過:
startActivityLocked(null, intent, null, null, 0, aInfo,
null, null, 0, 0, 0, false, false);
啟動Home程序了。而這個啟動Home程序的過程實際上還是去通過zygote fork出的一個子程序。
因此只要在manifest中具備這樣的intent-filter都可以在開機的時候作為Home啟動:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
多個home之間的switch會在開始的時候有個選擇,至於這個選擇好像是package manager來實現的,沒有仔細研究過。
好啦,瞭解了Lancher是如何執行的,我們再來看看整個lancher內部構造。看看一個lancher如何構造才算是個長得對得起觀眾的娃:
1、取得系統中所有安裝好的應用程式,並提供能執行這些程式的對映(形象的理解就是一個一個應用程式的小圖示)。這是Lancher的骨架,
正所謂何謂lancher是吧~~如果它不能提供應用程式的訪問,再好看也至多是一個華麗的花瓶而已,啥用米有。
2、更好一點我們就需要為這個設計良好的骨架提供一些畫皮以及一系列動畫效果,就是我們的wallpaper以及一系列的影象,
animation,graphic之類的。如果完成這部分工作,基本上我們的Home就基本成型了。
3、要使得我們的GUI更具親和性更方便使用,我們還提供一些額外的功能,比如說現在lancher實現的圖示的拖動,快捷方式等等。
這些都是仁者見仁智者見智的事情,取決你天馬行空的設計了。
總結起來一個lancher包含3個部分內容:應用程式資訊採集,事件處理,動畫。下面我們來講述一個自己的launcher的實現過程:
1、設計
從純使用者的角度來設計你的介面,你希望達到什麼樣的效果,儘量寫得詳細。尤其是應用程式資訊以如何方式的出現,
以及對它的操作一般都是一個好設計的亮點。我們現在設計一個簡單的,我們需要一個牆紙,然後在這個牆紙上面有一個條形的控制元件來顯示我們的應用程式圖示。
選擇這些圖示以後會在螢幕中間出現一張圖表示這個應用程式的功能,然後單擊這個圖就會開啟這個應用程式。
2、設計的總體實現
針對自己的設想來設計這個lancher的整體實現,如果有無法實現的內容就要及時修改設計,或者換一種設計方案。
我們這裡使用一個 FrameLayout來作為我們的Lancher的容器。然後分層,最下面一層用來放置可能需要的快捷方式以及我們的 wallpaper,
然後在wallpaper層上放一個我們自己定義的component來顯示我們的應用程式資訊。個人覺得FrameLayout是比較適合作為lancher的layout的,
它類似於photoshop的圖層這樣的控制,上面的圖層會覆蓋下面的圖層。
3、具體功能的具體實現
這裡具體到程式碼上就是設計各種java功能類了。對於wallpaper和圖示的拖拽移動這裡簡單提一下,更多的可以去看Android Lancher的實現。
wallpaper一般是註冊一個broadcastreceiver來處理系統中所有的更改背景圖片的請求,而圖示的脫拽移動則涉及到Draglayer這個類。
我們來把重點放在如何取得Android已安裝的應用程式資訊上。這裡就涉及到我們另外一個重要的service了,它就是package manager,
它負責對安裝的包進行管理。這裡涉及到一些許可權,我是直接照著android lancher的實現把它的許可權拷貝過來的:
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
下面來看看具體的實現,我們建立一個自己的控制元件,使用LinearLayout來裝載ImageSwitcher和Gallery兩個控制元件,
用 Gallery來顯示獲得的應用程式資訊,用ImageSwitcher來顯示應用程式的介紹,單擊ImageSwitcher就能開啟相應的應用程式。
public class MyLancherSwitcher extends LinearLayout implements ViewSwitcher.ViewFactory, AdapterView.OnItemSelectedListener,AdapterView.OnItemClickListener{
mImageSwitcher = new ImageSwitcher(context) ;
mGallery = new Gallery(context) ;
this.addView(mImageSwitcher, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,400)) ;
this.addView(mGallery, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, 80)) ;
}
架構選好了,下面就是如何為這個兩個控制元件提供已安裝的應用程式的資訊,首先我們取得package manager :
PackageManager manager = this.getContext().getPackageManager();
然後package manager通過intent資訊來提供相應的應用程式資訊:
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List <ResolveInfo> apps = manager.queryIntentActivities(mainIntent, 0);
Collections.sort(apps, new ResolveInfo.DisplayNameComparator(manager));
然後我們定義個自己的類MyAppInfo來儲存這些取得的資訊:
for (int i = 0; i < count; i++) {
MyAppInfo application = new MyAppInfo();
ResolveInfo info = apps.get(i);
application.title = info.loadLabel(manager);
application.setActivity(new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name),
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
application.icon = info.activityInfo.loadIcon(manager);
mApplications.add(application);
}
final void setActivity(ComponentName className, int launchFlags) {
intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setComponent(className);
intent.setFlags(launchFlags);
}
我們使用一個數組來儲存這些MyAppInfo資訊,並把它提供給Gallery:
private static ArrayList <MyAppInfo> mApplications;
mGallery.setAdapter(new ApplicationsAdapter(this.getContext(), mApplications)) ;
最後過載ArrayAdapter <MyAppInfo>的getView()函式對畫圖進行一些裁減就OK了,Gallery就能顯示我們的應用程式的圖片資訊了。
最後我們把Gallery中被選中的圖片的應用程式資訊傳給ImageSwitcher,併為ImageSwithcher註冊一個按鍵事件,就可以啟動應用程式了:
private OnClickListener mImageSwitcherListener = new OnClickListener(){
public void onClick(View v){
if(mAppInfo == null){}
else
v.getContext().startActivity(mAppInfo.intent);
}
} ;
這樣基本我們lancher的骨架就搞定了,不過還有一個,那就是當我們新安裝或刪除一個應用程式的時候,我們的Home必須捕獲這個intent,
並及時調整home裡面的應用程式資訊,因此我要為我們的控制元件註冊一個package的broadcast receiver :
private class ApplicationsIntentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
loadApplications(false);
}
}
private void registerIntentReceivers() {
filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
registerReceiver(mApplicationsReceiver, filter);
}
Ok這樣我們的lancher就基本完成了,剩下的就是為各個事件新增你需要的動畫效果,這裡就不說了。以前沒有經歷過java程式設計,
但是個人覺得 android java應用的程式設計還是相對簡單的,只是因為東西很多所以顯得有點複雜,但是基本上使用起來還是很方便的,
基本就是繼承之後過載或者實現介面,而且 Android為Ui的程式設計提供了一個更方便的方式就是使用XML,使用xml可以更直觀地來進行你的設計,
而且也方便了你以後的修改和移植。