Android項目:手機安全衛士(16)—— 複雜 ListView淺析

來源:互聯網
上載者:User

Android項目:手機安全衛士(16)—— 複雜 ListView淺析
Android項目:手機安全衛士(16)—— 複雜 ListView1 介紹

接著昨天的內容,今天繼續完善應用列表,首先,應用分為系統應用和使用者應用,安裝位置分為手機記憶體和 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_parent">                                                            </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() + ")");                }            }        }    });

效果如下:

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 installedApp = getInstalledApp();                mUserList = new ArrayList();                mSystemList = new ArrayList();                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 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.