本帖最后由 小鲁哥哥 于 2017-4-12 14:28 编辑
Android课程同步笔记day08:Android应用之安全卫士
软件管家
软件管家模块用于获取当前手机中的所有应用,包括用户程序和系统程序,以及手机剩余内存、SD卡剩余内存,当点击某个应用时,下方会浮出一个操作条,可以管理程序的启动、卸载、分享,该功能界面效果如下所示: 首先,我们分析软件管家界面效果图中进度条部分, 该部分是用于显示手机内存信息和SD 卡内存信息,每一部分都包含三个TextView 和一个ProgressBar,如果将这些控件的代码都写在主界面的布局文件则会显得代码过于繁多,不利于以后的代码维护。 为解决这个问题,我们可以把这些控件组合在一起,定义一个具有水平进度条和三个TextView 的组合控件ProgressStateView,这样在书写主界面布局文件时也会变得更加简便。 关于自定义组合控件ProgressStateView的布局文件view_progress_state的代码如下所示: [XML] 纯文本查看 复制代码 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/tv_title"
style="@style/textNormal"
android:layout_width="50dp"
android:layout_centerVertical="true"
android:text="内存:"
android:textColor="#ee000000"
android:textSize="14sp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/tv_title" >
<ProgressBar
android:id="@+id/pb_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:progressDrawable="@drawable/progress_horizontal" />
<TextView
android:id="@+id/tv_left"
style="@style/textNormal"
android:layout_centerVertical="true"
android:text="左侧的文本"
android:textColor="#99000000"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_right"
style="@style/textNormal"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="右侧的文本"
android:textColor="#99000000"
android:textSize="12sp" />
</RelativeLayout>
</RelativeLayout>
上面ProgressBar 中的progressDrawable 属性,它所引用的是用于设置进度条的背景层次关系的progress_horizontal 文件,这是为了进度条的效果更加美观,我们设置其主进度条和副进度条的颜色,具体如下所示: [XML] 纯文本查看 复制代码 <?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@android:id/background"
android:drawable="@color/progress_bg">
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<solid android:color="@color/progress_sencondary" />
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="@color/progress_progress" />
</shape>
</clip>
</item>
</layer-list> 布局文件写好了,接下来就可以创建一个自定义的view:ProgressStateView去继承一个容器比如RelativeLayout,不过为了在引用这个自定义view的时候可以方便的在布局去定义作文本的文本内容所以这里使用了自定义属性的操作: [Java] 纯文本查看 复制代码 public class ProgressStateView extends RelativeLayout {
private TextView mTvText;
private TextView mTvLeft;
private TextView mTvRight;
private ProgressBar mPbProgress;
public ProgressStateView(Context context) {
this(context, null);
}
public ProgressStateView(Context context, AttributeSet attrs) {
super(context, attrs);
// 加载布局
View.inflate(context, R.layout.view_progress_state, this);
// 初始化view
initView();
// 设置属性值
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.ProgressStateView);
String text = ta.getString(R.styleable.ProgressStateView_psvText);
ta.recycle();
mTvText.setText(text);
}
private void initView() {
mTvText = (TextView) findViewById(R.id.tv_title);
mTvLeft = (TextView) findViewById(R.id.tv_left);
mTvRight = (TextView) findViewById(R.id.tv_right);
mPbProgress = (ProgressBar) findViewById(R.id.pb_progress);
}
/**设置左边文本*/
public void setLeftText(String text) {
mTvLeft.setText(text);
}
/**设置右边文本*/
public void setRightText(String text) {
mTvRight.setText(text);
}
/**设置当前的进度*/
public void setProgress(int progress) {
mPbProgress.setProgress(progress);
}
/**设置最大进度*/
public void setMax(int max) {
mPbProgress.setMax(max);
}
} R.styleable.ProgressStateView在res/values/attrs.xml中定义如下: [XML] 纯文本查看 复制代码 <!-- 为ProgressStateView添加自定义属性 -->
<declare-styleable name="ProgressStateView">
<attr name="psvText" format="reference|string" />
</declare-styleable> 那么自定义的progressStateView定义好了后,就可以写入到我们的activity_app_manager.xml中去了: [XML] 纯文本查看 复制代码 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:itheima="http://schemas.android.com/apk/res/com.itheima.zphuanlove"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
style="@style/titleBarStyle"
android:text="软件管家" />
<com.itheima.zphuanlove.view.ProgressStateView
android:id="@+id/am_psv_rom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
itheima:psvText="内存:" />
<com.itheima.zphuanlove.view.ProgressStateView
android:id="@+id/am_psv_sd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
itheima:psvText="SD卡:" />
</LinearLayout> 显示的效果如下:
获取手机内存和SD 卡内存 布局文件前面我们已经写好,这里,我们首先需要实现获取手机的内存和SD 卡内存的信息,AppManagerActivity 中的逻辑代码如下所示: [Java] 纯文本查看 复制代码 public class AppManagerActivity extends Activity {
private ProgressStateView mPsvRom;
private ProgressStateView mPsvSD;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_manager);
initView();
initData();
}
private void initView() {
mPsvRom = (ProgressStateView) findViewById(R.id.am_psv_rom);
mPsvSD = (ProgressStateView) findViewById(R.id.am_psv_sd);
}
private void initData() {
// 1. 去获得内部存储的总大小和剩余 (data目录)
File dataDir = Environment.getDataDirectory();
long romTotalSpace = dataDir.getTotalSpace();// 总的
long romFreeSpace = dataDir.getFreeSpace();// 剩余
// long usableSpace = dataDir.getUsableSpace();
// System.out.println("可用空间:"+usableSpace+"---剩余空间:"+romFreeSpace);
long romUsedSpace = romTotalSpace - romFreeSpace;// 使用的
int romProgress = (int) (romUsedSpace * 100f / romTotalSpace + 0.5f);
// 剩余
mPsvRom.setRightText(Formatter.formatFileSize(this, romFreeSpace)
+ "可用");
mPsvRom.setLeftText(Formatter.formatFileSize(this, romUsedSpace) + "已用");
mPsvRom.setProgress(romProgress);
// 3.去获取sd卡相关信息
File sdDir = Environment.getExternalStorageDirectory();
long sdTotalSpace = sdDir.getTotalSpace();// 总的
long sdFreeSpace = sdDir.getFreeSpace();// 剩余
long sdUsedSpace = sdTotalSpace - sdFreeSpace;// 使用的
int sdProgress = (int) (sdUsedSpace * 100f / sdTotalSpace + 0.5f);
// 剩余
mPsvSD.setRightText(Formatter.formatFileSize(this, sdFreeSpace) + "可用");
mPsvSD.setLeftText(Formatter.formatFileSize(this, sdUsedSpace) + "已用");
mPsvSD.setProgress(sdProgress);
}
} 运行后的效果如下:
应用程序的实体类
在获取应用程序列表之前首先需要创建一个实体类(AppInfo.java),该类用于存储应用程序的相关信息,如程序包名、程序图标、程序大小等,具体代码如下所示:
[Java] 纯文本查看 复制代码 public class AppInfo {
// Drawable 不仅仅表示图片表示资源文件表示的范围更广泛
// bitmap 只能单纯表示图片
public Drawable icon;// 应用的icon
public String name;// 名称
public boolean isInstallSD;// 是否安装在sd卡
public long space;// 使用的空间大小
public boolean isSystem;// 表示是否是系统应用
public String packageName;// 应用的包名
}
获取应用程序的业务类
在开发软件管家模块时,需要获取手机中所有已安装程序,且ListView 中每个条目都是手机里的一个安装程序,如下图所示。由于该功能比较独立,因此,可以将其专门定义为一个业务逻辑类,以提高代码的可移植性,具体代码如下所示:
在包名.business包下创建一个业务逻辑类:AppProvider,代码如下:
[Java] 纯文本查看 复制代码 public class AppProvider {
private static final String TAG = "AppProvider";
/**
* 获取所有已安装的应用程序
* @param context
* @return
*/
public static List<AppInfo> getAllApps(Context context) {
List<AppInfo> list = new ArrayList<AppInfo>();
PackageManager pm = context.getPackageManager();
List<PackageInfo> packages = pm.getInstalledPackages(0);
// 迭代所有的安装包
for (PackageInfo info : packages) {
// 获取到应用的包名
String name = PackageUtils.getAppName(context, info);
//获取到应用的icon
Drawable icon = PackageUtils.getAppIcon(context, info);
boolean isInstallSD = PackageUtils.isInstallSD(info);
AppInfo bean = new AppInfo();
bean.name = name;
bean.icon = icon;
bean.space = PackageUtils.getAppSpace(info);
bean.isInstallSD = isInstallSD;
bean.isSystem = PackageUtils.isSystemApp(info);
Log.d(TAG, name + " : " + bean.isSystem);
bean.packageName = info.packageName;
list.add(bean);
}
return list;
}
}
里面涉及到工具类PackageUtils几个方法:
[Java] 纯文本查看 复制代码 /**
* 获取应用程序名称
* @param context:上下文
* @param info:包信息
* @return
*/
public static String getAppName(Context context, PackageInfo info) {
PackageManager pm = context.getPackageManager();
ApplicationInfo applicationInfo = info.applicationInfo;
return applicationInfo.loadLabel(pm).toString();
}
/**
* 获取应用程序icon
* @param context:上下文
* @param info:包信息
* @return
*/
public static Drawable getAppIcon(Context context, PackageInfo info) {
PackageManager pm = context.getPackageManager();
ApplicationInfo applicationInfo = info.applicationInfo;
return applicationInfo.loadIcon(pm);
}
/**
* 获取应用程序apk大小
* @param info:包信息
* @return
*/
public static long getAppSpace(PackageInfo info) {
ApplicationInfo applicationInfo = info.applicationInfo;
// applicationInfo.dataDir
String sourceDir = applicationInfo.sourceDir;
File file = new File(sourceDir);
return file.length();
}
/**
* 判断当前应用是否安装在sd卡
* @param info:包信息
* @return true:是安装在sd卡
*/
public static boolean isInstallSD(PackageInfo info) {
ApplicationInfo applicationInfo = info.applicationInfo;
int flags = applicationInfo.flags;// 应用的标记,能力或是特性
boolean isInsallSD = false;
if ((flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == ApplicationInfo.FLAG_EXTERNAL_STORAGE) {
isInsallSD = true;
}
return isInsallSD;
}
/**
* 判断当前应用是否为系统应用
* @param info:包信息
* @return true:是系统应用
*/
public static boolean isSystemApp(PackageInfo info) {
ApplicationInfo applicationInfo = info.applicationInfo;
int flags = applicationInfo.flags;// 应用的标记,能力或是特性
boolean isSystemApp = false;
if ((flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM) {
isSystemApp = true;
}
return isSystemApp;
} 软件管家界面ListView 的条目布局
主界面中ListView 的展示应用程序信息的条目可知,这里我们需要定义一个布局文件用于显示这些信息,具体代码如下所示:
[XML] 纯文本查看 复制代码 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp" >
<ImageView
android:id="@+id/item_am_iv_icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_centerVertical="true"
android:src="@drawable/ic_launcher" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_toRightOf="@id/item_am_iv_icon"
android:orientation="vertical" >
<TextView
android:id="@+id/item_am_tv_name"
style="@style/textNormal"
android:text="名称 " />
<TextView
android:id="@+id/item_am_tv_install"
style="@style/textNormal"
android:text="安装位置" />
</LinearLayout>
<TextView
android:id="@+id/item_am_tv_space"
style="@style/textNormal"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="空间" />
</RelativeLayout>
软件管家界面ListView 的数据展示
接下来数据有了,item界面也有了,只需要给AppManagerActivity中的listview设置数据适配器,将数据和界面结合起来即可,代码如下:
[Java] 纯文本查看 复制代码 public class AppManagerActivity extends Activity {
private ProgressStateView mPsvRom;
private ProgressStateView mPsvSD;
private ListView mListView;
private List<AppInfo> mDatas;
private AppAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_manager);
initView();
initData();
}
private void initView() {
mPsvRom = (ProgressStateView) findViewById(R.id.am_psv_rom);
mPsvSD = (ProgressStateView) findViewById(R.id.am_psv_sd);
mListView = (ListView) findViewById(R.id.am_listview);
}
private void initData() {
// 1. 去获得内部存储的总大小和剩余 (data目录)
File dataDir = Environment.getDataDirectory();
long romTotalSpace = dataDir.getTotalSpace();// 总的
long romFreeSpace = dataDir.getFreeSpace();// 剩余
// long usableSpace = dataDir.getUsableSpace();
// System.out.println("可用空间:"+usableSpace+"---剩余空间:"+romFreeSpace);
long romUsedSpace = romTotalSpace - romFreeSpace;// 使用的
int romProgress = (int) (romUsedSpace * 100f / romTotalSpace + 0.5f);
// 剩余
mPsvRom.setRightText(Formatter.formatFileSize(this, romFreeSpace)+ "可用");
mPsvRom.setLeftText(Formatter.formatFileSize(this, romUsedSpace) + "已用");
mPsvRom.setProgress(romProgress);
// 3.去获取sd卡相关信息
File sdDir = Environment.getExternalStorageDirectory();
long sdTotalSpace = sdDir.getTotalSpace();// 总的
long sdFreeSpace = sdDir.getFreeSpace();// 剩余
long sdUsedSpace = sdTotalSpace - sdFreeSpace;// 使用的
int sdProgress = (int) (sdUsedSpace * 100f / sdTotalSpace + 0.5f);
// 剩余
mPsvSD.setRightText(Formatter.formatFileSize(this, sdFreeSpace) + "可用");
mPsvSD.setLeftText(Formatter.formatFileSize(this, sdUsedSpace) + "已用");
mPsvSD.setProgress(sdProgress);
//获取所有安装的应用信息
mDatas = AppProvider.getAllApps(AppManagerActivity.this);
// 给listView设置adapter
mAdapter = new AppAdapter();
mListView.setAdapter(mAdapter);
}
private class AppAdapter extends BaseAdapter{
@Override
public int getCount() {
if (mDatas != null) {
return mDatas.size();
}
return 0;
}
@Override
public Object getItem(int position) {
if (mDatas != null) {
return mDatas.get(position);
}
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
// 没有复用
// 1.加载xml布局
convertView = View.inflate(AppManagerActivity.this,
R.layout.item_activity_manager, null);
// 2.新建holder
holder = new ViewHolder();
// 3. 设置tag
convertView.setTag(holder);
// 4.holder findViewbyid
holder.ivIcon = (ImageView) convertView
.findViewById(R.id.item_am_iv_icon);
holder.tvInstall = (TextView) convertView
.findViewById(R.id.item_am_tv_install);
holder.tvName = (TextView) convertView
.findViewById(R.id.item_am_tv_name);
holder.tvSpace = (TextView) convertView
.findViewById(R.id.item_am_tv_space);
} else {
// 有复用
holder = (ViewHolder) convertView.getTag();
}
// 设置数据
AppInfo bean = mDatas.get(position);
holder.ivIcon.setImageDrawable(bean.icon);
holder.tvName.setText(bean.name);
holder.tvInstall.setText(bean.isInstallSD ? "SD卡安装" : "内存安装");
holder.tvSpace.setText(Formatter.formatFileSize(
AppManagerActivity.this, bean.space));
return convertView;
}
}
private static class ViewHolder {
ImageView ivIcon;
TextView tvName;
TextView tvInstall;
TextView tvSpace;
}
}
应用程序安装在SD卡
在清单文件中设置installlocation即可控制当前应用安装在哪个位置,默认为安装在系统的内存,设置为preferExternal可以设置在sd卡上。
|