Android中实现Launcher功能之一 ----- 添加快捷方式

转载请注明出处:http://blog.csdn.net/qinjuning

前言: 最近一直在看Launcher模块,经过差不多两个月学习,终于摸透了Launcher的一些主要功能实现,目前继续还处于

摸索状态。未看Launcher时,于我而言,只能膜拜,以为所有功能都是它实现的 ;入门后,才发现,Launcher的很多功能只是

集成了框架/应用程序提供的功能。很多陌生的东西,只有接触了才感叹:“oh ,原来是这样的!”

今天先给大家分享下Launcher如何实现添加快捷方式(Shortcut) ,后续会慢慢增加其他方面的功能,帮助大家“一叶而知秋”。

具体来说,Launcher中的快捷方式有两种类型:

1 、"伪"快捷方式 —— 应用程序类型

2 、"真"快捷方式 —— Activity具备<action/>为ACTION_CREATE_SHORTCUT的配置信息

这两种类型的快捷方式是怎么勾搭在一起的,在下面大家通过代码自己理解,也不方便细说了。

关于如何创建一个”真”快捷方式(Shortcut)的App ,大家可以先去看看杨丰盛老师的博客《Android特色开发之桌面组》 ,

从中我们可以掌握如何创建一个快捷方式,实现也不是很难。

知识点介绍:

知识点一、ACTION_PICK_ACTIVITY使用说明 ,具体可以参考SDK Intent

功能:显示匹配附加值为EXTRA_INTENT所有Activity,并将它们以列表呈现给用户。当用户从该列表选中一项

时,并不会启动该Activity(这与与ACTION_CHOOSER不同,此Action会启动用户选择的Activity),而是将该Activity的详细信

(可能包括Action、ComponentName、data信息等)以Intent对象返回给调用者(通常为onActivityResult方法)。

附加值:EXTRA_INTENT显示所有匹配显示所有匹配附加值EXTRA_INTENTActivity,

EXTRA_TITLE 作为显示列表即所有Activity的标题 。

因此,根据ACTION_PICK_ACTIVITY的特性,真正地创建快捷方式需要两步走:

第一步:发送ACTION_PICK_ACTIVITY以及EXTRA_INTENT找到我们希望能创建快捷方式的Activity列表。

第二步:根据第一步所选择的Activity返回的Intent对象,再次发送此Intent对象,即可创建该Activity提供给

我们快捷方式了。

例如,下面我们只是简单的发送一个请求显示所有应用程序的Intent,如下:

  //重新发送一个Action为Pick_Activity的Intent,获取所有应用程序信息
                Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
                Intent mainIntent = new Intent () ;
                mainIntent.setAction(Intent.ACTION_MAIN);
                mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
                
                pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
                pickIntent.putExtra(Intent.EXTRA_TITLE, "选择应用程序"); //设置界面title
                
                //继续选择所有应用程序
                startActivityForResult(pickIntent,MY_REQUEST_ALL_APPLICATION );


ACTION_PICK_ACTIVITY效果图如下:

点击某一具体Activity , 即可选择创建该Activity的快捷方式了。

知识点二、 Intent.ShortcutIconResource类介绍

功能: 为快捷方式(Shortcut)和文件夹(live folder)提供图片资源

常用方法为:

public static Intent.ShortcutIconResource fromContext(Context context, int resourceId)

功能: 创建一个 Intent.ShortcutIconResource 对象

参数说明:context Context类对象

resourceId 具体的图片资源id 。

常用属性:

packageName 该应用程序所在包名,类型为 packageName:type/entityname

resourceName resourceId所对应地的资源名

例如 某个图片资源 R.id.icon = 0x7f020000,则resourceName 为 packageName:drawable/icon

具体怎么通过 Intent.ShortcutIconResource对象获取图片资源,请参考示例Demo。


示例Demo

说明:点击创建快捷方式对话框后, 选择某一项具体的快捷方式,即可添加至MainActivity界面中 ,继续点击每个View,则

可启动该快快捷方式的App,挺给力的吧。

PS: 由于我只是简单的利用了LinearLayout去当容器,会存在局限性,大家可在此基础上,利用GridView/ListView构建更好

的布局,当然更NB的是,去提供类似Launcher的自定义布局。

由于执行快捷方式可能需要一些特定的权限,因此我们必须得在AndroidManifest.xml里配置对应的权限。例如,直接拨打电话

需要的权限为: <uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>

如上效果图,增加几个快捷方式后截图如下,点击即可启动该应用。

主工程逻辑如下:

package com.qin.addshortcut;

import java.util.ArrayList;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
/**
 * 
 * @author http://http://blog.csdn.net/qinjuning
 */
public class MainActivity extends Activity implements View.OnClickListener
{

	private LinearLayout linearlayout  ;   //用来存放所有快捷方式的容器 --- 父视图
    private Button btAddShortCut;

    
    private static final int MY_REQUEST_SHORT_CUT = 1;   //  第一步 、 显示所有能创建快捷方式的Activity
    private static final int MY_CREATE_SHOURT_CUT = 2;         //第二步、 创建 真快捷方式
    private static final int MY_REQUEST_ALL_APPLICATION = 3 ;  //第二步 、创建 伪快捷方式 -- 应用程序类
    private static String TAG = "AddShortActivity" ;
    
    private PackageManager mPackageManager = null ;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        btAddShortCut = (Button) findViewById(R.id.bt_addShortcut);
        linearlayout = (LinearLayout)findViewById(R.id.linearLayout) ;
        linearlayout.setOnClickListener(this) ;
        
        //演示如何通过一个资源的type获取该资源文件在R文件中的整数描述符
        // getIdentifier 参数为 package:type/entry . 例如求icon的资源描述符如下:
        int iconId = this.getResources().getIdentifier("com.qin.addshortcut:drawable/icon", null, null);        
        Log.i(TAG, " icon id : " + iconId);
        
        //获取PackageManager对象
        mPackageManager = getPackageManager();
               
        btAddShortCut.setOnClickListener(new View.OnClickListener()
        {

            @Override
            public void onClick(View v)
            {
            	//创建快捷方式对话框
            	createShortDialog() ;
            }
        });
        
    }
    private void createShortDialog(){
    	
        //begin :添加创建应用程序的图标('快捷方式")
    	// 此bundle值为附加的创建PICK_ACTIVITY的一个列表项
        Bundle bundle = new Bundle() ;
        //设置name
        ArrayList<String> shortcutNames = new ArrayList<String>();
        shortcutNames.add("应用程序");
        bundle.putStringArrayList(Intent.EXTRA_SHORTCUT_NAME, shortcutNames);
        //设置对应的头像
        ArrayList<ShortcutIconResource> shortcutIconRes= new ArrayList<ShortcutIconResource>();
        shortcutIconRes.add(ShortcutIconResource.fromContext(MainActivity.this, R.drawable.icon));
        bundle.putParcelableArrayList(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, shortcutIconRes);
        // end               
        
        Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);

        Intent extraIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);

        pickIntent.putExtra(Intent.EXTRA_INTENT, extraIntent);//所要查找的Activity Intent
        pickIntent.putExtra(Intent.EXTRA_TITLE, "选择快捷方式");  // Title
        //看到没 , 伪快捷方式 是在这儿创建地。 
        pickIntent.putExtras(bundle);
        
        startActivityForResult(pickIntent, MY_REQUEST_SHORT_CUT); 
    }
    
    
    protected void onActivityResult(int requestCode, int resultCode, final Intent data)
    {
        super.onActivityResult(requestCode, resultCode, data);
        //未成功的选择任何一个shortcut(比如直接按back键) , 直接返回
        if(resultCode == RESULT_CANCELED )
        	return ;
        
        
        switch(requestCode){
        case MY_REQUEST_SHORT_CUT:
        	//第一次发送 PICK_Activity后 ,对所选列表项进行选择后,做的中间处理操作,需要判断是选择“应用程序” 抑或真正滴快捷方式
        	 // 获得快捷方式Label
            String label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
            //这个字段是我们之前主动添加的
            if(label.equals("应用程序")){  
                //重新发送一个Action为Pick_Activity的Intent,获取所有应用程序信息
                Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
                Intent mainIntent = new Intent () ;
                mainIntent.setAction(Intent.ACTION_MAIN);
                mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
                
                pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
                pickIntent.putExtra(Intent.EXTRA_TITLE, "选择应用程序"); //设置界面title
                
                //继续选择所有应用程序
                startActivityForResult(pickIntent,MY_REQUEST_ALL_APPLICATION );
                
            }
            else{  //重新请求创建上一次点击的Activity , 启动后即可创建	
            	Log.v(TAG, "MY_REQUEST_SHORT_CUT Intent Info--- >" + data);
            	//下一步,创建某一具体的shortcut
                startActivityForResult(data, MY_CREATE_SHOURT_CUT);
            }
            break ;
            
        //以下操作才是真正地处理添加快捷方式的操作    
        case MY_CREATE_SHOURT_CUT:
        	//data数据封装了所有快捷方式应该该附加的值
        	Log.v(TAG, "MY_CREATE_SHOURT_CUT Intent Info--- >" + data);
        	completeAddShortCut(data);
        	break ;
        	
        case MY_REQUEST_ALL_APPLICATION:    //创建一个应用程序的"快捷方式"
        	//data数据封装了点击某个应用程序的intent,包括action,ComponentName组信息等,继而我们可以通过intent对象获取该应用程序的信息
        	Log.v(TAG, "MY_REQUEST_ALL_APPLICATION Intent Info--- >" + data);
        	completeAddApplication(data)  ;
        	break ;
        default :
        	break ;
        	
        }
        
    }
    
    //添加一个应用程序的"快捷方式"
    private void completeAddApplication(Intent data){
    	
    	//打印应用程序返回的intent信息, 一个完整的启动应用程序的Intent
    	Log.i(TAG, "Application intent info ---->" +data) ;
    	
    	ComponentName componentName = data.getComponent() ;
    	Log.i(TAG, "ComponentName Info ---->  " + componentName) ;
    	
        try
        {
        	//获取资源图片
            ActivityInfo activityInfo =  mPackageManager.getActivityInfo(componentName, 0);
            CharSequence applabel = activityInfo.loadLabel(mPackageManager) ;
            Drawable appIcon = activityInfo.loadIcon(mPackageManager) ;
            
            //创建一个View对象
            View view  = makeViewForShortcut(applabel , appIcon) ;
            //为该快捷方式的View设置onClick监听
            view.setOnClickListener(this) ;
            //将该intent对象设置为View的tag属性 , onClick时获取该tag  , --->getTag()
            view.setTag(data) ;
            
            //将该View对象添加至LinearLayout中,由于大小发生了变化,系统会重新走”measure“ , ”layout“, ”draw“ 过程
            //设置长宽高
            LinearLayout.LayoutParams llparams = new LinearLayout.LayoutParams(80,90) ;
            linearlayout.addView(view,llparams) ;
        }
        catch (NameNotFoundException e)
        {
             Log.e(TAG, "NameNotFoundException  at completeAddApplication method") ;
        }        
    }
    
    //添加快捷方式(真正地,非应用程序)
    private void completeAddShortCut(Intent data){
        
        Drawable shortcutIcon = null;  //快捷方式的图标 , 可以有两种方式获取,如下 if else 判断
  	
        // 获得快捷方式Label
        String label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
        
        Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
        // 直接了图片 , 即设置了 EXTRA_SHORTCUT_ICON 参数值
        if (bitmap != null && bitmap instanceof Bitmap)
        {
            shortcutIcon = new BitmapDrawable((Bitmap) bitmap);
        }
        else   //设置了EXTRA_SHORTCUT_ICON_RESOURCE 附加值
        {
        	Parcelable iconParcel = data .getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
            if(iconParcel != null && iconParcel instanceof ShortcutIconResource)
            {
            	// 获得ShortcutIconResource对象
                ShortcutIconResource iconRes = (ShortcutIconResource) iconParcel;
                //获得inconRes对象的Resource对象
                try
                {
                	//获取对应packageName的Resources对象
                    Resources resources = mPackageManager.getResourcesForApplication(iconRes.packageName);                
                    //获取对应图片的id号
                    int iconid = resources.getIdentifier(iconRes.resourceName, null, null);
                    Log.i(TAG, "icon identifier is " + iconRes.resourceName) ;
                    //获取资源图片
                    shortcutIcon = resources.getDrawable(iconid);
                }
                catch (NameNotFoundException e)
                {
                	 Log.e(TAG, "NameNotFoundException  at completeAddShortCut method") ;
                }

            }
        }
        //可能快捷方式没有为我们设置任何图像以及ShortcutIconResource对象,我们需要重置快捷方式的头像
        if ( shortcutIcon == null) {
        	// 一定会有图片,这儿我简单的处理了 .
        	Toast.makeText(MainActivity.this, "sorry , we could not shortcut image", Toast.LENGTH_SHORT) ;
        	return ;
        }

        // 获得快捷方式Intent , 直接startActivity 即可
        Intent shortcut_intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
        
        if (shortcut_intent != null)
            Log.i(TAG, "shortCut intent info  ----> "+shortcut_intent) ;
                
        //创建一个View对象
        View view  = makeViewForShortcut(label , shortcutIcon) ;
        //为该快捷方式的View设置onClick监听
        view.setOnClickListener(this) ;
        //将该intent对象设置为View的tag属性 , onClick时获取该tag  , --->getTag()
        view.setTag(shortcut_intent) ;
        
        //将该View对象添加至LinearLayout中,由于大小发生了变化,系统会重新走”measure“ , ”layout“, ”draw“ 过程
        //设置长宽高
        LinearLayout.LayoutParams llparams = new LinearLayout.LayoutParams(100,90) ;
        
        linearlayout.addView(view,llparams) ;
   
    }

    //点击事件
	@Override
	public void onClick(View v) {
        Object tag = v.getTag() ;
		if(tag !=null && tag instanceof Intent){
			Intent intent = (Intent)tag ;
			startActivityForSafely(intent) ;
		}
		
	}
	//安全启动一个Activity
	private void startActivityForSafely(Intent data) {
		Intent launchIntent = data ;
		
		//有些启动后的Activity需要设置该选项,即为启动的Activity设置一个界面,例如联系人的快捷方式, so , 我们加上它吧
		launchIntent.setSourceBounds(new Rect(0,0,300,300)); 
		launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		
		startActivity(launchIntent) ;
	}
	
	//为每个快捷方式创建一个View对象
	private View makeViewForShortcut(CharSequence label , Drawable icon){
		LayoutInflater inflater = LayoutInflater.from(this) ;
		View shortcut_view = inflater.inflate(R.layout.shortcut_view, null) ;
		
		TextView tv = (TextView)shortcut_view.findViewById(R.id.shortcut_label) ;
		tv.setText(label) ;
		
		ImageView img = (ImageView)shortcut_view.findViewById(R.id.shortcut_img) ;
		img.setImageDrawable(icon) ;
		return shortcut_view ;		
	}
	
	
}



某些快捷方式的功能实现起来还是很炫彩的 , 尤其是启动直接拨打、直接发送短信的Activity,最开始我以为是Launcher实

现地,如今才明白原来是快捷方式提供了一个Intent对象,Launcher直接采用了拿来主义了。 不多说了,大家慢慢感受体味吧。

示例DEMO下载地址: http://download.csdn.net/detail/qinjuning/4008605

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值