ListView-4,获取手机中所有应用信息,listView复用,为listView插入不同的item,根据包名开启该应用
功能:获取安装在手机里的所有应用.
注意点:复用listView Item对象时的判断
软件信息封装:AppInfo.java
/** * 封装应用程序的信息 */ public class AppInfo { /** * 应用图标 */ private Drawable icon; /** * 应用名称 */ private String name; /** * 应用包名 */ private String packname; /** * 判断应用安装的位置 * true代表安装在手机内存 * false代表安装在sd卡 */ private boolean inRom; /** * 判断是系统应用还是用户应用 * true代表用户 * false代表系统 */ private boolean userApp; public Drawable getIcon() { return icon; } public void setIcon(Drawable icon) { this.icon = icon; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPackname() { return packname; } public void setPackname(String packname) { this.packname = packname; } public boolean isInRom() { return inRom; } public void setInRom(boolean inRom) { this.inRom = inRom; } public boolean isUserApp() { return userApp; } public void setUserApp(boolean userApp) { this.userApp = userApp; } }
获取所有应用信息: AppInfoProvider.java
/** * 从该类获取所有的应用信息 * 系统的app都安装在:system/app目录下 * 用户应用都安装在:data/app目录下 */ public class AppInfoProvider { /** * 获取安装在该手机的所有app信息,并且分好类(系统程序,用户程序) * 信息:包名,图片,应用名,应用安装位置,应用类型 * @param context * @return */ public static HashMap<String,ArrayList<AppInfo>> getAppInfos(Context context){ //获取应用管理者 PackageManager pm = context.getPackageManager(); //所有安装在手机上的应用程序包信息 List<PackageInfo> packInfos = pm.getInstalledPackages(0); HashMap<String,ArrayList<AppInfo>> map=new HashMap<String, ArrayList<AppInfo>>();//放所有app信息 ArrayList<AppInfo> userList=new ArrayList<AppInfo>();//放用户程序 ArrayList<AppInfo> systemList=new ArrayList<AppInfo>();//放系统程序 for (PackageInfo packInfo : packInfos) { //packageInfo 相当于一个应用程序apk包的清单文件 String packname=packInfo.packageName;//获取应用包名 //packInfo.applicationInfo相当于清单文件中的application Drawable icon = packInfo.applicationInfo.loadIcon(pm);//获取应用图标 String name = packInfo.applicationInfo.loadLabel(pm).toString();//应用名称 //将得到的信息设置到自定义应用的封装类中 AppInfo app=new AppInfo(); app.setIcon(icon); app.setName(name); app.setPackname(packname); //flags携带了很多种状态信息 int flags = packInfo.applicationInfo.flags;//应用程序信息的标记 if((flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) ==0){ app.setInRom(true);//安装在手机内存中 }else{ app.setInRom(false);//安装在外部存储 System.out.println("外部"); } if((flags & ApplicationInfo.FLAG_SYSTEM)==0){ app.setUserApp(true);//用户程序 userList.add(app); }else{ app.setUserApp(false);//系统程序 systemList.add(app); } } map.put("用户程序", userList); map.put("系统程序", systemList); return map; } }
通过(上面for循环的):int appId=packInfo.applicationInfo.uid;就可以获取安卓系统分配给这个软件的id,每个软件都有自己独立的id,通过这个id可以查看该软件使用的流量信息,具体见:手机流量的统计
显示应用信息列表的适配器ListViewAdapter.java
/** 先显示用户程序,再显示系统 TextView 用户程序3个 (相对布局中的) ursr 1 position 0 listView的子条目 ursr 2 1 ursr 3 2 系统程序:2个 3 system 1 4 system 2 5 注意:listView复用对象的时候需要判断是否是合适的类型,否则会出现空指针异常 */ public class ListViewAdapter extends BaseAdapter{ private Context context; private ArrayList<AppInfo> userList;//存放用户应用 private ArrayList<AppInfo> systemList;//存放系统应用 public ListViewAdapter(Context context, HashMap<String, ArrayList<AppInfo>> map) { this.context = context; userList=map.get("用户程序"); systemList=map.get("系统程序"); } @Override public int getCount() { return userList.size()+systemList.size()+1;//加1是用来显示系统程序的条例 } @Override public Object getItem(int position) { if(position>userList.size()){ return systemList.get(position-userList.size()-1);//减去前面的系统个数条例 } return userList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { //显示系统个数的条目 if(position==userList.size()){ TextView tv=new TextView(context); tv.setText("系统应用有:"+systemList.size()+"个"); tv.setTextSize(20); tv.setTextColor(Color.RED); tv.setBackgroundColor(Color.GRAY);//灰色背景 return tv;//其实这里需要返回的是布局文件,而不是控件,就和向listView添加头一样,只能是R.layout找到的View }//但是对于TextView可以直接返回,如果这里返回一个ImageView或者其他的控件,肯定就报什么布局的类型转换异常了 ViewHolder holder; //解决复用时,将textview复用给LineatLayout而产生空指针异常 if(convertView!=null && convertView instanceof LinearLayout){ holder=(ViewHolder) convertView.getTag(); }else{ convertView=View.inflate(context, R.layout.appinfo_item_layout, null); holder=new ViewHolder(); holder.img=(ImageView) convertView.findViewById(R.id.appinfo_img); holder.name=(TextView) convertView.findViewById(R.id.app_name); holder.location=(TextView) convertView.findViewById(R.id.app_loction); convertView.setTag(holder); } //显示用户程序条目 if(position<userList.size()){ AppInfo appInfo = userList.get(position); holder.img.setImageDrawable(appInfo.getIcon()); holder.name.setText(appInfo.getName()); if(appInfo.isInRom()){ holder.location.setText("内部存储"); }else{ holder.location.setText("外部存储"); } return convertView; }else{ AppInfo appInfo = systemList.get(position-userList.size()-1); holder.img.setImageDrawable(appInfo.getIcon()); holder.name.setText(appInfo.getName()); if(appInfo.isInRom()){ holder.location.setText("内部存储"); }else{ holder.location.setText("外部存储"); } return convertView; } } class ViewHolder{ private ImageView img; private TextView name,location; } }
MainActivity.java
主活动布局文件:activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- 显示设备存储空间的内存大小 --> <TextView android:id="@+id/storageInfo" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="25sp" /> <RelativeLayout android:id="@+id/frame_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 做为应用程序类型的提示 和listview中的textview设置成一样,产生视觉效果 fastScrollEnabled为快速滑动--> <TextView android:id="@+id/listview_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" android:background="#888888" android:textColor="#ff0000" /> <ListView android:id="@+id/listView" android:fastScrollEnabled="true" android:dividerHeight="2dp" android:layout_below="@id/listview_text" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout> </LinearLayout>
ListView的item布局文件 appinfo_item_layout.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <ImageView android:id="@+id/appinfo_img" android:src="@drawable/ic_launcher" android:layout_width="70dp" android:layout_height="70dp"/> <RelativeLayout android:layout_width="match_parent" android:layout_marginLeft="7dp" android:layout_height="70dp"> <TextView android:id="@+id/app_name" android:text="应用名称:" android:textSize="30sp" android:textColor="#000000" android:singleLine="true" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:id="@+id/app_loction" android:text="安装位置:" android:textSize="25sp" android:textColor="#99000000" android:singleLine="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true"/> </RelativeLayout> </LinearLayout>
listView的单击事件弹出窗体可见:PopupWindow弹出窗口的使用(点击item弹出一个窗体,窗体包含一个布局,布局中为三个图片按钮,分别为卸载,开启,分享),一般的软件管理都是这样的,我们也可以仿照实现
通过程序的包名,启动这个程序: (上面有介绍获取程序包名)
//单击事件 public void btnOnClick(View v){ PackageManager manager = getPackageManager(); Intent intent=new Intent(); intent.setAction("android.intent.action.MAIN"); intent.addCategory("android.intent.category.LAUNCHER"); //查询出手机上所有具有启动能力的activity List<ResolveInfo> infos = manager.queryIntentActivities(intent, PackageManager.GET_META_DATA); for (ResolveInfo info : infos) { Log.i("tag","主activity的全包名:"+info.activityInfo.name); //获取包名 String packageName = info.activityInfo.packageName; try { //通过包名获取应用名称与图标 String appName= manager.getPackageInfo(packageName, 0).applicationInfo.loadLabel(manager).toString(); Drawable icon = manager.getPackageInfo(packageName, 0).applicationInfo.loadIcon(manager); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } //通过包名,开启这个应用 Intent intent2 = manager.getLaunchIntentForPackage("包名"); if(intent2!=null){ startActivity(intent2); }else{ Toast.makeText(this,"不能启动该应用",Toast.LENGTH_LONG).show(); } }
上面在代码中已经说明了BastAdapter的getView方法必须是返回一个布局文件View,但是返回TextView不会报错,返回其他控件就会报错了,那么当listView显示比较复杂的时候,还是使用之前的方法就不太合适了,要是数据还是动态的就更麻烦了,并且当你两个View的布局都是线性布局的时候,convertView instanceof LinearLayout就不能使用了,或许你会使用相对布局,这样确实可以,但是还是不太好吧
所以改进方法如下:
public class MyAdapter extends BaseAdapter { @Override public int getCount() { return 0; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } /** * 确定当前adapter中item的view的种类的数量 */ @Override public int getViewTypeCount() { return 2;//默认返回的1,所以只能显示一种布局 } /** * 根据position来获得当前的要显示的item的view类型 */ @Override public int getItemViewType(int position) { int type = Integer.parseInt(dataList.get(position).getType());//获取你数据中给的类型 return type;//getView也就是根据获取这个值来确定要显示哪个布局了 } @Override public View getView(int position, View convertView, ViewGroup parent) { int itemViewType = getItemViewType(position);//先获取类型 ViewHolder1 viewHolder1; ViewHolder2 viewHolder2; switch (itemViewType) { case 0://第一种布局,比如显示图片加文本 //这里就跟基本的使用差不多了 break; case 1://第二种布局,比如只显示一个图片 break; default: } return convertView; } class ViewHolder1 { public ImageView imageView; public TextView textView; } class ViewHolder2 { public ImageView img; } }