Android之经典Launcher主菜单模块学习

相信Android系统经典Launcher大家都见过是什么样子。如下图所示,是4.0比较原始的Launcher主菜单功能,今天我们要学习的就是这一块,通过这个小代码,我们可以复习的知识点有:

①. 应用的获取与处理,包括SD中的应用。

②. 动态监听用户应用安装、卸载以及语言系统的切换,比如中文切换到英文状态。

③. 仿ViewPager和PagerIndicater自定义View的实现,注意是仿哦,不是同一个。

④...

 

 

我们都知道,如果直接将系统的源码弄出来,直接导入eclipse是会报错的,下面我们看看从源码中移植出来的效果图,仅是这个模块而已!

  

 

下面让我们来看看源码结构,由于仅仅是一个简单的例子,所以细节未过多考虑,敬请谅解:

 

这个App是整个程序的入口,继承Application,在这里面,以静态变量的形式缓存了所有的应用,还有注册了应用添加、删除、变化等广播,以及相应处理并通知MainActivity,下面,就让我们来看看这个最重要的类,几乎最重要的知识点都在这里面了:

[java]  view plain copy
  1. public class App extends Application {  
  2.     public static String TAG = "way";  
  3.     private BroadcastReceiver mLauncherReceiver;  
  4.     static ArrayList<ApplicationInfo> mApps;  
  5.     static final HandlerThread sWorkerThread = new HandlerThread("LoadingApps");  
  6.     static {  
  7.         sWorkerThread.start();  
  8.     }  
  9.     static final Handler sWorkerHandler = new Handler(sWorkerThread.getLooper());  
  10.     static final Handler sHandler = new Handler();  
  11.     private WeakReference<MainActivity> mLauncher;  
  12.   
  13.     @Override  
  14.     public void onCreate() {  
  15.         super.onCreate();  
  16.         registerRecever();// 注册应用添加、删除等广播  
  17.         getAllApps();  
  18.     }  
  19.   
  20.     /** 
  21.      * 注册应用添加、删除等广播 
  22.      */  
  23.     private void registerRecever() {  
  24.         // register recevier  
  25.         mLauncherReceiver = new LauncherReceiver();  
  26.         IntentFilter filter = new IntentFilter();  
  27.         filter.addAction(Intent.ACTION_PACKAGE_ADDED);  
  28.         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);  
  29.         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);  
  30.         filter.addDataScheme("package");  
  31.         registerReceiver(mLauncherReceiver, filter);  
  32.         filter = new IntentFilter();  
  33.         filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);  
  34.         filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);  
  35.         registerReceiver(mLauncherReceiver, filter);  
  36.         filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);  
  37.         registerReceiver(mLauncherReceiver, filter);  
  38.     }  
  39.   
  40.     /** 
  41.      * 安全启动Activity,防止未找到应用或权限问题而挂掉 
  42.      *  
  43.      * @param intent 
  44.      * @return 
  45.      */  
  46.     public boolean startActivitySafely(Intent intent) {  
  47.         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  48.         try {  
  49.             startActivity(intent);  
  50.             return true;  
  51.         } catch (ActivityNotFoundException e) {  
  52.             // Toast.makeText(this, R.string.activity_not_found,  
  53.             // Toast.LENGTH_SHORT).show();  
  54.             Log.e(TAG, "Unable to launch intent = " + intent, e);  
  55.         } catch (SecurityException e) {  
  56.             // Toast.makeText(this, R.string.activity_not_found,  
  57.             // Toast.LENGTH_SHORT).show();  
  58.             Log.e(TAG, "does not have the permission to launch intent = "  
  59.                     + intent, e);  
  60.         } catch (Exception e) {  
  61.             Log.e(TAG, "catch Exception ", e);  
  62.         }  
  63.         return false;  
  64.     }  
  65.   
  66.     /** 
  67.      * 获取所有应用信息,供外部调用 
  68.      *  
  69.      * @return 
  70.      */  
  71.     public ArrayList<ApplicationInfo> getAllApps() {  
  72.         if (mApps == null) {  
  73.             mApps = new ArrayList<ApplicationInfo>();  
  74.             fillAllapps();  
  75.         }  
  76.         return mApps;  
  77.     }  
  78.   
  79.     /** 
  80.      * 弱引用管理主界面 
  81.      *  
  82.      * @param launcher 
  83.      */  
  84.     public void setLauncher(MainActivity launcher) {  
  85.         this.mLauncher = new WeakReference<MainActivity>(launcher);  
  86.     }  
  87.   
  88.     /** 
  89.      * 搜索所有的app 
  90.      */  
  91.     private void fillAllapps() {  
  92.         final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);  
  93.         mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  
  94.   
  95.         final PackageManager packageManager = getPackageManager();  
  96.         List<ResolveInfo> apps = packageManager.queryIntentActivities(  
  97.                 mainIntent, 0);  
  98.         if (mApps != null)  
  99.             mApps.clear();  
  100.         else  
  101.             mApps = new ArrayList<ApplicationInfo>();  
  102.         for (ResolveInfo app : apps) {  
  103.             mApps.add(new ApplicationInfo(this, app));  
  104.         }  
  105.         Collections.sort(mApps, APP_NAME_COMPARATOR);// 按应用名排序  
  106.         // sortAppsByCustom(mApps);//自定义排序,如果有的话  
  107.     }  
  108.   
  109.     /** 
  110.      * 重新搜索所有应用 当重新加载的时候调用 
  111.      */  
  112.     private void refillAllapps() {  
  113.         fillAllapps();  
  114.     }  
  115.   
  116.     /** 
  117.      * 根据包名寻找应用 
  118.      *  
  119.      * @param packageName 
  120.      *            包名 
  121.      * @return 
  122.      */  
  123.     private List<ResolveInfo> findActivitiesForPackage(String packageName) {  
  124.         final PackageManager packageManager = getPackageManager();  
  125.         final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);  
  126.         mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  
  127.         mainIntent.setPackage(packageName);  
  128.   
  129.         final List<ResolveInfo> apps = packageManager.queryIntentActivities(  
  130.                 mainIntent, 0);  
  131.         return apps != null ? apps : new ArrayList<ResolveInfo>();  
  132.     }  
  133.   
  134.     /** 
  135.      * 添加指定包名的应用,当监听到用户安装新应用的时候调用 
  136.      *  
  137.      * @param packageName 
  138.      */  
  139.     private void addPackage(String packageName) {  
  140.         final List<ResolveInfo> matches = findActivitiesForPackage(packageName);  
  141.   
  142.         if (matches.size() > 0) {  
  143.             for (ResolveInfo info : matches) {  
  144.                 mApps.add(new ApplicationInfo(this, info));  
  145.             }  
  146.         }  
  147.     }  
  148.   
  149.     /** 
  150.      * 移除指定包名的应用,当监听到用户删除应用时调用 
  151.      *  
  152.      * @param packageName 
  153.      */  
  154.     private void removePackage(String packageName) {  
  155.         for (int i = mApps.size() - 1; i >= 0; i--) {  
  156.             ApplicationInfo info = mApps.get(i);  
  157.             final ComponentName component = info.intent.getComponent();  
  158.             if (packageName.equals(component.getPackageName())) {  
  159.                 mApps.remove(i);  
  160.             }  
  161.         }  
  162.     }  
  163.   
  164.     /** 
  165.      * 此处可以自定义应用排序方式,我未调用该函数 
  166.      *  
  167.      * @param list 
  168.      */  
  169.     private void sortAppsByCustom(List<ApplicationInfo> list) {  
  170.         int N = list.size();  
  171.   
  172.         // 没有自定义排序表,则按名称排序  
  173.         for (int i = 0; i < N; i++) {  
  174.             ApplicationInfo app = list.get(i);  
  175.             app.index = i;  
  176.         }  
  177.         Collections.sort(list, APPLICATION_CUST_SORT);  
  178.     }  
  179.   
  180.     /** 
  181.      * 自定义排序的Comparator 
  182.      */  
  183.     static final Comparator<ApplicationInfo> APPLICATION_CUST_SORT = new Comparator<ApplicationInfo>() {  
  184.         public final int compare(ApplicationInfo a, ApplicationInfo b) {  
  185.             return a.index - b.index;  
  186.         }  
  187.     };  
  188.   
  189.     /** 
  190.      * 根据应用名排序的Comparator 
  191.      */  
  192.     static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR = new Comparator<ApplicationInfo>() {  
  193.         public final int compare(ApplicationInfo a, ApplicationInfo b) {  
  194.             int result = Collator.getInstance().compare(a.title.toString(),  
  195.                     b.title.toString());  
  196.             if (result == 0) {  
  197.                 result = a.componentName.compareTo(b.componentName);  
  198.             }  
  199.             return result;  
  200.         }  
  201.     };  
  202.   
  203.     /** 
  204.      * 处理应用更新的任务 
  205.      *包括添加、删除、更新、更改语言等  
  206.      *  
  207.      */  
  208.     private class PackageUpdatedTask implements Runnable {  
  209.         public static final int OP_NONE = 0;//未知状态  
  210.         public static final int OP_ADD = 1;//添加应用  
  211.         public static final int OP_UPDATE = 2;//更新应用  
  212.         public static final int OP_REMOVED = 3;//移除应用  
  213.         public static final int OP_RELOAD = 4;//重新加载,比如切换语言等  
  214.   
  215.         int mOp;  
  216.         String[] mPackages;  
  217.   
  218.         public PackageUpdatedTask(int op, String[] packages) {  
  219.             mOp = op;  
  220.             mPackages = packages;  
  221.         }  
  222.   
  223.         public PackageUpdatedTask(int op) {  
  224.             mOp = op;  
  225.         }  
  226.   
  227.         @Override  
  228.         public void run() {  
  229.             if (mOp == OP_RELOAD) {  
  230.                 refillAllapps();  
  231.             } else {  
  232.                 final String[] packages = mPackages;  
  233.                 final int N = packages.length;  
  234.                 switch (mOp) {  
  235.                 case OP_ADD:  
  236.                     if (N > 0) {  
  237.                         sHandler.post(new Runnable() {  
  238.                             @Override  
  239.                             public void run() {  
  240.                                 for (int i = 0; i < N; i++) {  
  241.                                     Log.d(TAG,  
  242.                                             "PackageUpdatedTask add packageName = "  
  243.                                                     + packages[i]);  
  244.                                     addPackage(packages[i]);  
  245.                                 }  
  246.                                 sHandler.post(new Runnable() {  
  247.   
  248.                                     @Override  
  249.                                     public void run() {  
  250.                                         MainActivity launcher = mLauncher.get();  
  251.                                         if (launcher != null) {  
  252.                                             launcher.bindAllapps();//回调主界面更新  
  253.                                         }  
  254.                                     }  
  255.                                 });  
  256.                             }  
  257.                         });  
  258.                     }  
  259.                     break;  
  260.   
  261.                 case OP_REMOVED:  
  262.                     if (N > 0) {  
  263.                         sHandler.post(new Runnable() {  
  264.   
  265.                             @Override  
  266.                             public void run() {  
  267.                                 for (int i = 0; i < N; i++) {  
  268.                                     Log.d(TAG,  
  269.                                             "PackageUpdatedTask remove packageName = "  
  270.                                                     + packages[i]);  
  271.                                     removePackage(packages[i]);  
  272.                                 }  
  273.                                 sHandler.post(new Runnable() {  
  274.   
  275.                                     @Override  
  276.                                     public void run() {  
  277.                                         MainActivity launcher = mLauncher.get();  
  278.                                         if (launcher != null) {  
  279.                                             launcher.bindAllapps();//回调主界面更新  
  280.                                         }  
  281.                                     }  
  282.                                 });  
  283.                             }  
  284.                         });  
  285.                     }  
  286.                     break;  
  287.   
  288.                 case OP_UPDATE://更新,这里未作处理  
  289.                     for (int i = 0; i < N; i++) {  
  290.                         Log.d(TAG, "PackageUpdatedTask update packageName = "  
  291.                                 + packages[i]);  
  292.                     }  
  293.                     break;  
  294.                 }  
  295.             }  
  296.         }  
  297.   
  298.     }  
  299.   
  300.     private class LauncherReceiver extends BroadcastReceiver {  
  301.         @Override  
  302.         public void onReceive(Context context, Intent intent) {  
  303.             final String action = intent.getAction();  
  304.             Log.d(TAG, "LauncherReceiver onRecive action = " + action);  
  305.             //应用添加、改变、移除的广播  
  306.             if (Intent.ACTION_PACKAGE_ADDED.equals(action)  
  307.                     || Intent.ACTION_PACKAGE_CHANGED.equals(action)  
  308.                     || Intent.ACTION_PACKAGE_REMOVED.equals(action)) {  
  309.                 final String packageName = intent.getData()  
  310.                         .getSchemeSpecificPart();  
  311.                 final boolean replacing = intent.getBooleanExtra(  
  312.                         Intent.EXTRA_REPLACING, false);  
  313.                 Log.d(TAG, "LauncherReceiver onRecive packageName = "  
  314.                         + packageName);  
  315.                 int op = PackageUpdatedTask.OP_NONE;  
  316.                 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {  
  317.                     op = PackageUpdatedTask.OP_UPDATE;  
  318.                 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {  
  319.                     if (!replacing)  
  320.                         op = PackageUpdatedTask.OP_REMOVED;  
  321.                 } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {  
  322.                     if (!replacing)  
  323.                         op = PackageUpdatedTask.OP_ADD;  
  324.                 }  
  325.                 if (op != PackageUpdatedTask.OP_NONE) {  
  326.                     sWorkerHandler.post(new PackageUpdatedTask(op,  
  327.                             new String[] { packageName }));  
  328.                 }  
  329.   
  330.             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE  
  331.                     .equals(action)) {//SD卡应用可用的广播,有用户的应用安装到SD卡中  
  332.                 String[] packages = intent  
  333.                         .getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);  
  334.                 sWorkerHandler.post(new PackageUpdatedTask(  
  335.                         PackageUpdatedTask.OP_ADD, packages));  
  336.             } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE  
  337.                     .equals(action)) {//SD卡应用可用的广播  
  338.                 String[] packages = intent  
  339.                         .getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);  
  340.                 sWorkerHandler.post(new PackageUpdatedTask(  
  341.                         PackageUpdatedTask.OP_REMOVED, packages));  
  342.             } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {//系统切换语言的广播  
  343.                 sWorkerHandler.post(new PackageUpdatedTask(  
  344.                         PackageUpdatedTask.OP_RELOAD));  
  345.             }  
  346.         }  
  347.   
  348.     }  
  349. }  


ApplicationInfo其实就是一个JavaBean,但又不全是,他也有一个特殊之处,就是对所有应用的图标做了一下处理,使得显示在我们面前的应用图标不至于参差不齐,或者区别太大,这也算是一个值得学习之处。我们还是来看一下吧,重点是下面那个应用图标处理工具类。

[java]  view plain copy
  1. public class ApplicationInfo {  
  2.     public CharSequence title;//应用名  
  3.     public Bitmap iconBitmap;//应用图标  
  4.     public Intent intent;//应用的Intent  
  5.     public ComponentName componentName;//应用包名  
  6.       
  7.     int index;    
  8.       
  9.     private PackageManager mPackageManager;  
  10.     private Context mContext;  
  11.       
  12.     public ApplicationInfo(Context context, ResolveInfo info) {  
  13.         mContext = context;  
  14.         mPackageManager = context.getPackageManager();  
  15.         this.componentName = new ComponentName(  
  16.                 info.activityInfo.applicationInfo.packageName,  
  17.                 info.activityInfo.name);  
  18.         this.title = info.loadLabel(mPackageManager);  
  19.         this.iconBitmap = loadIcon(info);  
  20.         Intent intent = new Intent(Intent.ACTION_MAIN);  
  21.         intent.addCategory(Intent.CATEGORY_LAUNCHER);  
  22.         intent.setComponent(componentName);  
  23.         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);  
  24.         this.intent = intent;  
  25.     }  
  26.       
  27.     private Bitmap loadIcon(ResolveInfo info) {  
  28.         Bitmap bitmap = BitmapUtility.createIconBitmap(  
  29.                 info.activityInfo.loadIcon(mPackageManager), mContext);  
  30.           
  31.         return bitmap;  
  32.     }  
  33. }  
  34. /** 
  35.  * 这是一个应用图标处理的工具类 
  36.  * @author way 
  37.  * 
  38.  */  
  39. final class BitmapUtility {  
  40.     private static int sIconWidth = -1;  
  41.     private static int sIconHeight = -1;  
  42.     private static int sIconTextureWidth = -1;  
  43.     private static int sIconTextureHeight = -1;  
  44.       
  45.     private static final Rect sOldBounds = new Rect();  
  46.     private static final Canvas sCanvas = new Canvas();  
  47.     static {  
  48.         sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,  
  49.                 Paint.FILTER_BITMAP_FLAG));  
  50.     }  
  51.       
  52.     private static void initStatics(Context context) {  
  53.         final Resources resources = context.getResources();  
  54.   
  55.         sIconWidth = sIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);  
  56.         sIconTextureWidth = sIconTextureHeight = sIconWidth;  
  57.     }      
  58.       
  59.     static Bitmap createIconBitmap(Drawable icon, Context context) {  
  60.         synchronized (sCanvas) { // we share the statics :-(  
  61.             if (sIconWidth == -1) {  
  62.                 initStatics(context);  
  63.             }  
  64.   
  65.             int width = sIconWidth;  
  66.             int height = sIconHeight;  
  67.   
  68.             if (icon instanceof PaintDrawable) {  
  69.                 PaintDrawable painter = (PaintDrawable) icon;  
  70.                 painter.setIntrinsicWidth(width);  
  71.                 painter.setIntrinsicHeight(height);  
  72.             } else if (icon instanceof BitmapDrawable) {  
  73.                 // Ensure the bitmap has a density.  
  74.                 BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;  
  75.                 Bitmap bitmap = bitmapDrawable.getBitmap();  
  76.                 if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {  
  77.                     bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());  
  78.                 }  
  79.             }  
  80.             int sourceWidth = icon.getIntrinsicWidth();  
  81.             int sourceHeight = icon.getIntrinsicHeight();  
  82.   
  83.             if (sourceWidth > 0 && sourceHeight > 0) {  
  84.                 // There are intrinsic sizes.  
  85.                 if (width < sourceWidth || height < sourceHeight) {  
  86.                     // It's too big, scale it down.  
  87.                     final float ratio = (float) sourceWidth / sourceHeight;  
  88.                     if (sourceWidth > sourceHeight) {  
  89.                         height = (int) (width / ratio);  
  90.                     } else if (sourceHeight > sourceWidth) {  
  91.                         width = (int) (height * ratio);  
  92.                     }  
  93.                 } else if (sourceWidth < width && sourceHeight < height) {  
  94.                     // Don't scale up the icon  
  95.                     width = sourceWidth;  
  96.                     height = sourceHeight;  
  97.                 }  
  98.             }  
  99.   
  100.             // no intrinsic size --> use default size  
  101.             int textureWidth = sIconTextureWidth;  
  102.             int textureHeight = sIconTextureHeight;  
  103.   
  104.             final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,  
  105.                     Bitmap.Config.ARGB_8888);  
  106.             final Canvas canvas = sCanvas;  
  107.             canvas.setBitmap(bitmap);  
  108.   
  109.             final int left = (textureWidth-width) / 2;  
  110.             final int top = (textureHeight-height) / 2;  
  111.   
  112.             sOldBounds.set(icon.getBounds());  
  113.             icon.setBounds(left, top, left+width, top+height);  
  114.             icon.draw(canvas);  
  115.             icon.setBounds(sOldBounds);  
  116.   
  117.             return bitmap;  
  118.         }  
  119.     }  
  120.       
  121. }  


应用全部加装完毕,剩下就是在主界面中显示了,自然而然到了MainActivity:

[java]  view plain copy
  1. public class MainActivity extends Activity {  
  2.     private App mSceneLauncherApplication;  
  3.     private SceneAllAppsPagedView mSceneAllApps;  
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.scene_allapps);  
  8.         mSceneLauncherApplication = (App) getApplication();  
  9.         mSceneLauncherApplication.setLauncher(this);  
  10.         initView();  
  11.         bindAllapps();  
  12.     }  
  13.   
  14.     /** 
  15.      * 绑定所有应用 
  16.      */  
  17.     public void bindAllapps() {  
  18.         mSceneAllApps.setApps(mSceneLauncherApplication.getAllApps());  
  19.     }  
  20.     /** 
  21.      * 初始化view 
  22.      */  
  23.     private void initView() {  
  24.         mSceneAllApps = (SceneAllAppsPagedView)findViewById(R.id.scene_allapps);  
  25.     }  
  26.   
  27.     /** 
  28.      * 处理应用点击的回调 
  29.      * @param v 
  30.      */  
  31.     public void onAppsItemClick(View v) {  
  32.         final ApplicationInfo info = (ApplicationInfo) v.getTag();  
  33.         if (info != null) {  
  34.             mSceneLauncherApplication.startActivitySafely(info.intent);  
  35.         }  
  36.     }  
  37.   
  38.     @Override  
  39.     public boolean onCreateOptionsMenu(Menu menu) {  
  40.         // Inflate the menu; this adds items to the action bar if it is present.  
  41.         getMenuInflater().inflate(R.menu.main, menu);  
  42.         return true;  
  43.     }  
  44.   
  45. }  


相信如果只是显示一些应用,这个很简单,很多人都可以实现,但是像系统应用,他会考虑很多细节问题,比如缓存优化、线程安全、应用图标优化、图标点击效果等等,都是我们值得学习的地方,其他的类就是一些自定义View了,有需要的童鞋可以下载源码看看:http://download.csdn.net/detail/weidi1989/5927283

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值