现在的位置: 首页 > 综合 > 正文

android Launcher基础知识

2012年01月31日 ⁄ 综合 ⁄ 共 11890字 ⁄ 字号 评论关闭

        本文将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(五 )

抱歉!评论已关闭.