文章目录
1.软件管理——PopupWindow的使用
之前,我们完成了“软件管理”模块中的软件展示功能,现在需要实现——点击某个条目时,弹出对应的选单,在选单中可以执行相应操作,如图所示:
这个选单可以使用Android官方提供的PopupWindows
控件来实现。
修改AppManagerActivity,修改intiListView()方法,对每一项图文条目注册点击事件,并新建showPopupWindow(),作为显示PopupWindow窗体的方法,代码如下:
package com.example.mobilesafe.activity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.StatFs;
import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;
import com.example.mobilesafe.R;
import com.example.mobilesafe.dao.AppInfoDao;
import com.example.mobilesafe.domain.AppInfo;
import java.util.ArrayList;
import java.util.List;
public class AppManagerActivity extends AppCompatActivity {
private ListView lv_app_list;
private List<AppInfo> mAppInfoList;
private MyAdapter mAdapter;
// 系统应用所在的集合
private List<AppInfo> mSystemList;
// 用户应用所在的集合
private List<AppInfo> mCustomerList;
private TextView tv_title_type;
private AppInfo mAppInfo;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
// 4.使用数据适配器
mAdapter = new MyAdapter();
lv_app_list.setAdapter(mAdapter);
}
};
class MyAdapter extends BaseAdapter{
// 在ListView中多添加一种类型条目,条目总数有2种
@Override
public int getViewTypeCount() {
return super.getViewTypeCount() + 1;
}
// 根据索引值决定展示哪种类型的条目
@Override
public int getItemViewType(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return 0;
}else {
// 图文条目
return 1;
}
}
@Override
public int getCount() {
return mCustomerList.size() + mSystemList.size() + 2;
}
@Override
public AppInfo getItem(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return null;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
return mCustomerList.get(position - 1);
}else {
// 系统应用
return mSystemList.get(position - mCustomerList.size() - 2);
}
}
// return mAppInfoList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 判断当前索引指向的条目类型状态码
int itemViewType = getItemViewType(position);
if (itemViewType == 0){
// 纯文本条目
ViewTitleHolder holder = null;
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_app_item_text,null);
holder = new ViewTitleHolder();
holder.tv_title_des = convertView.findViewById(R.id.tv_title_des);
convertView.setTag(holder);
}else {
holder = (ViewTitleHolder) convertView.getTag();
}
if (position == 0){
holder.tv_title_des.setText("用户应用("+mCustomerList.size()+")");
}else {
holder.tv_title_des.setText("系统应用("+mSystemList.size()+")");
}
return convertView;
}else {
// 图文条目
ViewHolder holder = null;
// 1.判断ConverView是否为空
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_app_item,null);
// 2.获取控件实例赋值给ViewHolder
holder = new ViewHolder();
holder.iv_icon_application = convertView.findViewById(R.id.iv_icon_application);
holder.tv_app_name = convertView.findViewById(R.id.tv_app_name);
holder.tv_app_location = convertView.findViewById(R.id.tv_app_location);
// 3.将Holder放到ConverView上
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
// 4.获取Holder中的控件实例,赋值
holder.iv_icon_application.setBackground(getItem(position).getIcon());
holder.tv_app_name.setText(getItem(position).getName());
// 5.显示应用安装的位置
if (getItem(position).isSdCard()){
holder.tv_app_location.setText("sd卡应用");
}else {
holder.tv_app_location.setText("内存应用");
}
// 6.返回现有条目填充上数据的View对象
return convertView;
}
}
}
static class ViewHolder{
ImageView iv_icon_application;
TextView tv_app_name;
TextView tv_app_location;
}
static class ViewTitleHolder{
TextView tv_title_des;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_manager);
// 初始化磁盘 & SD卡可用大小
initTitle();
// 初始化ListView
intiListView();
}
/**
* 初始化磁盘 & SD卡可用大小
*/
private void initTitle() {
// 0.初始化控件
TextView tv_memory = findViewById(R.id.tv_memory);
TextView tv_sd_memory = findViewById(R.id.tv_sd_memory);
// 1.获取磁盘(内存,区分于手机运行内存)可用大小,磁盘路径
String path = Environment.getDataDirectory().getAbsolutePath();
// 2.获取SD卡可用大小,SD卡路径
String sdpath = Environment.getExternalStorageDirectory().getAbsolutePath();
// 3.获取以上两个路径下文件夹的可用大小
long space = getAvailSpace(path);
long sdspace = getAvailSpace(sdpath);
// 4.对bytes为单位的数值格式化
String strSpace = Formatter.formatFileSize(this, space);
String strSdSpace = Formatter.formatFileSize(this, sdspace);
// 5.给控件赋值
tv_memory.setText("磁盘可用:" + strSpace);
tv_sd_memory.setText("sd卡可用:" + strSdSpace);
}
/**
* 计算文件夹的可用大小
* @param path 路径名
* @return 可用空间大小,单位为byte=8bit
*/
private long getAvailSpace(String path) {
// 获取可用磁盘大小的对象
StatFs statFs = new StatFs(path);
// 获取可用区块的个数
long availableBlocks = statFs.getAvailableBlocks();
// 获取区块的大小
long blockSize = statFs.getBlockSize();
// 可用空间大小 = 区块大小 * 可用区块个数
return availableBlocks * blockSize;
}
/**
* 初始化ListView
*/
private void intiListView() {
// 1.初始化控件
tv_title_type = findViewById(R.id.tv_title_type);
lv_app_list = findViewById(R.id.lv_app_list);
new Thread(){
@Override
public void run() {
// 2.准备填充ListView中数据适配器的数据
mAppInfoList = AppInfoDao.getAppInfoList(getApplicationContext());
mSystemList = new ArrayList<>();
mCustomerList = new ArrayList<>();
// 分割集合
for (AppInfo appInfo : mAppInfoList) {
if (appInfo.isSystem()){
mSystemList.add(appInfo);
}else {
mCustomerList.add(appInfo);
}
}
// 3.发送空消息进行数据绑定
mHandler.sendEmptyMessage(0);
}
}.start();
// 4.给ListView注册滚动事件
lv_app_list.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// 在滚动过程中调用方法
if (mSystemList != null && mCustomerList != null){
if (firstVisibleItem >= mCustomerList.size() + 1){
// 滚动到了系统应用
tv_title_type.setText("系统应用("+mSystemList.size()+")");
}else {
// 滚动到了用户应用
tv_title_type.setText("用户应用("+mCustomerList.size()+")");
}
}
}
});
// 5.给ListView中的每个条目注册点击事件
lv_app_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
mAppInfo = mCustomerList.get(position - 1);
}else {
// 系统应用
mAppInfo = mSystemList.get(position - mCustomerList.size() - 2);
}
showPopupWindow(view);
}
}
});
}
/**
* 显示PopupWindow窗体
* @param view 当前选中条目的view
*/
private void showPopupWindow(View view) {
View popupView = View.inflate(this, R.layout.popupwindow_layout, null);
TextView tv_uninstall = popupView.findViewById(R.id.tv_uninstall);
TextView tv_start = popupView.findViewById(R.id.tv_start);
TextView tv_share = popupView.findViewById(R.id.tv_share);
// 1.创建窗体对象,指定宽高
PopupWindow popupWindow = new PopupWindow(popupView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, true);
// 2.设置一个背景
popupWindow.setBackgroundDrawable(new ColorDrawable());
// 3.指定窗体位置
popupWindow.showAsDropDown(view,50,-view.getHeight());
// 卸载
tv_uninstall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
// 启动
tv_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
// 分享
tv_share.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
}
在res/layout下新建popupwindow_layout.xml,作为PopupWindow窗体的布局,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:background="@drawable/local_popup_bg"
android:layout_height="wrap_content">
<TextView
android:text="卸载"
android:id="@+id/tv_uninstall"
android:drawableTop="@drawable/img1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="启动"
android:id="@+id/tv_start"
android:drawableTop="@drawable/img2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="分享"
android:id="@+id/tv_share"
android:drawableTop="@drawable/img3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
2.软件管理——PopupWindow的动画
为了丰富PopupWindow的表现效果,我们添加一段淡入淡出的动画效果。
修改AppManagerActivity,修改showPopupWindow()方法,添加一段对PopupWindow出现时的透明 & 缩放动画,代码如下:
/**
* 显示PopupWindow窗体
* @param view 当前选中条目的view
*/
private void showPopupWindow(View view) {
View popupView = View.inflate(this, R.layout.popupwindow_layout, null);
TextView tv_uninstall = popupView.findViewById(R.id.tv_uninstall);
TextView tv_start = popupView.findViewById(R.id.tv_start);
TextView tv_share = popupView.findViewById(R.id.tv_share);
// 设置透明动画
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(1000);
alphaAnimation.setFillAfter(true);
// 设置缩放动画
ScaleAnimation scaleAnimation = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(1000);
alphaAnimation.setFillAfter(true);
// 设置动画集合Set(传tru代表共享一个数学函数)
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(scaleAnimation);
// 1.创建窗体对象,指定宽高
PopupWindow popupWindow = new PopupWindow(popupView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, true);
// 2.设置一个背景
popupWindow.setBackgroundDrawable(new ColorDrawable());
// 3.指定窗体位置
popupWindow.showAsDropDown(view,50,-view.getHeight());
// 4.popupView
popupView.startAnimation(animationSet);
}
3.软件管理——卸载应用 & 启动应用 & 分享应用
前面我们完成了在点击每个条目时弹出PopupWindow,现在需要逐步实现PopupWindow中的三个功能——卸载、启动、分享。
修改AppManagerActivity,对PopupWindow中的三个TextView控件分别注册点击事件,执行相应逻辑,注意在删除某个应用后要重写onResume()
,以此来更新数据集合,代码如下:
package com.example.mobilesafe.activity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.StatFs;
import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.ScaleAnimation;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;
import com.example.mobilesafe.R;
import com.example.mobilesafe.dao.AppInfoDao;
import com.example.mobilesafe.domain.AppInfo;
import com.example.mobilesafe.utils.ToastUtil;
import java.util.ArrayList;
import java.util.List;
public class AppManagerActivity extends AppCompatActivity {
private ListView lv_app_list;
private List<AppInfo> mAppInfoList;
private MyAdapter mAdapter;
// 系统应用所在的集合
private List<AppInfo> mSystemList;
// 用户应用所在的集合
private List<AppInfo> mCustomerList;
private TextView tv_title_type;
private AppInfo mAppInfo;
private PopupWindow mPopupWindow;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
// 4.使用数据适配器
mAdapter = new MyAdapter();
lv_app_list.setAdapter(mAdapter);
}
};
class MyAdapter extends BaseAdapter{
// 在ListView中多添加一种类型条目,条目总数有2种
@Override
public int getViewTypeCount() {
return super.getViewTypeCount() + 1;
}
// 根据索引值决定展示哪种类型的条目
@Override
public int getItemViewType(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return 0;
}else {
// 图文条目
return 1;
}
}
@Override
public int getCount() {
return mCustomerList.size() + mSystemList.size() + 2;
}
@Override
public AppInfo getItem(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return null;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
return mCustomerList.get(position - 1);
}else {
// 系统应用
return mSystemList.get(position - mCustomerList.size() - 2);
}
}
// return mAppInfoList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 判断当前索引指向的条目类型状态码
int itemViewType = getItemViewType(position);
if (itemViewType == 0){
// 纯文本条目
ViewTitleHolder holder = null;
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_app_item_text,null);
holder = new ViewTitleHolder();
holder.tv_title_des = convertView.findViewById(R.id.tv_title_des);
convertView.setTag(holder);
}else {
holder = (ViewTitleHolder) convertView.getTag();
}
if (position == 0){
holder.tv_title_des.setText("用户应用("+mCustomerList.size()+")");
}else {
holder.tv_title_des.setText("系统应用("+mSystemList.size()+")");
}
return convertView;
}else {
// 图文条目
ViewHolder holder = null;
// 1.判断ConverView是否为空
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_app_item,null);
// 2.获取控件实例赋值给ViewHolder
holder = new ViewHolder();
holder.iv_icon_application = convertView.findViewById(R.id.iv_icon_application);
holder.tv_app_name = convertView.findViewById(R.id.tv_app_name);
holder.tv_app_location = convertView.findViewById(R.id.tv_app_location);
// 3.将Holder放到ConverView上
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
// 4.获取Holder中的控件实例,赋值
holder.iv_icon_application.setBackground(getItem(position).getIcon());
holder.tv_app_name.setText(getItem(position).getName());
// 5.显示应用安装的位置
if (getItem(position).isSdCard()){
holder.tv_app_location.setText("sd卡应用");
}else {
holder.tv_app_location.setText("内存应用");
}
// 6.返回现有条目填充上数据的View对象
return convertView;
}
}
}
static class ViewHolder{
ImageView iv_icon_application;
TextView tv_app_name;
TextView tv_app_location;
}
static class ViewTitleHolder{
TextView tv_title_des;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_manager);
// 初始化磁盘 & SD卡可用大小
initTitle();
// 初始化ListView
intiListView();
}
/**
* 初始化磁盘 & SD卡可用大小
*/
private void initTitle() {
// 0.初始化控件
TextView tv_memory = findViewById(R.id.tv_memory);
TextView tv_sd_memory = findViewById(R.id.tv_sd_memory);
// 1.获取磁盘(内存,区分于手机运行内存)可用大小,磁盘路径
String path = Environment.getDataDirectory().getAbsolutePath();
// 2.获取SD卡可用大小,SD卡路径
String sdpath = Environment.getExternalStorageDirectory().getAbsolutePath();
// 3.获取以上两个路径下文件夹的可用大小
long space = getAvailSpace(path);
long sdspace = getAvailSpace(sdpath);
// 4.对bytes为单位的数值格式化
String strSpace = Formatter.formatFileSize(this, space);
String strSdSpace = Formatter.formatFileSize(this, sdspace);
// 5.给控件赋值
tv_memory.setText("磁盘可用:" + strSpace);
tv_sd_memory.setText("sd卡可用:" + strSdSpace);
}
/**
* 计算文件夹的可用大小
* @param path 路径名
* @return 可用空间大小,单位为byte=8bit
*/
private long getAvailSpace(String path) {
// 获取可用磁盘大小的对象
StatFs statFs = new StatFs(path);
// 获取可用区块的个数
long availableBlocks = statFs.getAvailableBlocks();
// 获取区块的大小
long blockSize = statFs.getBlockSize();
// 可用空间大小 = 区块大小 * 可用区块个数
return availableBlocks * blockSize;
}
/**
* 初始化ListView
*/
private void intiListView() {
// 1.初始化控件
tv_title_type = findViewById(R.id.tv_title_type);
lv_app_list = findViewById(R.id.lv_app_list);
// 4.给ListView注册滚动事件
lv_app_list.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// 在滚动过程中调用方法
if (mSystemList != null && mCustomerList != null){
if (firstVisibleItem >= mCustomerList.size() + 1){
// 滚动到了系统应用
tv_title_type.setText("系统应用("+mSystemList.size()+")");
}else {
// 滚动到了用户应用
tv_title_type.setText("用户应用("+mCustomerList.size()+")");
}
}
}
});
// 5.给ListView中的每个条目注册点击事件
lv_app_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
mAppInfo = mCustomerList.get(position - 1);
}else {
// 系统应用
mAppInfo = mSystemList.get(position - mCustomerList.size() - 2);
}
showPopupWindow(view);
}
}
});
}
/**
* 显示PopupWindow窗体
* @param view 当前选中条目的view
*/
private void showPopupWindow(View view) {
View popupView = View.inflate(this, R.layout.popupwindow_layout, null);
TextView tv_uninstall = popupView.findViewById(R.id.tv_uninstall);
TextView tv_start = popupView.findViewById(R.id.tv_start);
TextView tv_share = popupView.findViewById(R.id.tv_share);
// 设置透明动画
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(1000);
alphaAnimation.setFillAfter(true);
// 设置缩放动画
ScaleAnimation scaleAnimation = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(1000);
alphaAnimation.setFillAfter(true);
// 设置动画集合Set(传tru代表共享一个数学函数)
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(scaleAnimation);
// 1.创建窗体对象,指定宽高
mPopupWindow = new PopupWindow(popupView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, true);
// 2.设置一个背景
mPopupWindow.setBackgroundDrawable(new ColorDrawable());
// 3.指定窗体位置
mPopupWindow.showAsDropDown(view,50,-view.getHeight());
// 4.popupView
popupView.startAnimation(animationSet);
// 卸载
tv_uninstall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mAppInfo.isSystem()){
ToastUtil.show(getApplicationContext(),"此应用为系统应用,不能卸载!");
}else {
// 隐式Intent启动卸载功能
Intent intent = new Intent("android.intent.action.DELETE");
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse("package:" + mAppInfo.getPackagename()));
startActivity(intent);
}
// 点击窗体后消失窗体
if (mPopupWindow != null){
mPopupWindow.dismiss();
}
}
});
// 启动
tv_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 通过桌面去启动指定包名应用
PackageManager packageManager = getPackageManager();
Intent launchIntentForPackage = packageManager.getLaunchIntentForPackage(mAppInfo.getPackagename());// 开启指定包名
if(launchIntentForPackage != null){
startActivity(launchIntentForPackage);
}else {
ToastUtil.show(getApplicationContext(),"此应用不能被开启!");
}
// 点击窗体后消失窗体
if (mPopupWindow != null){
mPopupWindow.dismiss();
}
}
});
// 分享
tv_share.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 通过短信应用,向外发送短信(启动短信应用)
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT,"分享一个应用,应用名称为:" + mAppInfo.getName());
intent.setType("text/plain");
startActivity(intent);
// 点击窗体后消失窗体
if (mPopupWindow != null){
mPopupWindow.dismiss();
}
}
});
}
@Override
protected void onResume() {
super.onResume();
// 重新获取数据
getData();
}
/**
* 获取数据的方法
*/
private void getData(){
new Thread(){
@Override
public void run() {
// 2.准备填充ListView中数据适配器的数据
mAppInfoList = AppInfoDao.getAppInfoList(getApplicationContext());
mSystemList = new ArrayList<>();
mCustomerList = new ArrayList<>();
// 分割集合
for (AppInfo appInfo : mAppInfoList) {
if (appInfo.isSystem()){
mSystemList.add(appInfo);
}else {
mCustomerList.add(appInfo);
}
}
// 3.发送空消息进行数据绑定
mHandler.sendEmptyMessage(0);
}
}.start();
}
}
4.进程管理——布局实现
前面我们完成了“软件管理”模块的编写,现在需要完成“进程管理”模块的编写,如图所示:
该界面需要显示以下信息:
- 进程数
- 使用内存/总共内存
- 运行中的进程
- 全选 & 反选 & 一键清理 & 设置
首先修改HomeActivity,修改initData()方法,添加GridView
中第四个条目的点击事件,代码如下:
/**
* 2.初始化数据
*/
private void initData() {
// 1.初始化每个图标的标题
mTitleStrs = new String[]{"手机防盗","通信卫士","软件管理","进程管理","流量统计","手机杀毒","缓存清理","高级工具","设置中心"};
// 2.初始化每个图标的图像
mDrawableIds = new int[]{R.drawable.home_safe,R.drawable.home_callmsgsafe,R.drawable.home_apps,R.drawable.home_taskmanager,R.drawable.home_netmanager,R.drawable.home_trojan,R.drawable.home_sysoptimize,R.drawable.home_tools,R.drawable.home_settings};
// 3.为GridView设置数据适配器
gv_home.setAdapter(new MyAdapter());
// 4.注册GridView中单个条目的点击事件
gv_home.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch (position){
case 0:
// 手机防盗
showDialog();
break;
case 1:
// 通信卫士
startActivity(new Intent(getApplicationContext(),BlackNumberActivity.class));
break;
case 2:
// 软件管理
startActivity(new Intent(getApplicationContext(),AppManagerActivity.class));
break;
case 3:
// 进程管理
startActivity(new Intent(getApplicationContext(),ProcessManagerActivity.class));
break;
case 7:
// 高级工具
startActivity(new Intent(getApplicationContext(),AToolActivity.class));
break;
case 8:
// 设置中心
Intent intent = new Intent(getApplicationContext(), SettingActivity.class);
startActivity(intent);
break;
default:
break;
}
}
});
}
创建一个名为ProcessManagerActivity的Activity,作为进程管理的界面,首先修改其布局文件activity_process_manager.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".activity.ProcessManagerActivity"
android:orientation="vertical">
<TextView
android:text="进程管理"
style="@style/TitleStyle"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_process_count"
android:text="进程总数"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_memory_info"
android:text="剩余/总共"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
<!-- ListView默认不占高度 -->
<ListView
android:id="@+id/lv_process_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_all"
android:layout_width="0dp"
android:text="全选"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_reverse"
android:layout_width="0dp"
android:text="反选"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_clear"
android:layout_width="0dp"
android:text="一键清理"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_setting"
android:layout_width="0dp"
android:text="设置"
android:layout_weight="1"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
5.进程管理——获取可用内存数 & 总内存数
上一节我们完成了“进程管理”模块中的界面编写,现在需要获取到可用内存和总内存数,并且显示上去。
在dao包下新建ProcessInfoDao,作为提供进程信息的工具类,拥有几个常用的方法,代码如下:
package com.example.mobilesafe.dao;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Debug;
import com.example.mobilesafe.R;
import com.example.mobilesafe.domain.ProcessInfo;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ProcessInfoDao {
/**
* 获取进程总数的方法
* @param context 上下文环境
* @return
*/
public static int getProcessCount(Context context){
// 1.获取ActivityManager对象
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// 2.获取正在运行的进程的集合
List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
// 3.返回集合的总数
return runningAppProcesses.size();
}
/**
* 获取可用空间的大小
* @param context 上下文环境
* @return 返回可用的内存数,bytes
*/
public static long getAvailSpace(Context context){
// 1.获取ActivityManager对象
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// 2.构建存储可用内存的对象
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
// 3.给MemoryInfo对象赋值(可用内存)
activityManager.getMemoryInfo(memoryInfo);
// 4.获取MemoryInfo中相应的可用内存大小
return memoryInfo.availMem;
}
/**
* 获取所有空间的大小
* @param context 上下文环境
* @return 返回可用的内存数,单位为bytes
*/
@TargetApi(16)
public static long getTotalSpace(Context context){
if (Build.VERSION.SDK_INT >= 16){
// api版本大于16的方式
// 1.获取ActivityManager对象
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// 2.构建存储可用内存的对象
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
// 3.给MemoryInfo对象赋值(所有内存)
activityManager.getMemoryInfo(memoryInfo);
// 4.获取MemoryInfo中相应的所有内存大小
return memoryInfo.totalMem;
}else {
// api版本小于16的方式
// 如果你的api版本在16以下,需要读取proc/meminfo文件,读取第一行,获取数字字符,转换成bytes信息返回
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try {
fileReader = new FileReader("proc/meminfo");
bufferedReader = new BufferedReader(fileReader);
String lineOne = bufferedReader.readLine();
// 将字符串转换成字符数组
char[] chars = lineOne.toCharArray();
// 循环遍历每一个字符,如果此字符的ASCII码在0到9的区域内,说明此字符有效
StringBuffer stringBuffer = new StringBuffer();
for (char c : chars) {
if (c >= '0' && c <= '9'){
stringBuffer.append(c);
}
}
// 将字符数组转化成字符串,解析成long类型,并且乘1024转换成byte
return Long.parseLong(stringBuffer.toString()) * 1024;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null && bufferedReader != null) {
fileReader.close();
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return 0;
}
/**
* 获取进程信息的列表
* @param context 上下文环境
* @return 进程信息的列表
*/
public static List<ProcessInfo> getProcessInfoList(Context context){
// 获取进程相关信息
// 0.创建ProcessInfo集合
List<ProcessInfo> processInfoList = new ArrayList<>();
// 1.获取ActivityManager对象和PackageManger管理者对象
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
PackageManager packageManager = context.getPackageManager();
// 2.获取正在运行的进程的集合
List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
// 3.循环遍历上述集合,获取进程相关信息(名称,包名,图标,使用内存大小,是否为系统进程)
for (ActivityManager.RunningAppProcessInfo runningAppProcess : runningAppProcesses) {
ProcessInfo processInfo = new ProcessInfo();
// 4.获取进程名称,即包名
processInfo.setPackageName(runningAppProcess.processName);
// 5.获取进程占用的内存大小(传递一个进程对应的pid数组)
Debug.MemoryInfo[] processMemoryInfo = activityManager.getProcessMemoryInfo(new int[]{runningAppProcess.pid}); // 数组中索引为0的对象为当前线程的内存信息的对象
Debug.MemoryInfo memoryInfo = processMemoryInfo[0];
processInfo.setMemSize(memoryInfo.getTotalPrivateDirty() * 1024);
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(runningAppProcess.processName, 0);
// 6.获取应用的名称
processInfo.setName(applicationInfo.loadLabel(packageManager).toString());
// 7.获取应用的图标
processInfo.setIcon(applicationInfo.loadIcon(packageManager));
// 8.判断是否为启动线程
if ((applicationInfo.flags & applicationInfo.FLAG_SYSTEM) == applicationInfo.FLAG_SYSTEM){
processInfo.setSystem(true);
}else {
processInfo.setSystem(false);
}
} catch (PackageManager.NameNotFoundException e) {
// 需要处理,即非应用的无名称、无图标的进程
processInfo.setName(runningAppProcess.processName);
processInfo.setIcon(context.getResources().getDrawable(R.drawable.ic_launcher));
processInfo.setSystem(true);
e.printStackTrace();
}
processInfoList.add(processInfo);
}
return processInfoList;
}
}
为了将条目信息更好地进行维护,在domain包下新建ProcessInfo,作为封装进程信息的实体类,代码如下:
package com.example.mobilesafe.domain;
import android.graphics.drawable.Drawable;
public class ProcessInfo {
private String name; // 应用名称
private Drawable icon; // 应用图标
private long memSize; // 应用已使用的内存数
private boolean isCheck; // 应用是否被选中
private boolean isSystem; // 应用是否为系统应用
public String packageName; // 如果应用服务没有名称,则将其应用所在的包名作为名称
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Drawable getIcon() {
return icon;
}
public void setIcon(Drawable icon) {
this.icon = icon;
}
public long getMemSize() {
return memSize;
}
public void setMemSize(long memSize) {
this.memSize = memSize;
}
public boolean isCheck() {
return isCheck;
}
public void setCheck(boolean check) {
isCheck = check;
}
public boolean isSystem() {
return isSystem;
}
public void setSystem(boolean system) {
isSystem = system;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
}
6.进程管理——显示内存使用情况
上一节中我们完成了工具类的编写,现在就是需要使用这个工具类来实现内存情况的可视化管理。
修改ProcessManagerActivity,使用之前编写好的工具类获取可用内存/总内存大小,并且格式化显示到文本控件上,代码如下:
package com.example.mobilesafe.activity;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.format.Formatter;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import com.example.mobilesafe.R;
import com.example.mobilesafe.dao.ProcessInfoDao;
public class ProcessManagerActivity extends AppCompatActivity {
private TextView tv_process_count;
private TextView tv_memory_info;
private ListView lv_process_list;
private Button btn_all;
private Button btn_reverse;
private Button btn_clear;
private Button btn_setting;
private int mProcessCount; // 进程总数
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_process_manager);
// 初始化UI
initUI();
// 初始化标题上的信息:进程总数,内存使用情况
initTitleData();
}
/**
* 初始化UI
*/
private void initUI() {
tv_process_count = findViewById(R.id.tv_process_count);
tv_memory_info = findViewById(R.id.tv_memory_info);
lv_process_list = findViewById(R.id.lv_process_list);
btn_all = findViewById(R.id.btn_all);
btn_reverse = findViewById(R.id.btn_reverse);
btn_clear = findViewById(R.id.btn_clear);
btn_setting = findViewById(R.id.btn_setting);
btn_all.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_reverse.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_setting.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
/**
* 初始化标题上的信息:进程总数,内存使用情况
*/
private void initTitleData() {
// 获取进程总数
mProcessCount = ProcessInfoDao.getProcessCount(this);
tv_process_count.setText("进程总数:" + mProcessCount);
// 获取可用内存大小
long availSpace = ProcessInfoDao.getAvailSpace(this);
String strAvailSpace = Formatter.formatFileSize(this, availSpace); // 格式化
// 获取总内存大小
long totalSpace = ProcessInfoDao.getTotalSpace(this);
String strTotalSpace = Formatter.formatFileSize(this, totalSpace); // 格式化
tv_memory_info.setText("剩余/总共" + strAvailSpace + "/" + strTotalSpace);
}
}
7.进程管理——列表数据填充
前面我们完成了内存使用情况的获取和显示,现在需要将进程信息填充到列表控件中。
修改ProcessManagerActivity,参考之前写过的AppManagerActivity,完善相应逻辑,代码如下:
package com.example.mobilesafe.activity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.example.mobilesafe.R;
import com.example.mobilesafe.dao.ProcessInfoDao;
import com.example.mobilesafe.domain.AppInfo;
import com.example.mobilesafe.domain.ProcessInfo;
import java.util.ArrayList;
import java.util.List;
public class ProcessManagerActivity extends AppCompatActivity {
private TextView tv_process_count;
private TextView tv_memory_info;
private ListView lv_process_list;
private Button btn_all;
private Button btn_reverse;
private Button btn_clear;
private Button btn_setting;
private int mProcessCount; // 进程总数
private List<ProcessInfo> mProcessInfoList;
private List<ProcessInfo> mSystemList;
private List<ProcessInfo> mCustomerList;
private MyAdapter mAdapter;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
mAdapter = new MyAdapter();
lv_process_list.setAdapter(mAdapter);
}
};
class MyAdapter extends BaseAdapter {
// 在ListView中多添加一种类型条目,条目总数有2种
@Override
public int getViewTypeCount() {
return super.getViewTypeCount() + 1;
}
// 根据索引值决定展示哪种类型的条目
@Override
public int getItemViewType(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return 0;
}else {
// 图文条目
return 1;
}
}
@Override
public int getCount() {
return mCustomerList.size() + mSystemList.size() + 2;
}
@Override
public ProcessInfo getItem(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return null;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
return mCustomerList.get(position - 1);
}else {
// 系统应用
return mSystemList.get(position - mCustomerList.size() - 2);
}
}
// return mAppInfoList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 判断当前索引指向的条目类型状态码
int itemViewType = getItemViewType(position);
if (itemViewType == 0){
// 纯文本条目
ViewTitleHolder holder = null;
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_app_item_text,null);
holder = new ViewTitleHolder();
holder.tv_title_des = convertView.findViewById(R.id.tv_title_des);
convertView.setTag(holder);
}else {
holder = (ViewTitleHolder) convertView.getTag();
}
if (position == 0){
holder.tv_title_des.setText("用户进程("+mCustomerList.size()+")");
}else {
holder.tv_title_des.setText("系统进程("+mSystemList.size()+")");
}
return convertView;
}else {
// 图文条目
ViewHolder holder = null;
// 1.判断ConverView是否为空
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_process_item,null);
// 2.获取控件实例赋值给ViewHolder
holder = new ViewHolder();
holder.iv_icon_application = convertView.findViewById(R.id.iv_icon_application);
holder.tv_app_name = convertView.findViewById(R.id.tv_app_name);
holder.tv_memory = convertView.findViewById(R.id.tv_memory);
holder.cb_box = convertView.findViewById(R.id.cb_box);
// 3.将Holder放到ConverView上
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
// 4.获取Holder中的控件实例,赋值
holder.iv_icon_application.setBackground(getItem(position).getIcon());
holder.tv_app_name.setText(getItem(position).getName());
String strMemorySize = Formatter.formatFileSize(getApplicationContext(), getItem(position).getMemSize());
holder.tv_memory.setText(strMemorySize);
// 5.本应用无法被选中,所以先讲CheckBox隐藏
if ((getItem(position).getPackageName()).equals(getPackageName())){
holder.cb_box.setVisibility(View.GONE);
}else {
holder.cb_box.setVisibility(View.VISIBLE);
}
holder.cb_box.setChecked(getItem(position).isCheck());
// 6.返回现有条目填充上数据的View对象
return convertView;
}
}
}
static class ViewHolder{
ImageView iv_icon_application;
TextView tv_app_name;
TextView tv_memory;
CheckBox cb_box;
}
static class ViewTitleHolder{
TextView tv_title_des;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_process_manager);
// 初始化UI
initUI();
// 初始化标题上的信息:进程总数,内存使用情况
initTitleData();
// 初始化ListView列表中的数据
initListData();
}
/**
* 初始化UI
*/
private void initUI() {
tv_process_count = findViewById(R.id.tv_process_count);
tv_memory_info = findViewById(R.id.tv_memory_info);
lv_process_list = findViewById(R.id.lv_process_list);
btn_all = findViewById(R.id.btn_all);
btn_reverse = findViewById(R.id.btn_reverse);
btn_clear = findViewById(R.id.btn_clear);
btn_setting = findViewById(R.id.btn_setting);
btn_all.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_reverse.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_setting.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
/**
* 初始化标题上的信息:进程总数,内存使用情况
*/
private void initTitleData() {
// 获取进程总数
mProcessCount = ProcessInfoDao.getProcessCount(this);
tv_process_count.setText("进程总数:" + mProcessCount);
// 获取可用内存大小
long availSpace = ProcessInfoDao.getAvailSpace(this);
String strAvailSpace = Formatter.formatFileSize(this, availSpace); // 格式化
// 获取总内存大小
long totalSpace = ProcessInfoDao.getTotalSpace(this);
String strTotalSpace = Formatter.formatFileSize(this, totalSpace); // 格式化
tv_memory_info.setText("剩余/总共" + strAvailSpace + "/" + strTotalSpace);
}
/**
* 初始化列表中的数据
*/
private void initListData() {
getData();
}
/**
* 获取数据的方法
*/
private void getData(){
new Thread(){
@Override
public void run() {
// 2.准备填充ListView中数据适配器的数据
mProcessInfoList = ProcessInfoDao.getProcessInfoList(getApplicationContext());
mSystemList = new ArrayList<>();
mCustomerList = new ArrayList<>();
// 分割集合
for (ProcessInfo processInfo : mProcessInfoList) {
if (processInfo.isSystem()){
// 系统进程
mSystemList.add(processInfo);
}else {
// 用户进程
mCustomerList.add(processInfo);
}
}
// 3.发送空消息进行数据绑定
mHandler.sendEmptyMessage(0);
}
}.start();
}
}
为了更好地显示图文条目,新建一个名为list_process_item.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">
<ImageView
android:id="@+id/iv_icon_application"
android:background="@drawable/ic_launcher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_app_name"
android:layout_toRightOf="@id/iv_icon_application"
android:text="应用名称"
android:textColor="#000"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_memory"
android:layout_toRightOf="@id/iv_icon_application"
android:layout_below="@id/tv_app_name"
android:text="占用内存大小"
android:textColor="#000"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox
android:id="@+id/cb_box"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
8.进程管理——常驻悬浮框
跟“软件管理”模块一样,“进程管理”模块同样需要一个常驻悬浮框的功能。
修改activity_process_manager.xml,同样适用一个帧布局进行包装,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".activity.ProcessManagerActivity"
android:orientation="vertical">
<TextView
android:text="进程管理"
style="@style/TitleStyle"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_process_count"
android:text="进程总数"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_memory_info"
android:text="剩余/总共"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
<!-- ListView默认不占高度 -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<ListView
android:id="@+id/lv_process_list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/tv_title_type"
android:background="#ccc"
android:textColor="#fff"
android:text="条目类型的说明"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_all"
android:layout_width="0dp"
android:text="全选"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_reverse"
android:layout_width="0dp"
android:text="反选"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_clear"
android:layout_width="0dp"
android:text="一键清理"
android:layout_weight="1"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_setting"
android:layout_width="0dp"
android:text="设置"
android:layout_weight="1"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
修改ProcessManagerActivity,完善相应逻辑,代码如下:
package com.example.mobilesafe.activity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.example.mobilesafe.R;
import com.example.mobilesafe.dao.ProcessInfoDao;
import com.example.mobilesafe.domain.ProcessInfo;
import java.util.ArrayList;
import java.util.List;
public class ProcessManagerActivity extends AppCompatActivity {
private TextView tv_process_count;
private TextView tv_memory_info;
private TextView tv_title_type;
private ListView lv_process_list;
private Button btn_all;
private Button btn_reverse;
private Button btn_clear;
private Button btn_setting;
private int mProcessCount; // 进程总数
private List<ProcessInfo> mProcessInfoList;
private List<ProcessInfo> mSystemList;
private List<ProcessInfo> mCustomerList;
private MyAdapter mAdapter;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
mAdapter = new MyAdapter();
lv_process_list.setAdapter(mAdapter);
if (tv_title_type != null && mCustomerList != null ){
tv_title_type.setText("用户进程("+mCustomerList.size()+")");
}
}
};
class MyAdapter extends BaseAdapter {
// 在ListView中多添加一种类型条目,条目总数有2种
@Override
public int getViewTypeCount() {
return super.getViewTypeCount() + 1;
}
// 根据索引值决定展示哪种类型的条目
@Override
public int getItemViewType(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return 0;
}else {
// 图文条目
return 1;
}
}
@Override
public int getCount() {
return mCustomerList.size() + mSystemList.size() + 2;
}
@Override
public ProcessInfo getItem(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return null;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
return mCustomerList.get(position - 1);
}else {
// 系统应用
return mSystemList.get(position - mCustomerList.size() - 2);
}
}
// return mAppInfoList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 判断当前索引指向的条目类型状态码
int itemViewType = getItemViewType(position);
if (itemViewType == 0){
// 纯文本条目
ViewTitleHolder holder = null;
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_app_item_text,null);
holder = new ViewTitleHolder();
holder.tv_title_des = convertView.findViewById(R.id.tv_title_des);
convertView.setTag(holder);
}else {
holder = (ViewTitleHolder) convertView.getTag();
}
if (position == 0){
holder.tv_title_des.setText("用户进程("+mCustomerList.size()+")");
}else {
holder.tv_title_des.setText("系统进程("+mSystemList.size()+")");
}
return convertView;
}else {
// 图文条目
ViewHolder holder = null;
// 1.判断ConverView是否为空
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_process_item,null);
// 2.获取控件实例赋值给ViewHolder
holder = new ViewHolder();
holder.iv_icon_application = convertView.findViewById(R.id.iv_icon_application);
holder.tv_app_name = convertView.findViewById(R.id.tv_app_name);
holder.tv_memory = convertView.findViewById(R.id.tv_memory);
holder.cb_box = convertView.findViewById(R.id.cb_box);
// 3.将Holder放到ConverView上
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
// 4.获取Holder中的控件实例,赋值
holder.iv_icon_application.setBackground(getItem(position).getIcon());
holder.tv_app_name.setText(getItem(position).getName());
String strMemorySize = Formatter.formatFileSize(getApplicationContext(), getItem(position).getMemSize());
holder.tv_memory.setText(strMemorySize);
// 5.本进程无法被选中,所以先将CheckBox隐藏
if ((getItem(position).getPackageName()).equals(getPackageName())){
holder.cb_box.setVisibility(View.GONE);
}else {
holder.cb_box.setVisibility(View.VISIBLE);
}
holder.cb_box.setChecked(getItem(position).isCheck());
// 6.返回现有条目填充上数据的View对象
return convertView;
}
}
}
static class ViewHolder{
ImageView iv_icon_application;
TextView tv_app_name;
TextView tv_memory;
CheckBox cb_box;
}
static class ViewTitleHolder{
TextView tv_title_des;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_process_manager);
// 初始化UI
initUI();
// 初始化标题上的信息:进程总数,内存使用情况
initTitleData();
// 初始化ListView列表中的数据
initListData();
}
/**
* 初始化UI
*/
private void initUI() {
tv_process_count = findViewById(R.id.tv_process_count);
tv_memory_info = findViewById(R.id.tv_memory_info);
tv_title_type = findViewById(R.id.tv_title_type);
lv_process_list = findViewById(R.id.lv_process_list);
btn_all = findViewById(R.id.btn_all);
btn_reverse = findViewById(R.id.btn_reverse);
btn_clear = findViewById(R.id.btn_clear);
btn_setting = findViewById(R.id.btn_setting);
// 给ListView注册滚动事件
lv_process_list.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// 在滚动过程中调用方法
if (mSystemList != null && mCustomerList != null){
if (firstVisibleItem >= mCustomerList.size() + 1){
// 滚动到了系统应用
tv_title_type.setText("系统进程("+mSystemList.size()+")");
}else {
// 滚动到了用户应用
tv_title_type.setText("用户进程("+mCustomerList.size()+")");
}
}
}
});
btn_all.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_reverse.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_setting.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
/**
* 初始化标题上的信息:进程总数,内存使用情况
*/
private void initTitleData() {
// 获取进程总数
mProcessCount = ProcessInfoDao.getProcessCount(this);
tv_process_count.setText("进程总数:" + mProcessCount);
// 获取可用内存大小
long availSpace = ProcessInfoDao.getAvailSpace(this);
String strAvailSpace = Formatter.formatFileSize(this, availSpace); // 格式化
// 获取总内存大小
long totalSpace = ProcessInfoDao.getTotalSpace(this);
String strTotalSpace = Formatter.formatFileSize(this, totalSpace); // 格式化
tv_memory_info.setText("剩余/总共" + strAvailSpace + "/" + strTotalSpace);
}
/**
* 初始化列表中的数据
*/
private void initListData() {
getData();
}
/**
* 获取数据的方法
*/
private void getData(){
new Thread(){
@Override
public void run() {
// 2.准备填充ListView中数据适配器的数据
mProcessInfoList = ProcessInfoDao.getProcessInfoList(getApplicationContext());
mSystemList = new ArrayList<>();
mCustomerList = new ArrayList<>();
// 分割集合
for (ProcessInfo processInfo : mProcessInfoList) {
if (processInfo.isSystem()){
// 系统进程
mSystemList.add(processInfo);
}else {
// 用户进程
mCustomerList.add(processInfo);
}
}
// 3.发送空消息进行数据绑定
mHandler.sendEmptyMessage(0);
}
}.start();
}
}
9.进程管理——点击过程中选中状态切换
修改ProcessManagerActivity,给列表控件注册点击事件,完善相应逻辑,代码如下:
package com.example.mobilesafe.activity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.example.mobilesafe.R;
import com.example.mobilesafe.dao.ProcessInfoDao;
import com.example.mobilesafe.domain.ProcessInfo;
import java.util.ArrayList;
import java.util.List;
public class ProcessManagerActivity extends AppCompatActivity {
private TextView tv_process_count;
private TextView tv_memory_info;
private TextView tv_title_type;
private ListView lv_process_list;
private Button btn_all;
private Button btn_reverse;
private Button btn_clear;
private Button btn_setting;
private int mProcessCount; // 进程总数
private List<ProcessInfo> mProcessInfoList;
private List<ProcessInfo> mSystemList;
private List<ProcessInfo> mCustomerList;
private MyAdapter mAdapter;
private ProcessInfo mProcessInfo;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
mAdapter = new MyAdapter();
lv_process_list.setAdapter(mAdapter);
if (tv_title_type != null && mCustomerList != null ){
tv_title_type.setText("用户进程("+mCustomerList.size()+")");
}
}
};
class MyAdapter extends BaseAdapter {
// 在ListView中多添加一种类型条目,条目总数有2种
@Override
public int getViewTypeCount() {
return super.getViewTypeCount() + 1;
}
// 根据索引值决定展示哪种类型的条目
@Override
public int getItemViewType(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return 0;
}else {
// 图文条目
return 1;
}
}
@Override
public int getCount() {
return mCustomerList.size() + mSystemList.size() + 2;
}
@Override
public ProcessInfo getItem(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return null;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
return mCustomerList.get(position - 1);
}else {
// 系统应用
return mSystemList.get(position - mCustomerList.size() - 2);
}
}
// return mAppInfoList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 判断当前索引指向的条目类型状态码
int itemViewType = getItemViewType(position);
if (itemViewType == 0){
// 纯文本条目
ViewTitleHolder holder = null;
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_app_item_text,null);
holder = new ViewTitleHolder();
holder.tv_title_des = convertView.findViewById(R.id.tv_title_des);
convertView.setTag(holder);
}else {
holder = (ViewTitleHolder) convertView.getTag();
}
if (position == 0){
holder.tv_title_des.setText("用户进程("+mCustomerList.size()+")");
}else {
holder.tv_title_des.setText("系统进程("+mSystemList.size()+")");
}
return convertView;
}else {
// 图文条目
ViewHolder holder = null;
// 1.判断ConverView是否为空
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_process_item,null);
// 2.获取控件实例赋值给ViewHolder
holder = new ViewHolder();
holder.iv_icon_application = convertView.findViewById(R.id.iv_icon_application);
holder.tv_app_name = convertView.findViewById(R.id.tv_app_name);
holder.tv_memory = convertView.findViewById(R.id.tv_memory);
holder.cb_box = convertView.findViewById(R.id.cb_box);
// 3.将Holder放到ConverView上
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
// 4.获取Holder中的控件实例,赋值
holder.iv_icon_application.setBackground(getItem(position).getIcon());
holder.tv_app_name.setText(getItem(position).getName());
String strMemorySize = Formatter.formatFileSize(getApplicationContext(), getItem(position).getMemSize());
holder.tv_memory.setText(strMemorySize);
// 5.本进程无法被选中,所以先将CheckBox隐藏
if ((getItem(position).getPackageName()).equals(getPackageName())){
holder.cb_box.setVisibility(View.GONE);
}else {
holder.cb_box.setVisibility(View.VISIBLE);
}
holder.cb_box.setChecked(getItem(position).isCheck());
// 6.返回现有条目填充上数据的View对象
return convertView;
}
}
}
static class ViewHolder{
ImageView iv_icon_application;
TextView tv_app_name;
TextView tv_memory;
CheckBox cb_box;
}
static class ViewTitleHolder{
TextView tv_title_des;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_process_manager);
// 初始化UI
initUI();
// 初始化标题上的信息:进程总数,内存使用情况
initTitleData();
// 初始化ListView列表中的数据
initListData();
}
/**
* 初始化UI
*/
private void initUI() {
tv_process_count = findViewById(R.id.tv_process_count);
tv_memory_info = findViewById(R.id.tv_memory_info);
tv_title_type = findViewById(R.id.tv_title_type);
lv_process_list = findViewById(R.id.lv_process_list);
btn_all = findViewById(R.id.btn_all);
btn_reverse = findViewById(R.id.btn_reverse);
btn_clear = findViewById(R.id.btn_clear);
btn_setting = findViewById(R.id.btn_setting);
// 给ListView注册滚动事件
lv_process_list.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// 在滚动过程中调用方法
if (mSystemList != null && mCustomerList != null){
if (firstVisibleItem >= mCustomerList.size() + 1){
// 滚动到了系统应用
tv_title_type.setText("系统进程("+mSystemList.size()+")");
}else {
// 滚动到了用户应用
tv_title_type.setText("用户进程("+mCustomerList.size()+")");
}
}
}
});
// 给ListView注册点击事件
lv_process_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
mProcessInfo = mCustomerList.get(position - 1);
}else {
// 系统应用
mProcessInfo = mSystemList.get(position - mCustomerList.size() - 2);
}
if (mProcessInfo != null){
if (!mProcessInfo.getPackageName().equals(getPackageName())){
// 选中条目指向的对象和本应用的包名不一致,才需要去取反状态和设置单选框状态
// 状态取反
mProcessInfo.setCheck(!mProcessInfo.isCheck());
// CheckBox显示状态取反
// 通过选中条目的view对象,findViewById找到此条目指向的cb_box,然后切换其状态
CheckBox cb_box = view.findViewById(R.id.cb_box);
cb_box.setChecked(mProcessInfo.isCheck());
}
}
}
}
});
btn_all.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_reverse.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_setting.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
/**
* 初始化标题上的信息:进程总数,内存使用情况
*/
private void initTitleData() {
// 获取进程总数
mProcessCount = ProcessInfoDao.getProcessCount(this);
tv_process_count.setText("进程总数:" + mProcessCount);
// 获取可用内存大小
long availSpace = ProcessInfoDao.getAvailSpace(this);
String strAvailSpace = Formatter.formatFileSize(this, availSpace); // 格式化
// 获取总内存大小
long totalSpace = ProcessInfoDao.getTotalSpace(this);
String strTotalSpace = Formatter.formatFileSize(this, totalSpace); // 格式化
tv_memory_info.setText("剩余/总共" + strAvailSpace + "/" + strTotalSpace);
}
/**
* 初始化列表中的数据
*/
private void initListData() {
getData();
}
/**
* 获取数据的方法
*/
private void getData(){
new Thread(){
@Override
public void run() {
// 2.准备填充ListView中数据适配器的数据
mProcessInfoList = ProcessInfoDao.getProcessInfoList(getApplicationContext());
mSystemList = new ArrayList<>();
mCustomerList = new ArrayList<>();
// 分割集合
for (ProcessInfo processInfo : mProcessInfoList) {
if (processInfo.isSystem()){
// 系统进程
mSystemList.add(processInfo);
}else {
// 用户进程
mCustomerList.add(processInfo);
}
}
// 3.发送空消息进行数据绑定
mHandler.sendEmptyMessage(0);
}
}.start();
}
}
10.进程管理——全选 & 反选
上一节中我们完成了选择某个条目时CheckBox
控件发生变化的效果,现在需要完成全选 & 反选整个条目的功能。
修改ProcessManagerActivity,完善之前注册的按钮点击事件,主要添加selectAll()和selectReverse()方法,并完善相应逻辑,代码如下:
package com.example.mobilesafe.activity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.example.mobilesafe.R;
import com.example.mobilesafe.dao.ProcessInfoDao;
import com.example.mobilesafe.domain.ProcessInfo;
import java.util.ArrayList;
import java.util.List;
public class ProcessManagerActivity extends AppCompatActivity {
private TextView tv_process_count;
private TextView tv_memory_info;
private TextView tv_title_type;
private ListView lv_process_list;
private Button btn_all;
private Button btn_reverse;
private Button btn_clear;
private Button btn_setting;
private int mProcessCount; // 进程总数
private List<ProcessInfo> mProcessInfoList;
private List<ProcessInfo> mSystemList;
private List<ProcessInfo> mCustomerList;
private MyAdapter mAdapter;
private ProcessInfo mProcessInfo;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
mAdapter = new MyAdapter();
lv_process_list.setAdapter(mAdapter);
if (tv_title_type != null && mCustomerList != null ){
tv_title_type.setText("用户进程("+mCustomerList.size()+")");
}
}
};
class MyAdapter extends BaseAdapter {
// 在ListView中多添加一种类型条目,条目总数有2种
@Override
public int getViewTypeCount() {
return super.getViewTypeCount() + 1;
}
// 根据索引值决定展示哪种类型的条目
@Override
public int getItemViewType(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return 0;
}else {
// 图文条目
return 1;
}
}
@Override
public int getCount() {
return mCustomerList.size() + mSystemList.size() + 2;
}
@Override
public ProcessInfo getItem(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return null;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
return mCustomerList.get(position - 1);
}else {
// 系统应用
return mSystemList.get(position - mCustomerList.size() - 2);
}
}
// return mAppInfoList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 判断当前索引指向的条目类型状态码
int itemViewType = getItemViewType(position);
if (itemViewType == 0){
// 纯文本条目
ViewTitleHolder holder = null;
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_app_item_text,null);
holder = new ViewTitleHolder();
holder.tv_title_des = convertView.findViewById(R.id.tv_title_des);
convertView.setTag(holder);
}else {
holder = (ViewTitleHolder) convertView.getTag();
}
if (position == 0){
holder.tv_title_des.setText("用户进程("+mCustomerList.size()+")");
}else {
holder.tv_title_des.setText("系统进程("+mSystemList.size()+")");
}
return convertView;
}else {
// 图文条目
ViewHolder holder = null;
// 1.判断ConverView是否为空
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_process_item,null);
// 2.获取控件实例赋值给ViewHolder
holder = new ViewHolder();
holder.iv_icon_application = convertView.findViewById(R.id.iv_icon_application);
holder.tv_app_name = convertView.findViewById(R.id.tv_app_name);
holder.tv_memory = convertView.findViewById(R.id.tv_memory);
holder.cb_box = convertView.findViewById(R.id.cb_box);
// 3.将Holder放到ConverView上
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
// 4.获取Holder中的控件实例,赋值
holder.iv_icon_application.setBackground(getItem(position).getIcon());
holder.tv_app_name.setText(getItem(position).getName());
String strMemorySize = Formatter.formatFileSize(getApplicationContext(), getItem(position).getMemSize());
holder.tv_memory.setText(strMemorySize);
// 5.本进程无法被选中,所以先将CheckBox隐藏
if ((getItem(position).getPackageName()).equals(getPackageName())){
holder.cb_box.setVisibility(View.GONE);
}else {
holder.cb_box.setVisibility(View.VISIBLE);
}
holder.cb_box.setChecked(getItem(position).isCheck());
// 6.返回现有条目填充上数据的View对象
return convertView;
}
}
}
static class ViewHolder{
ImageView iv_icon_application;
TextView tv_app_name;
TextView tv_memory;
CheckBox cb_box;
}
static class ViewTitleHolder{
TextView tv_title_des;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_process_manager);
// 初始化UI
initUI();
// 初始化标题上的信息:进程总数,内存使用情况
initTitleData();
// 初始化ListView列表中的数据
initListData();
}
/**
* 初始化UI
*/
private void initUI() {
tv_process_count = findViewById(R.id.tv_process_count);
tv_memory_info = findViewById(R.id.tv_memory_info);
tv_title_type = findViewById(R.id.tv_title_type);
lv_process_list = findViewById(R.id.lv_process_list);
btn_all = findViewById(R.id.btn_all);
btn_reverse = findViewById(R.id.btn_reverse);
btn_clear = findViewById(R.id.btn_clear);
btn_setting = findViewById(R.id.btn_setting);
// 给ListView注册滚动事件
lv_process_list.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// 在滚动过程中调用方法
if (mSystemList != null && mCustomerList != null){
if (firstVisibleItem >= mCustomerList.size() + 1){
// 滚动到了系统应用
tv_title_type.setText("系统进程("+mSystemList.size()+")");
}else {
// 滚动到了用户应用
tv_title_type.setText("用户进程("+mCustomerList.size()+")");
}
}
}
});
// 给ListView注册点击事件
lv_process_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
mProcessInfo = mCustomerList.get(position - 1);
}else {
// 系统应用
mProcessInfo = mSystemList.get(position - mCustomerList.size() - 2);
}
if (mProcessInfo != null){
if (!mProcessInfo.getPackageName().equals(getPackageName())){
// 选中条目指向的对象和本应用的包名不一致,才需要去取反状态和设置单选框状态
// 状态取反
mProcessInfo.setCheck(!mProcessInfo.isCheck());
// CheckBox显示状态取反
// 通过选中条目的view对象,findViewById找到此条目指向的cb_box,然后切换其状态
CheckBox cb_box = view.findViewById(R.id.cb_box);
cb_box.setChecked(mProcessInfo.isCheck());
}
}
}
}
});
btn_all.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 全选
selectAll();
}
});
btn_reverse.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 反选
selectReverse();
}
});
btn_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
btn_setting.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
/**
* “全选”按钮的功能
*/
private void selectAll() {
// 1.将所有集合中的对象上的isCheck字段设置为true代表全选(除了手机卫士自己)
for (ProcessInfo processInfo : mCustomerList){
if (processInfo.getPackageName().equals(getPackageName())){
continue;
}
processInfo.setCheck(true);
}
for (ProcessInfo processInfo : mSystemList){
processInfo.setCheck(true);
}
// 2.通知数据适配器刷新
if (mAdapter != null){
mAdapter.notifyDataSetChanged();
}
}
/**
* “反选”按钮的功能
*/
private void selectReverse() {
// 1.将所有集合中的对象上的isCheck字段设置为true代表全选(除了手机卫士自己)
for (ProcessInfo processInfo : mCustomerList){
if (processInfo.getPackageName().equals(getPackageName())){
continue;
}
processInfo.setCheck(!processInfo.isCheck());
}
for (ProcessInfo processInfo : mSystemList){
processInfo.setCheck(!processInfo.isCheck());
}
// 2.通知数据适配器刷新
if (mAdapter != null){
mAdapter.notifyDataSetChanged();
}
}
/**
* 初始化标题上的信息:进程总数,内存使用情况
*/
private void initTitleData() {
// 获取进程总数
mProcessCount = ProcessInfoDao.getProcessCount(this);
tv_process_count.setText("进程总数:" + mProcessCount);
// 获取可用内存大小
long availSpace = ProcessInfoDao.getAvailSpace(this);
String strAvailSpace = Formatter.formatFileSize(this, availSpace); // 格式化
// 获取总内存大小
long totalSpace = ProcessInfoDao.getTotalSpace(this);
String strTotalSpace = Formatter.formatFileSize(this, totalSpace); // 格式化
tv_memory_info.setText("剩余/总共" + strAvailSpace + "/" + strTotalSpace);
}
/**
* 初始化列表中的数据
*/
private void initListData() {
getData();
}
/**
* 获取数据的方法
*/
private void getData(){
new Thread(){
@Override
public void run() {
// 2.准备填充ListView中数据适配器的数据
mProcessInfoList = ProcessInfoDao.getProcessInfoList(getApplicationContext());
mSystemList = new ArrayList<>();
mCustomerList = new ArrayList<>();
// 分割集合
for (ProcessInfo processInfo : mProcessInfoList) {
if (processInfo.isSystem()){
// 系统进程
mSystemList.add(processInfo);
}else {
// 用户进程
mCustomerList.add(processInfo);
}
}
// 3.发送空消息进行数据绑定
mHandler.sendEmptyMessage(0);
}
}.start();
}
}
11.进程管理——清理内存
上一节中我们完成了全选 & 反选整个条目的功能,现在需要完成一键清理内存的功能。
修改ProcessManagerActivity,为“清理内存”的按钮注册点击事件,完善相应逻辑,代码如下:
package com.example.mobilesafe.activity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.example.mobilesafe.R;
import com.example.mobilesafe.dao.ProcessInfoDao;
import com.example.mobilesafe.domain.ProcessInfo;
import com.example.mobilesafe.utils.ToastUtil;
import java.util.ArrayList;
import java.util.List;
public class ProcessManagerActivity extends AppCompatActivity {
private TextView tv_process_count;
private TextView tv_memory_info;
private TextView tv_title_type;
private ListView lv_process_list;
private Button btn_all;
private Button btn_reverse;
private Button btn_clear;
private Button btn_setting;
private int mProcessCount; // 进程总数
private List<ProcessInfo> mProcessInfoList;
private List<ProcessInfo> mSystemList;
private List<ProcessInfo> mCustomerList;
private MyAdapter mAdapter;
private ProcessInfo mProcessInfo;
private long mAvailSpace;
private long mTotalSpace;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
mAdapter = new MyAdapter();
lv_process_list.setAdapter(mAdapter);
if (tv_title_type != null && mCustomerList != null ){
tv_title_type.setText("用户进程("+mCustomerList.size()+")");
}
}
};
class MyAdapter extends BaseAdapter {
// 在ListView中多添加一种类型条目,条目总数有2种
@Override
public int getViewTypeCount() {
return super.getViewTypeCount() + 1;
}
// 根据索引值决定展示哪种类型的条目
@Override
public int getItemViewType(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return 0;
}else {
// 图文条目
return 1;
}
}
@Override
public int getCount() {
return mCustomerList.size() + mSystemList.size() + 2;
}
@Override
public ProcessInfo getItem(int position) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return null;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
return mCustomerList.get(position - 1);
}else {
// 系统应用
return mSystemList.get(position - mCustomerList.size() - 2);
}
}
// return mAppInfoList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 判断当前索引指向的条目类型状态码
int itemViewType = getItemViewType(position);
if (itemViewType == 0){
// 纯文本条目
ViewTitleHolder holder = null;
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_app_item_text,null);
holder = new ViewTitleHolder();
holder.tv_title_des = convertView.findViewById(R.id.tv_title_des);
convertView.setTag(holder);
}else {
holder = (ViewTitleHolder) convertView.getTag();
}
if (position == 0){
holder.tv_title_des.setText("用户进程("+mCustomerList.size()+")");
}else {
holder.tv_title_des.setText("系统进程("+mSystemList.size()+")");
}
return convertView;
}else {
// 图文条目
ViewHolder holder = null;
// 1.判断ConverView是否为空
if (convertView == null){
convertView = View.inflate(getApplicationContext(),R.layout.list_process_item,null);
// 2.获取控件实例赋值给ViewHolder
holder = new ViewHolder();
holder.iv_icon_application = convertView.findViewById(R.id.iv_icon_application);
holder.tv_app_name = convertView.findViewById(R.id.tv_app_name);
holder.tv_memory = convertView.findViewById(R.id.tv_memory);
holder.cb_box = convertView.findViewById(R.id.cb_box);
// 3.将Holder放到ConverView上
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
// 4.获取Holder中的控件实例,赋值
holder.iv_icon_application.setBackground(getItem(position).getIcon());
holder.tv_app_name.setText(getItem(position).getName());
String strMemorySize = Formatter.formatFileSize(getApplicationContext(), getItem(position).getMemSize());
holder.tv_memory.setText(strMemorySize);
// 5.本进程无法被选中,所以先将CheckBox隐藏
if ((getItem(position).getPackageName()).equals(getPackageName())){
holder.cb_box.setVisibility(View.GONE);
}else {
holder.cb_box.setVisibility(View.VISIBLE);
}
holder.cb_box.setChecked(getItem(position).isCheck());
// 6.返回现有条目填充上数据的View对象
return convertView;
}
}
}
static class ViewHolder{
ImageView iv_icon_application;
TextView tv_app_name;
TextView tv_memory;
CheckBox cb_box;
}
static class ViewTitleHolder{
TextView tv_title_des;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_process_manager);
// 初始化UI
initUI();
// 初始化标题上的信息:进程总数,内存使用情况
initTitleData();
// 初始化ListView列表中的数据
initListData();
}
/**
* 初始化UI
*/
private void initUI() {
tv_process_count = findViewById(R.id.tv_process_count);
tv_memory_info = findViewById(R.id.tv_memory_info);
tv_title_type = findViewById(R.id.tv_title_type);
lv_process_list = findViewById(R.id.lv_process_list);
btn_all = findViewById(R.id.btn_all);
btn_reverse = findViewById(R.id.btn_reverse);
btn_clear = findViewById(R.id.btn_clear);
btn_setting = findViewById(R.id.btn_setting);
// 给ListView注册滚动事件
lv_process_list.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// 在滚动过程中调用方法
if (mSystemList != null && mCustomerList != null){
if (firstVisibleItem >= mCustomerList.size() + 1){
// 滚动到了系统应用
tv_title_type.setText("系统进程("+mSystemList.size()+")");
}else {
// 滚动到了用户应用
tv_title_type.setText("用户进程("+mCustomerList.size()+")");
}
}
}
});
// 给ListView注册点击事件
lv_process_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == 0 || position == mCustomerList.size() + 1){
// 纯文本条目
return;
}else {
// 图文条目
if (position < mCustomerList.size() + 1){
// 用户应用
mProcessInfo = mCustomerList.get(position - 1);
}else {
// 系统应用
mProcessInfo = mSystemList.get(position - mCustomerList.size() - 2);
}
if (mProcessInfo != null){
if (!mProcessInfo.getPackageName().equals(getPackageName())){
// 选中条目指向的对象和本应用的包名不一致,才需要去取反状态和设置单选框状态
// 状态取反
mProcessInfo.setCheck(!mProcessInfo.isCheck());
// CheckBox显示状态取反
// 通过选中条目的view对象,findViewById找到此条目指向的cb_box,然后切换其状态
CheckBox cb_box = view.findViewById(R.id.cb_box);
cb_box.setChecked(mProcessInfo.isCheck());
}
}
}
}
});
btn_all.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 全选
selectAll();
}
});
btn_reverse.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 反选
selectReverse();
}
});
btn_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
clearAll();
}
});
btn_setting.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
/**
* "一键清理"按钮的功能
*/
private void clearAll() {
// 0.创建一个记录释放了多大空间的变量
long totalReleaseSpace = 0;
// 1.创建一个记录需要杀死的进程的集合
List<ProcessInfo> killProcessList = new ArrayList<>();
// 2.获取选中进程
for (ProcessInfo processInfo : mCustomerList){
if (processInfo.getPackageName().equals(getPackageName())){
continue;
}
if (processInfo.isCheck()){
// 3.记录被选中的用户进程(即需要杀死)
killProcessList.add(processInfo);
}
}
for (ProcessInfo processInfo : mSystemList){
if (processInfo.isCheck()){
// 4.记录被选中的系统进程(即需要杀死)
killProcessList.add(processInfo);
}
}
// 5.循环遍历killProcessList,然后移除mCustomerList和mSystemList中的对象
for (ProcessInfo processInfo : killProcessList) {
// 6.判断当前进程再哪个集合中,从所在集合中移除
if (mCustomerList.contains(processInfo)){
mCustomerList.remove(processInfo);
}
if (mSystemList.contains(processInfo)){
mSystemList.remove(processInfo);
}
// 7.杀死记录在killProcessList中的进程
ProcessInfoDao.killProcess(this,processInfo);
// 8.记录释放控件的总大小
totalReleaseSpace += processInfo.getMemSize();
}
// 9.在集合改变后,需要通知数据适配器刷新
if (mAdapter != null){
mAdapter.notifyDataSetChanged();
}
// 10.更新进程总数
mProcessCount -= killProcessList.size();
// 11.更新可用剩余空间(释放空间 + 原有剩余空间 = 当前剩余空间)
mAvailSpace += totalReleaseSpace;
// 12.根据进程总数和剩余空间大小更新控件
tv_process_count.setText("进程总数:"+mProcessCount);
tv_memory_info.setText("剩余/总共" + Formatter.formatFileSize(this,mAvailSpace) + "/" + Formatter.formatFileSize(this,mTotalSpace));
// 13.通过Toast告知用户,释放了多少空间,杀死多少进程(也可以使用C语言的占位符)
ToastUtil.show(getApplicationContext(),"杀死了" + killProcessList.size() + "个进程,释放了" + Formatter.formatFileSize(this,totalReleaseSpace));
}
/**
* “全选”按钮的功能
*/
private void selectAll() {
// 1.将所有集合中的对象上的isCheck字段设置为true代表全选(除了手机卫士自己)
for (ProcessInfo processInfo : mCustomerList){
if (processInfo.getPackageName().equals(getPackageName())){
continue;
}
processInfo.setCheck(true);
}
for (ProcessInfo processInfo : mSystemList){
processInfo.setCheck(true);
}
// 2.通知数据适配器刷新
if (mAdapter != null){
mAdapter.notifyDataSetChanged();
}
}
/**
* “反选”按钮的功能
*/
private void selectReverse() {
// 1.将所有集合中的对象上的isCheck字段设置为true代表全选(除了手机卫士自己)
for (ProcessInfo processInfo : mCustomerList){
if (processInfo.getPackageName().equals(getPackageName())){
continue;
}
processInfo.setCheck(!processInfo.isCheck());
}
for (ProcessInfo processInfo : mSystemList){
processInfo.setCheck(!processInfo.isCheck());
}
// 2.通知数据适配器刷新
if (mAdapter != null){
mAdapter.notifyDataSetChanged();
}
}
/**
* 初始化标题上的信息:进程总数,内存使用情况
*/
private void initTitleData() {
// 获取进程总数
mProcessCount = ProcessInfoDao.getProcessCount(this);
tv_process_count.setText("进程总数:" + mProcessCount);
// 获取可用内存大小
mAvailSpace = ProcessInfoDao.getAvailSpace(this);
String strAvailSpace = Formatter.formatFileSize(this, mAvailSpace); // 格式化
// 获取总内存大小
mTotalSpace = ProcessInfoDao.getTotalSpace(this);
String strTotalSpace = Formatter.formatFileSize(this, mTotalSpace); // 格式化
tv_memory_info.setText("剩余/总共" + strAvailSpace + "/" + strTotalSpace);
}
/**
* 初始化列表中的数据
*/
private void initListData() {
getData();
}
/**
* 获取数据的方法
*/
private void getData(){
new Thread(){
@Override
public void run() {
// 2.准备填充ListView中数据适配器的数据
mProcessInfoList = ProcessInfoDao.getProcessInfoList(getApplicationContext());
mSystemList = new ArrayList<>();
mCustomerList = new ArrayList<>();
// 分割集合
for (ProcessInfo processInfo : mProcessInfoList) {
if (processInfo.isSystem()){
// 系统进程
mSystemList.add(processInfo);
}else {
// 用户进程
mCustomerList.add(processInfo);
}
}
// 3.发送空消息进行数据绑定
mHandler.sendEmptyMessage(0);
}
}.start();
}
}
为了方便进程的删除,修改ProcessInfoDao,添加killProcess(),作为杀死进程的方法,代码如下:
/**
* 杀死进程的方法
* @param context 上下文环境
* @param processInfo 传递进来的进程信息
*/
public static void killProcess(Context context,ProcessInfo processInfo) {
// 1.获取ActivityManager对象
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
// 2.杀死指定包名进程(需要声明权限)
activityManager.killBackgroundProcesses(processInfo.getPackageName());
}
注意,由于涉及到后台权限的删除,所以需要在清单文件中声明相应权限,代码如下:
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />