Android项目:手机安全卫士(16)—— 复杂 ListView浅析
Android项目:手机安全卫士(16)—— 复杂 ListView
1 介绍
接着昨天的内容,今天继续完善应用列表,首先,应用分为系统应用和用户应用,安装位置分为手机内存和 sdcard,所以,我们在 ListView 中添加一个分类,分为系统应用和用户应用,每一个 item 显示安装的位置,最终效果如下所示:
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 installedApp = getInstalledApp();
ArrayList userList = new ArrayList();
ArrayList systemList = new ArrayList();
//拆分成两个列表
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 mUserList;
private final ArrayList mSystemList;
private final Context mContext;
public AppManagerAdapter(Context ctx, ArrayList userList, ArrayList 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。最好的效果如图:
4 ListView 标题栏悬浮
每当标题栏滑动到顶端时,都会有一个悬浮效果,表示以下 item 都是属于该标题类型,其实这只是一种障眼法,那个标题栏一直都在,只是在适当的时候修改它的显示文字即可,需要修改 AppManagerActivity 的布局文件,将 ListView 放在一个 FrameLayout 布局中,然后添加一个 TextView 覆盖在上面,同时增加一个加载数据提示进度条,代码如下:
<framelayout android:layout_height="match_parent" android:layout_width="match_p
您可能想查找下面的文章:
- Android 手机卫士17--缓存清理,android17--
- Android 手机卫士13--进程设置,android13--
- Android 手机卫士10--应用管理器,android10--
- Android 手机卫士8--删除通话记录,android8--
- Android 手机卫士--导航界面4的业务逻辑,android卫士
- Android 手机卫士--平移动画实现,android卫士
- Android 手机卫士--获取联系人信息并显示与回显,android回显
- Android 手机卫士--参照文档编写选择器,android选择器
- Android 手机卫士--导航界面2,android卫士
- Android 手机卫士--设置界面&功能列表界面跳转逻辑处理,android卫士--界面