相信Android系统经典Launcher大家都见过是什么样子。如下图所示,是4.0比较原始的Launcher主菜单功能,今天我们要学习的就是这一块,通过这个小代码,我们可以复习的知识点有:
①. 应用的获取与处理,包括SD中的应用。
②. 动态监听用户应用安装、卸载以及语言系统的切换,比如中文切换到英文状态。
③. 仿ViewPager和PagerIndicater自定义View的实现,注意是仿哦,不是同一个。
④...
我们都知道,如果直接将系统的源码弄出来,直接导入eclipse是会报错的,下面我们看看从源码中移植出来的效果图,仅是这个模块而已!
下面让我们来看看源码结构,由于仅仅是一个简单的例子,所以细节未过多考虑,敬请谅解:
这个App是整个程序的入口,继承Application,在这里面,以静态变量的形式缓存了所有的应用,还有注册了应用添加、删除、变化等广播,以及相应处理并通知MainActivity,下面,就让我们来看看这个最重要的类,几乎最重要的知识点都在这里面了:
- public class App extends Application {
- public static String TAG = "way";
- private BroadcastReceiver mLauncherReceiver;
- static ArrayList<ApplicationInfo> mApps;
- static final HandlerThread sWorkerThread = new HandlerThread("LoadingApps");
- static {
- sWorkerThread.start();
- }
- static final Handler sWorkerHandler = new Handler(sWorkerThread.getLooper());
- static final Handler sHandler = new Handler();
- private WeakReference<MainActivity> mLauncher;
- @Override
- public void onCreate() {
- super.onCreate();
- registerRecever();// 注册应用添加、删除等广播
- getAllApps();
- }
- /**
- * 注册应用添加、删除等广播
- */
- private void registerRecever() {
- // register recevier
- mLauncherReceiver = new LauncherReceiver();
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addDataScheme("package");
- registerReceiver(mLauncherReceiver, filter);
- filter = new IntentFilter();
- filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
- filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- registerReceiver(mLauncherReceiver, filter);
- filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
- registerReceiver(mLauncherReceiver, filter);
- }
- /**
- * 安全启动Activity,防止未找到应用或权限问题而挂掉
- *
- * @param intent
- * @return
- */
- public boolean startActivitySafely(Intent intent) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- startActivity(intent);
- return true;
- } catch (ActivityNotFoundException e) {
- // Toast.makeText(this, R.string.activity_not_found,
- // Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Unable to launch intent = " + intent, e);
- } catch (SecurityException e) {
- // Toast.makeText(this, R.string.activity_not_found,
- // Toast.LENGTH_SHORT).show();
- Log.e(TAG, "does not have the permission to launch intent = "
- + intent, e);
- } catch (Exception e) {
- Log.e(TAG, "catch Exception ", e);
- }
- return false;
- }
- /**
- * 获取所有应用信息,供外部调用
- *
- * @return
- */
- public ArrayList<ApplicationInfo> getAllApps() {
- if (mApps == null) {
- mApps = new ArrayList<ApplicationInfo>();
- fillAllapps();
- }
- return mApps;
- }
- /**
- * 弱引用管理主界面
- *
- * @param launcher
- */
- public void setLauncher(MainActivity launcher) {
- this.mLauncher = new WeakReference<MainActivity>(launcher);
- }
- /**
- * 搜索所有的app
- */
- private void fillAllapps() {
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- final PackageManager packageManager = getPackageManager();
- List<ResolveInfo> apps = packageManager.queryIntentActivities(
- mainIntent, 0);
- if (mApps != null)
- mApps.clear();
- else
- mApps = new ArrayList<ApplicationInfo>();
- for (ResolveInfo app : apps) {
- mApps.add(new ApplicationInfo(this, app));
- }
- Collections.sort(mApps, APP_NAME_COMPARATOR);// 按应用名排序
- // sortAppsByCustom(mApps);//自定义排序,如果有的话
- }
- /**
- * 重新搜索所有应用 当重新加载的时候调用
- */
- private void refillAllapps() {
- fillAllapps();
- }
- /**
- * 根据包名寻找应用
- *
- * @param packageName
- * 包名
- * @return
- */
- private List<ResolveInfo> findActivitiesForPackage(String packageName) {
- final PackageManager packageManager = getPackageManager();
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- mainIntent.setPackage(packageName);
- final List<ResolveInfo> apps = packageManager.queryIntentActivities(
- mainIntent, 0);
- return apps != null ? apps : new ArrayList<ResolveInfo>();
- }
- /**
- * 添加指定包名的应用,当监听到用户安装新应用的时候调用
- *
- * @param packageName
- */
- private void addPackage(String packageName) {
- final List<ResolveInfo> matches = findActivitiesForPackage(packageName);
- if (matches.size() > 0) {
- for (ResolveInfo info : matches) {
- mApps.add(new ApplicationInfo(this, info));
- }
- }
- }
- /**
- * 移除指定包名的应用,当监听到用户删除应用时调用
- *
- * @param packageName
- */
- private void removePackage(String packageName) {
- for (int i = mApps.size() - 1; i >= 0; i--) {
- ApplicationInfo info = mApps.get(i);
- final ComponentName component = info.intent.getComponent();
- if (packageName.equals(component.getPackageName())) {
- mApps.remove(i);
- }
- }
- }
- /**
- * 此处可以自定义应用排序方式,我未调用该函数
- *
- * @param list
- */
- private void sortAppsByCustom(List<ApplicationInfo> list) {
- int N = list.size();
- // 没有自定义排序表,则按名称排序
- for (int i = 0; i < N; i++) {
- ApplicationInfo app = list.get(i);
- app.index = i;
- }
- Collections.sort(list, APPLICATION_CUST_SORT);
- }
- /**
- * 自定义排序的Comparator
- */
- static final Comparator<ApplicationInfo> APPLICATION_CUST_SORT = new Comparator<ApplicationInfo>() {
- public final int compare(ApplicationInfo a, ApplicationInfo b) {
- return a.index - b.index;
- }
- };
- /**
- * 根据应用名排序的Comparator
- */
- static final Comparator<ApplicationInfo> APP_NAME_COMPARATOR = new Comparator<ApplicationInfo>() {
- public final int compare(ApplicationInfo a, ApplicationInfo b) {
- int result = Collator.getInstance().compare(a.title.toString(),
- b.title.toString());
- if (result == 0) {
- result = a.componentName.compareTo(b.componentName);
- }
- return result;
- }
- };
- /**
- * 处理应用更新的任务
- *包括添加、删除、更新、更改语言等
- *
- */
- private class PackageUpdatedTask implements Runnable {
- public static final int OP_NONE = 0;//未知状态
- public static final int OP_ADD = 1;//添加应用
- public static final int OP_UPDATE = 2;//更新应用
- public static final int OP_REMOVED = 3;//移除应用
- public static final int OP_RELOAD = 4;//重新加载,比如切换语言等
- int mOp;
- String[] mPackages;
- public PackageUpdatedTask(int op, String[] packages) {
- mOp = op;
- mPackages = packages;
- }
- public PackageUpdatedTask(int op) {
- mOp = op;
- }
- @Override
- public void run() {
- if (mOp == OP_RELOAD) {
- refillAllapps();
- } else {
- final String[] packages = mPackages;
- final int N = packages.length;
- switch (mOp) {
- case OP_ADD:
- if (N > 0) {
- sHandler.post(new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < N; i++) {
- Log.d(TAG,
- "PackageUpdatedTask add packageName = "
- + packages[i]);
- addPackage(packages[i]);
- }
- sHandler.post(new Runnable() {
- @Override
- public void run() {
- MainActivity launcher = mLauncher.get();
- if (launcher != null) {
- launcher.bindAllapps();//回调主界面更新
- }
- }
- });
- }
- });
- }
- break;
- case OP_REMOVED:
- if (N > 0) {
- sHandler.post(new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < N; i++) {
- Log.d(TAG,
- "PackageUpdatedTask remove packageName = "
- + packages[i]);
- removePackage(packages[i]);
- }
- sHandler.post(new Runnable() {
- @Override
- public void run() {
- MainActivity launcher = mLauncher.get();
- if (launcher != null) {
- launcher.bindAllapps();//回调主界面更新
- }
- }
- });
- }
- });
- }
- break;
- case OP_UPDATE://更新,这里未作处理
- for (int i = 0; i < N; i++) {
- Log.d(TAG, "PackageUpdatedTask update packageName = "
- + packages[i]);
- }
- break;
- }
- }
- }
- }
- private class LauncherReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- Log.d(TAG, "LauncherReceiver onRecive action = " + action);
- //应用添加、改变、移除的广播
- if (Intent.ACTION_PACKAGE_ADDED.equals(action)
- || Intent.ACTION_PACKAGE_CHANGED.equals(action)
- || Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- final String packageName = intent.getData()
- .getSchemeSpecificPart();
- final boolean replacing = intent.getBooleanExtra(
- Intent.EXTRA_REPLACING, false);
- Log.d(TAG, "LauncherReceiver onRecive packageName = "
- + packageName);
- int op = PackageUpdatedTask.OP_NONE;
- if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
- op = PackageUpdatedTask.OP_UPDATE;
- } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- if (!replacing)
- op = PackageUpdatedTask.OP_REMOVED;
- } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
- if (!replacing)
- op = PackageUpdatedTask.OP_ADD;
- }
- if (op != PackageUpdatedTask.OP_NONE) {
- sWorkerHandler.post(new PackageUpdatedTask(op,
- new String[] { packageName }));
- }
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
- .equals(action)) {//SD卡应用可用的广播,有用户的应用安装到SD卡中
- String[] packages = intent
- .getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- sWorkerHandler.post(new PackageUpdatedTask(
- PackageUpdatedTask.OP_ADD, packages));
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE
- .equals(action)) {//SD卡应用可用的广播
- String[] packages = intent
- .getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- sWorkerHandler.post(new PackageUpdatedTask(
- PackageUpdatedTask.OP_REMOVED, packages));
- } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {//系统切换语言的广播
- sWorkerHandler.post(new PackageUpdatedTask(
- PackageUpdatedTask.OP_RELOAD));
- }
- }
- }
- }
ApplicationInfo其实就是一个JavaBean,但又不全是,他也有一个特殊之处,就是对所有应用的图标做了一下处理,使得显示在我们面前的应用图标不至于参差不齐,或者区别太大,这也算是一个值得学习之处。我们还是来看一下吧,重点是下面那个应用图标处理工具类。
- public class ApplicationInfo {
- public CharSequence title;//应用名
- public Bitmap iconBitmap;//应用图标
- public Intent intent;//应用的Intent
- public ComponentName componentName;//应用包名
- int index;
- private PackageManager mPackageManager;
- private Context mContext;
- public ApplicationInfo(Context context, ResolveInfo info) {
- mContext = context;
- mPackageManager = context.getPackageManager();
- this.componentName = new ComponentName(
- info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name);
- this.title = info.loadLabel(mPackageManager);
- this.iconBitmap = loadIcon(info);
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.setComponent(componentName);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- this.intent = intent;
- }
- private Bitmap loadIcon(ResolveInfo info) {
- Bitmap bitmap = BitmapUtility.createIconBitmap(
- info.activityInfo.loadIcon(mPackageManager), mContext);
- return bitmap;
- }
- }
- /**
- * 这是一个应用图标处理的工具类
- * @author way
- *
- */
- final class BitmapUtility {
- private static int sIconWidth = -1;
- private static int sIconHeight = -1;
- private static int sIconTextureWidth = -1;
- private static int sIconTextureHeight = -1;
- private static final Rect sOldBounds = new Rect();
- private static final Canvas sCanvas = new Canvas();
- static {
- sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
- Paint.FILTER_BITMAP_FLAG));
- }
- private static void initStatics(Context context) {
- final Resources resources = context.getResources();
- sIconWidth = sIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
- sIconTextureWidth = sIconTextureHeight = sIconWidth;
- }
- static Bitmap createIconBitmap(Drawable icon, Context context) {
- synchronized (sCanvas) { // we share the statics :-(
- if (sIconWidth == -1) {
- initStatics(context);
- }
- int width = sIconWidth;
- int height = sIconHeight;
- if (icon instanceof PaintDrawable) {
- PaintDrawable painter = (PaintDrawable) icon;
- painter.setIntrinsicWidth(width);
- painter.setIntrinsicHeight(height);
- } else if (icon instanceof BitmapDrawable) {
- // Ensure the bitmap has a density.
- BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
- Bitmap bitmap = bitmapDrawable.getBitmap();
- if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
- bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
- }
- }
- int sourceWidth = icon.getIntrinsicWidth();
- int sourceHeight = icon.getIntrinsicHeight();
- if (sourceWidth > 0 && sourceHeight > 0) {
- // There are intrinsic sizes.
- if (width < sourceWidth || height < sourceHeight) {
- // It's too big, scale it down.
- final float ratio = (float) sourceWidth / sourceHeight;
- if (sourceWidth > sourceHeight) {
- height = (int) (width / ratio);
- } else if (sourceHeight > sourceWidth) {
- width = (int) (height * ratio);
- }
- } else if (sourceWidth < width && sourceHeight < height) {
- // Don't scale up the icon
- width = sourceWidth;
- height = sourceHeight;
- }
- }
- // no intrinsic size --> use default size
- int textureWidth = sIconTextureWidth;
- int textureHeight = sIconTextureHeight;
- final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
- Bitmap.Config.ARGB_8888);
- final Canvas canvas = sCanvas;
- canvas.setBitmap(bitmap);
- final int left = (textureWidth-width) / 2;
- final int top = (textureHeight-height) / 2;
- sOldBounds.set(icon.getBounds());
- icon.setBounds(left, top, left+width, top+height);
- icon.draw(canvas);
- icon.setBounds(sOldBounds);
- return bitmap;
- }
- }
- }
应用全部加装完毕,剩下就是在主界面中显示了,自然而然到了MainActivity:
- public class MainActivity extends Activity {
- private App mSceneLauncherApplication;
- private SceneAllAppsPagedView mSceneAllApps;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.scene_allapps);
- mSceneLauncherApplication = (App) getApplication();
- mSceneLauncherApplication.setLauncher(this);
- initView();
- bindAllapps();
- }
- /**
- * 绑定所有应用
- */
- public void bindAllapps() {
- mSceneAllApps.setApps(mSceneLauncherApplication.getAllApps());
- }
- /**
- * 初始化view
- */
- private void initView() {
- mSceneAllApps = (SceneAllAppsPagedView)findViewById(R.id.scene_allapps);
- }
- /**
- * 处理应用点击的回调
- * @param v
- */
- public void onAppsItemClick(View v) {
- final ApplicationInfo info = (ApplicationInfo) v.getTag();
- if (info != null) {
- mSceneLauncherApplication.startActivitySafely(info.intent);
- }
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.main, menu);
- return true;
- }
- }
相信如果只是显示一些应用,这个很简单,很多人都可以实现,但是像系统应用,他会考虑很多细节问题,比如缓存优化、线程安全、应用图标优化、图标点击效果等等,都是我们值得学习的地方,其他的类就是一些自定义View了,有需要的童鞋可以下载源码看看:http://download.csdn.net/detail/weidi1989/5927283