Android Api Demos登顶之路(四十六)Loader-->Custom

这个demo演示了如何使用自定义加载器实现对数据的管理。
* 自定义loader的基本步骤:
* 1.继承AsyncTaskLoader
* 2.定义一个观察者来接收数据源改变的通知(可以是ContentObserver也可以是BroadcastReceiver)
* 3.实现抽象方法loadInBackground,在该方法中将开启一个工作线程实行异步加载数据的操作 我们需要在这个方法中准备需要加载的数据集
* 4.重写deliverResult方法,在该方法中将数据结果传递给客户端
* 5.重写onStartLoading(), 方法在开始加载时调用此方法,在该方法中需要将数据的结果立即发送给客户端 并完成对观察者的初始化,开始监听数据源的变化。
* 6.重写onStoppLoading()方法,当loader停止时调用此方法,当loader处于停止状态,他还会继续监听数据 源的变化,却不会向客户端发送结果
* 7.重写onReset()方法。在loader的重置状态,它即不再监听数据源的变化,也不向客户端发送结果。需要在此方法中 停止数据加载、释放数据源、清空数据集、释放观察者。
* 8.重写onCanceled方法,当试图取消异步加载loader时调用此方法,在此方法中应释放数据源。
activity_main.xml

<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"
    android:orientation="horizontal"
    android:padding="5dp">

    <ImageView 
        android:id="@+id/icon"
        android:layout_width="40dp"
        android:layout_height="40dp"
        />

    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
         />

</LinearLayout>

MainActivity

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        FragmentManager fm = getFragmentManager();
        if (fm.findFragmentById(android.R.id.content) == null) {
            AppListFragment listFragment = new AppListFragment();
            fm.beginTransaction().add(android.R.id.content, listFragment)
                    .commit();
        }
    }


    public static class AppListFragment extends ListFragment implements
            OnQueryTextListener, OnCloseListener {

        private String curFilter;
        private AppListAdapter mAdapter;
        private MySearchView serachView;
        private LoaderCallbacks<List<AppEntry>> MyLoader=new LoaderCallbacks<List<AppEntry>>() {

            @Override
            public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) {
                return new AppListLoader(getActivity());
            }

            @Override
            public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) {
                mAdapter.setData(data);
                if(isResumed()){
                    setListShown(true);
                }else{
                    setListShownNoAnimation(true);
                }
            }

            @Override
            public void onLoaderReset(Loader<List<AppEntry>> loader) {
                mAdapter.setData(null);
            }
        };

        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            setEmptyText("No applications!");
            setHasOptionsMenu(true);
            mAdapter = new AppListAdapter(getActivity());
            setListAdapter(mAdapter);
            setListShown(false);
            getLoaderManager().initLoader(0, null, MyLoader);
        }

        public class MySearchView extends SearchView {

            public MySearchView(Context context) {
                super(context);
            }

            @Override
            public void onActionViewCollapsed() {
                super.onActionViewCollapsed();
                setQuery("", false);
            }
        }

        @Override
        public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
            super.onCreateOptionsMenu(menu, inflater);
            MenuItem item = menu.add("search");
            item.setIcon(android.R.drawable.ic_menu_search);
            item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
                    | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
            serachView = new MySearchView(getActivity());
            serachView.setOnQueryTextListener(this);
            serachView.setOnCloseListener(this);
            serachView.setIconifiedByDefault(true);
            item.setActionView(serachView);
        }

        @Override
        public boolean onQueryTextSubmit(String query) {
            return true;
        }

        @Override
        public boolean onQueryTextChange(String newText) {
            curFilter = TextUtils.isEmpty(newText) ? newText:null;
            mAdapter.getFilter().filter(curFilter);
            return true;
        }

        @Override
        public boolean onClose() {
            if(TextUtils.isEmpty(serachView.getQuery())){
                serachView.setQuery(null, true);
            }
            return true;
        }

        @Override
        public void onListItemClick(ListView l, View v, int position, long id) {
            super.onListItemClick(l, v, position, id);
            Toast.makeText(getActivity(), "Item click:"+id, 0).show();
        }

    }

    public static class AppListAdapter extends ArrayAdapter<AppEntry> {
        private LayoutInflater mInflate;

        public AppListAdapter(Context context) {
            super(context, android.R.layout.simple_list_item_2);
            mInflate=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }
        public void setData(List<AppEntry> data){
            clear();
            if(data!=null){
                addAll(data);
            }
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v;
            if(convertView==null){
                v=mInflate.inflate(R.layout.activity_main, parent, false);
            }else{
                v=convertView;
            }
            ImageView icon=(ImageView) v.findViewById(R.id.icon);
            TextView title=(TextView) v.findViewById(R.id.title);
            AppEntry item=getItem(position);
            icon.setImageDrawable(item.getIcon());
            title.setText(item.getLabel());
            return v;
        }

    }

    public static class AppEntry {
        private ApplicationInfo mInfo;
        private AppListLoader mLoader;
        private File mApkFile;
        private boolean mMounted = false;
        private Drawable mIcon;
        private String mLabel;

        public AppEntry(ApplicationInfo info, AppListLoader loader) {
            super();
            this.mInfo = info;
            this.mLoader = loader;
            mApkFile = new File(info.sourceDir);
        }

        // 获取activity的图标
        public Drawable getIcon() {
            if (mIcon == null || !mMounted) {
                if (mApkFile.exists()) {
                    mMounted = true;
                    mIcon = mInfo.loadIcon(mLoader.pm);
                    return mIcon;
                } else {
                    mMounted = false;
                    return mLoader.getContext().getResources()
                            .getDrawable(android.R.drawable.sym_def_app_icon);
                }
            } else {
                return mIcon;
            }
        }

        // 获取activity的label
        public String getLabel() {
            if (mLabel == null || !mMounted) {
                if (!mApkFile.exists()) {
                    mMounted = false;
                    mLabel = mInfo.packageName;
                } else {
                    mMounted = true;
                    String label = (String) mInfo.loadLabel(mLoader.pm);
                    mLabel = label != null ? label : mInfo.packageName;
                }
            }
            return mLabel;
        }

    }

    // 定义一个比较器,用于集合排序
    public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<MainActivity.AppEntry>() {
        private final Collator collator = Collator.getInstance();

        @Override
        public int compare(AppEntry lhs, AppEntry rhs) {
            return collator.compare(lhs.getLabel(), rhs.getLabel());
        }
    };

    public static class AppListLoader extends AsyncTaskLoader<List<AppEntry>> {

        public final PackageManager pm;
        private List<AppEntry> mApps;
        private PackageIntentReceiver mReceiver;
        private InterestingConfigChange lastConfig=new InterestingConfigChange();

        public AppListLoader(Context context) {
            super(context);
            // 注意此处需要使用整个application的上下文
            pm = getContext().getPackageManager();
        }

        @Override
        public List<AppEntry> loadInBackground() {
            // 获取到本机上所有应用信息的集合,包括已经删除但还存在安装目录的应用,包括已经被禁用的应用
            List<ApplicationInfo> apps = pm
                    .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES
                            | PackageManager.GET_DISABLED_COMPONENTS);
            if (apps == null) {
                apps = new ArrayList<ApplicationInfo>();
            }

            List<AppEntry> mEntries = new ArrayList<MainActivity.AppEntry>(
                    apps.size());
            for (int i = 0; i < apps.size(); i++) {
                AppEntry entry = new AppEntry(apps.get(i), this);
                mEntries.add(entry);
            }

            // 对集合进行排序
            Collections.sort(mEntries, ALPHA_COMPARATOR);
            return mEntries;
        }

        @Override
        public void deliverResult(List<AppEntry> data) {
            // 如果loader处于重置状态,则释放数据源
            if (isReset()) {
                if (data != null) {
                    onReleaseResources(data);
                    return;
                }
            }
            // 记录原来的数据源
            List<AppEntry> oldApps = mApps;
            mApps = data;
            // 如果loader已经启动,则将结果发送至客户端
            if (isStarted()) {
                super.deliverResult(data);
            }
            // 释放掉原来的数据源
            if (oldApps != null) {
                onReleaseResources(oldApps);
            }
        }

        @Override
        protected void onStartLoading() {
            super.onStartLoading();
            if (mApps != null) {
                // 如果我们此时有一个可用的数据源,则立即将其发送至客户端
                deliverResult(mApps);
            }
            // 开始监听数据源的变化
            if (mReceiver == null) {
                mReceiver = new PackageIntentReceiver(this);
            }

            // 判断一下从我们最后创建list后,系统配置是否有变化,如果有则开启加载器
            boolean configChange=lastConfig.applyNewConfig(getContext().getResources());
            if(takeContentChanged() || mApps==null || configChange){
                //如果数据发生变化或者当前数据不可用,或者系统配置发生变化则开启加载器加载数据
                forceLoad();
            }
        }

        @Override
        protected void onStopLoading() {
            // 如果加载器已经停止则取消加载
            cancelLoad();
        }

        @Override
        protected void onReset() {
            super.onReset();
            //停止加载
            onStopLoading();
            if(mApps!=null){
                onReleaseResources(mApps);
                mApps=null;
            }

            if(mReceiver!=null){
                getContext().unregisterReceiver(mReceiver);
                mReceiver=null;
            }
        }

        @Override
        public void onCanceled(List<AppEntry> data) {
            super.onCanceled(data);
            onReleaseResources(data);
        }

        private void onReleaseResources(List<AppEntry> data) {
            // 在本例中数据源只是一个list所以这里不需要做任何事,但如果数据源
            // 是一个Cursor,则应该在这里进行释放。在这里应该释放掉一切与loader
            // 相关联的资源
        }

    }

    public static class PackageIntentReceiver extends BroadcastReceiver {
        private AppListLoader mLoader;

        public PackageIntentReceiver(AppListLoader loader) {
            super();
            this.mLoader = loader;
            // 定义意图过滤器,当package添加、删除、修改时都会接收到通知
            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
            // 添加数据的类型为package类型
            filter.addDataScheme("package");
            mLoader.getContext().registerReceiver(this, filter);

            // 定义与sd卡相关的意图过滤器
            IntentFilter sdFileter = new IntentFilter(
                    Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
            sdFileter
                    .addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
            mLoader.getContext().registerReceiver(this, sdFileter);
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            // 通知loader数据改变
            mLoader.onContentChanged();
        }
    }

    // 定义类关注系统配置是否发生变化
    public static class InterestingConfigChange {
        final Configuration mLastConfiguration = new Configuration();
        int mLastDensity;

        public boolean applyNewConfig(Resources res) {
            // 获取发生变化的系统配置
            int configChanges = mLastConfiguration.updateFrom(res
                    .getConfiguration());
            // 判断屏幕的dpi密度是否发生变化
            boolean densityChange = mLastDensity != res.getDisplayMetrics().densityDpi;
            if (densityChange
                    || (configChanges&(ActivityInfo.CONFIG_LOCALE
                            | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_UI_MODE)) != 0) {
                mLastDensity=res.getDisplayMetrics().densityDpi;
                return true;
            }
            return false;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值