Android项目:手机安全卫士(16)—— 复杂 ListView

Android项目:手机安全卫士(16)—— 复杂 ListView

1 介绍

接着昨天的内容,今天继续完善应用列表,首先,应用分为系统应用和用户应用,安装位置分为手机内存和 sdcard,所以,我们在 ListView 中添加一个分类,分为系统应用和用户应用,每一个 item 显示安装的位置,最终效果如下所示:

ListView标题栏悬浮

关于项目相关文章,请访问:

项目源码地址(实时更新):https://github.com/xwdoor/MobileSafe

2 判断应用类型和安装位置

ApplicationInfo 对象有个 flags 属性,它有很多个状态值,我们用它和 FLAG_EXTERNAL_STORAGE、FLAG_SYSTEM 进行与操作,从而判断是否具有该状态值,代码如下:


    int flag = applicationInfo.flags;
    if((flag&ApplicationInfo.FLAG_EXTERNAL_STORAGE)==ApplicationInfo.FLAG_EXTERNAL_STORAGE){
        //安装在sdcard中
        appInfo.isSdcardApp = true;
    }else {
        //安装在手机内存
        appInfo.isSdcardApp = false;
    }

    if((flag & ApplicationInfo.FLAG_SYSTEM)==ApplicationInfo.FLAG_SYSTEM){
        //系统应用
        appInfo.isUserApp = false;
    }else {
        //用户应用
        appInfo.isUserApp = true;
    }

然后在 AppManagerAdapter 的 getView() 方法中进行判断并赋值,代码如下:


    if (appInfo.isSdcardApp) {
        holder.tvLocation.setText("外置存储器");
    } else {
        holder.tvLocation.setText("手机内存");
    }

3 ListView 添加两种布局

接下来的就是今天的重点了,给 ListView 添加第二种 item 布局,将应用分为两类:系统应用和用户应用,并且用两个列表来存储,代码如下:


    ArrayList<AppInfo> installedApp = getInstalledApp();
    ArrayList<AppInfo> userList = new ArrayList<AppInfo>();
    ArrayList<AppInfo> systemList = new ArrayList<AppInfo>();
    //拆分成两个列表
    for (AppInfo info : installedApp) {
        if (info.isUserApp) {
            userList.add(info);
        } else {
            systemList.add(info);
        }
    }

给 ListView 添加两种及以上布局,需要重写 Adapter 的两个方法:getViewTypeCount()、getItemViewType(),分别返回布局类型的数量,以及当前 item 的类型,对 AppManagerAdapter 的代码修改如下:


    /**
     * 应用列表适配器
     *
     * Created by XWdoor on 2016/3/22 022 11:44.
     * 博客:http://blog.csdn.net/xwdoor
     */
    public class AppManagerAdapter extends BaseAdapter {
        private final ArrayList<AppInfo> mUserList;
        private final ArrayList<AppInfo> mSystemList;
        private final Context mContext;

        public AppManagerAdapter(Context ctx, ArrayList<AppInfo> userList, ArrayList<AppInfo> systemList) {
            this.mContext = ctx;
            this.mUserList = userList;
            this.mSystemList = systemList;
        }

        @Override
        public int getCount() {
            return mUserList.size() + mSystemList.size() + 2;//增加两个标题栏
        }

        @Override
        public AppInfo getItem(int position) {
            // 遇到标题栏,直接返回null
            if (position == 0 || position == mUserList.size() + 1) {
                return null;
            }

            if (position < mUserList.size() + 1) {
                return mUserList.get(position - 1);//去掉标题栏的占位
            } else {
                return mSystemList.get(position - mUserList.size() - 2);//需要减掉两个标题栏的占位
            }
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        // 返回布局类型的个数, 就会缓存两种convertView
        @Override
        public int getViewTypeCount() {
            return 2;
        }

        // 根据当前位置,返回相应布局类型, 必须从0开始计算
        @Override
        public int getItemViewType(int position) {
            if (position == 0 || position == mUserList.size() + 1) {// 遇到标题栏
                return 0;
            } else {
                return 1;
            }
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // 首先要判断当前布局类型, 系统会缓存多种convertView, 然后根据当前布局类型,返回对应的convertView
            // 根据类型,加载不同布局
            switch (getItemViewType(position)){
                case 0://标题
                    HeaderHolder headerHolder = null;
                    if(convertView==null){
                        convertView = View.inflate(mContext,R.layout.item_app_manager_header,null);
                        headerHolder = new HeaderHolder();
                        headerHolder.tvHeader = (TextView) convertView.findViewById(R.id.tv_header);

                        convertView.setTag(headerHolder);
                    }else {
                        headerHolder = (HeaderHolder) convertView.getTag();
                    }

                    if(position == 0){
                        headerHolder.tvHeader.setText("用户应用(" + mUserList.size() + ")");
                    }else {
                        headerHolder.tvHeader.setText("系统应用(" + mSystemList.size() + ")");
                    }
                    break;
                case 1://应用item
                    ViewHolder holder = null;
                    if (convertView == null) {
                        convertView = View.inflate(mContext, R.layout.item_app_manager_adapter, null);
                        holder = new ViewHolder();
                        holder.tvName = (TextView) convertView.findViewById(R.id.tv_name);
                        holder.tvLocation = (TextView) convertView.findViewById(R.id.tv_location);
                        holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
                        convertView.setTag(holder);
                    } else {
                        holder = (ViewHolder) convertView.getTag();
                    }
                    AppInfo appInfo = getItem(position);
                    holder.tvName.setText(appInfo.appName);
                    holder.ivIcon.setImageDrawable(appInfo.icon);
                    if (appInfo.isSdcardApp) {
                        holder.tvLocation.setText("外置存储器");
                    } else {
                        holder.tvLocation.setText("手机内存");
                    }
                    break;
            }

            return convertView;
        }

        static class ViewHolder {
            public TextView tvName;
            public ImageView ivIcon;
            public TextView tvLocation;
        }

        static class HeaderHolder{
            public TextView tvHeader;
        }
    }

首先是构造函数需要传入两个应用列表,getCount() 方法需要返回两个列表总数再加上两个标题栏;getItem() 方法根据情况返回,若是标题栏,则返回 null,若是具体的某个 item,需要去掉标题栏的占位;同理,在 getView() 方法中,需要根据不同的类型,加载不同的布局,这里需要说一下关于 View 的复用问题,根据 getViewTypeCount() 方法的返回值,系统会保存多种 convertView, 然后根据当前布局类型,返回对应的 convertView。最好的效果如图:

复杂ListView-标题栏

4 ListView 标题栏悬浮

每当标题栏滑动到顶端时,都会有一个悬浮效果,表示以下 item 都是属于该标题类型,其实这只是一种障眼法,那个标题栏一直都在,只是在适当的时候修改它的显示文字即可,需要修改 AppManagerActivity 的布局文件,将 ListView 放在一个 FrameLayout 布局中,然后添加一个 TextView 覆盖在上面,同时增加一个加载数据提示进度条,代码如下:


    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <ListView
            android:id="@+id/lv_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <TextView
            android:id="@+id/tv_header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/holo_blue_dark"
            android:padding="5dp"
            android:text="用户应用(0)"
            android:textColor="#fff"
            android:textSize="16sp" />

        <LinearLayout
            android:id="@+id/ll_loading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center"
            android:orientation="vertical" >

            <ProgressBar
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="正在加载,请稍候..." />
        </LinearLayout>
    </FrameLayout>

然后添加 ListView 的滚动监听,用于更改标题栏的文字显示,代码如下:


    final TextView tvHeader = (TextView) findViewById(R.id.tv_header);

    lvList.setOnScrollListener(new AbsListView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {

        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            // firstVisibleItem 第一个可见元素位置
            // visibleItemCount 可见元素数量
            // totalItemCount 元素总数
            if (mUserList != null && mSystemList != null) {
                if (firstVisibleItem >= mUserList.size() + 1) {//算上标题栏占位
                    tvHeader.setText("系统应用(" + mSystemList.size() + ")");
                } else {
                    tvHeader.setText("用户应用(" + mUserList.size() + ")");
                }
            }
        }
    });

效果如下:

ListView标题栏悬浮

5 在线程中加载数据

获取手机所有安装的应用是一个耗时操作,所以我们需要在线程中加载数据,为了用户体验,防止白屏出现,所以加上了一个进度条,代码如下:


    @Override
    protected void loadData() {
        llLoading.setVisibility(View.VISIBLE);
        //在线程中加载数据
        new Thread() {
            @Override
            public void run() {
                String availRom = getAvailSpace(Environment.getExternalStorageDirectory().getAbsolutePath());
                String availSdcard = getAvailSpace(Environment.getDataDirectory().getAbsolutePath());
                tvRomAvail.setText("内部存储可用:" + availRom);
                tvSdcardAvail.setText("sdcard可用:" + availSdcard);

                ArrayList<AppInfo> installedApp = getInstalledApp();
                mUserList = new ArrayList<AppInfo>();
                mSystemList = new ArrayList<AppInfo>();
                for (AppInfo info : installedApp) {
                    if (info.isUserApp) {
                        mUserList.add(info);
                    } else {
                        mSystemList.add(info);
                    }
                }

                //更新UI数据
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        lvList.setAdapter(new AppManagerAdapter(AppManagerActivity.this, mUserList, mSystemList));
                        llLoading.setVisibility(View.GONE);
                    }
                });
            }
        }.start();

    }

一般在线程中更新 UI,我们都会使用 Handler 来完成,但是只有一两行语句,感觉大材小用了,这里我们可以使用 runOnUiThread() 方法,它是在主线程中运行的,所以可以放心使用。

6 总结

今天的内容还是比较充实的,学到了两个知识点:

  • ListView 多种 item 的实现
  • 线程中方便的更新 UI

关于项目相关文章,请访问:

项目源码地址(实时更新):https://github.com/xwdoor/MobileSafe

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1.整体项目采用MVC框架,是对android知识点的综合应用,用到的技术有 (activity,service,broadcast,content provider,Notification , 数据库,自定义title,自定义控件,自定义toast,widget,aidl进程间通讯, javascript和java的互相调用等) 2.介绍了listview和gridview等控件的优化技巧,提升软件的效率 3.穿插介绍了企业开发中的bug管理,mantis的使用,自动化测试robotium的使用 ,log管理. 软件的打包,混淆,反编译和三方广告的加入等技巧。 该项目主要涵盖以下几大功能: 手机防盗: 根据预设参数, 判断手机是否被盗,根据自定义协议发送手机中sim卡的信息和手机的位置信息给安全号码. 可以自定义特殊号码,拨号快速进入手机防盗功能,可远程通过短信指令,给手机设置锁屏密码, 远程锁定手机屏幕, 远程格式化手机sd卡,恢复出厂设置,极大的保护用户的隐私安全,通过aidl注册admin设备,一般用户无法卸载该程序。 通讯卫士: 来电号码归属地显示,来电归属地位置的调整,来电黑名单/短信黑名单管理. 电话短信备份和还原. 保护手机的数据和通讯安全。 软件管理: 系统软件和本地软件,可以显示软件的详细信息,启动,删除应用程序.连接获取服务器上软件的评分信息等功能。程序锁可以指定要保护的程序, 用户进入要保护的程序之前必须输入密码。 任务管理: 显示当前系统运行的进程信息 显示系统内存信息, 可以杀死某个进程,批量杀死进程,通过桌面widget 实时展现用户当前的手机内存状态。 上网管理: 显示出每个程序wifi和3g/2g 访问的上传和下载的流量,帮助用户了解程序产生的流量信息. 漫游管理. 当检查到手机处于漫游状态时会提示用户。 手机杀毒:从服务器下载最新的病毒库, 根据程序的包名和程序的数字签名识别病毒,提示用户并查杀. 恶意软件,吸费木马无处可藏。 系统优化: 清理手机缓存,提高手机性能,优化电池管理 高级工具: 自动ip拨号,手机号码归属地查询,更改归属地位置,常用号码查询。 安全专题: 1、通过0权限上传下载数据,重启手机等案例,深入讲解android沙箱,安全机制和权限模型。 2、通过分析恶意代码的提权漏洞,讲解如何维护系统的安全。 3、通过linux键盘驱动案例的讲解,分析盗号木马的原理及其实现方式。 4、恶意软件发展速度的确一日千里,安全软件也要与时俱进,世面上的手机病毒已经具有了可以杀掉安全软件的功能, 通过多进程互相守护案例,讲述如何实现安全软件的自我保护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值