http://blog.csdn.net/xianming01/article/details/7678050
http://blog.csdn.net/qinjuning/article/details/7197359
本文将网络中的《android手把手教你开发launcher》系列文章进行了整理。这篇文章对lancher的基本功能点的实现做了简单介绍,看完后会对lancher有比较深刻的认识。
1、launcher最简单实例
launcher,也就是android的桌面应用程序。下图是android2.3的launcher应用程序:
接下来我们要开发一个自己的launcher,使其替代系统的默认launcher。
怎样使我们的应用程序成为一个launcher?
下面我们就新建一个叫做MyHome的工程,具体步骤略。创建完工程后整个目录结构如下图:
现在我们的AndroidManifest.xml文件这样的:
"1.0" encoding= "utf-8" ?>
""
package = "org.bangchui.myhome"
android:versionCode= "1"
android:versionName= "1.0" >
"@drawable/icon" android:label= "@string/app_name" >
".MyHome"
android:label= "@string/app_name" >
"android.intent.action.MAIN" />
"android.intent.category.LAUNCHER" />
请注意
里面的内容。
下面我们在其中添加上以下两行:
"android.intent.category.HOME" />
"android.intent.category.DEFAULT" />
此时AndroidManifest.xml文件是这样:
xml version = "1.0" encoding = "utf-8" ?>
< manifest xmlns: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中添加:
"android.intent.category.HOME" />
"android.intent.category.DEFAULT" />
2、列出安装的应用程序
列出已经安装的应用程序是作为launcher必不可少的功能。下面我们就讲解怎样将应用程序列出来。程序运行后的样子如下:
一. 修改main.xml,在其中添加一个GridView用来显示应用程序列表。
修改后如下:
xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout xmlns: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
mApps; 中,如下:
private void loadApps() {
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null );
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mApps = getPackageManager().queryIntentActivities(mainIntent, 0 );
}
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;
@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 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;
}
}
}
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 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) {}
};
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);
ComponentName componet = new ComponentName(pkg, cls);
Intent i = new Intent();
i.setComponent(componet);
startActivity(i);
}
@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 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;
String cls = info.activityInfo.name;
ComponentName componet = new ComponentName(pkg, cls);
Intent i = new Intent();
i.setComponent(componet);
startActivity(i);
}
};
@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;
}
}
}
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 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);
startActivityForResult(pickIntent, [b]REQUEST_PICK_APPWIDGET[/b]);
}
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) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUEST_PICK_APPWIDGET:
addAppWidget(data);
break ;
case REQUEST_CREATE_APPWIDGET:
completeAddAppWidget(data);
break ;
}
}
}
void addAppWidget(Intent data) {
int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, - 1 );
AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
if (appWidget.configure != null ) {
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 {
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();
}
}
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);
synchronized (mLock) {
mHostView = mAppWidgetHost.createView( this , appWidgetId, appWidgetInfo);
mHostView.setAppWidget(appWidgetId, appWidgetInfo);
LayoutParams lp = new LinearLayout.LayoutParams(appWidgetInfo.minWidth, appWidgetInfo.minHeight);
mainLayout.addView(mHostView, lp);
mHostView.requestLayout();
}
}
@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" >
< item name = "android:windowNoTitle" > true item >
style >
resources >
此时整个工程的结果如下:
下面在AndroidManifest.xml中使用这个theme,如下代码所示:
xml version = "1.0" encoding = "utf-8" ?>
< manifest xmlns: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);
}
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 = ""
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 >
最后运行代码,步骤如下图所示:
设置壁纸后:
阅读(1111) | 评论(0) | 转发(0) |