android 广播+服务+Application结合案例:获取所有安装应用的信息并展示到listview上,结合SearchView进行检索

效果图:

思路:

  • 1、创建自定义Application,在其onCreate()回调方法中启动一个Service,在Service中开启一个Thread线程,在该线程中,使用PackageManger类(主要职责是管理应用程序包)的getInstalledApplications()方法获取已安装的应用程序信息,并将获取的数据集合(类型为List<ApplicationInfo>)通过Intent发送给自定义广播。
  • 2、在广播的回调方法中,接收来自1中传来的数据,并将其作为数据源设置给ListView的适配器。
  • 3、在SearchView的回调方法中, 循环使用loadLabel(PackageManager pm)  获得当前应用程序的label与用户输入的字符串进行包含比较,再创建List<ApplicationInfo>集合,如果包含就添加到此集合。最后将该集合重新作为数据源绑定ListView的适配器。

布局很简单:

1。main_acitivity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <android.support.v7.widget.SearchView
        android:layout_margin="10dp"
        android:id="@+id/main_sv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:searchHintIcon="@drawable/ic_action_action_search"
        android:background="@drawable/sv_bg"
        app:iconifiedByDefault="false"
        app:queryHint="请输入...">
    </android.support.v7.widget.SearchView>

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

    </ListView>

</LinearLayout>

2。list_item.xml 适配器每一个item的布局

<?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="wrap_content"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_margin="10dp"
        tools:background="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="10dp"
        android:layout_weight="1"
        android:textSize="20dp"
        tools:text="测试测试" />

</LinearLayout>

代码:

bean类 封装你想要app的信息类 

AppInfos.java  因为涉及intent传输需实现Parcelable方法

import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;

/**
 * <p>文件描述:<p>
 * <p>作者:Mr-Donkey<p>
 * <p>创建时间:2018/11/24 18:14<p>
 * <p>更改时间:2018/11/24 18:14<p>
 * <p>版本号:1<p>
 */

/**
 * app应用信息封装类
 */
public class AppInfos implements Parcelable {

    private CharSequence label;
    private Drawable img;

    public AppInfos(CharSequence label, Drawable img) {
        this.label = label;
        this.img = img;
    }


    public CharSequence getLabel() {
        return label;
    }

    public void setLabel(CharSequence label) {
        this.label = label;
    }

    public Drawable getImg() {
        return img;
    }

    public void setImg(Drawable img) {
        this.img = img;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    }

    protected AppInfos(Parcel in) {
    }

    public static final Creator<AppInfos> CREATOR = new Creator<AppInfos>() {
        @Override
        public AppInfos createFromParcel(Parcel in) {
            return new AppInfos(in);
        }

        @Override
        public AppInfos[] newArray(int size) {
            return new AppInfos[size];
        }
    };
}

MyApliacation.java类 全局的进行开启服务


import android.app.Application;
import android.content.Intent;

/**
 * <p>文件描述:<p>
 * <p>作者:Mr-Donkey<p>
 * <p>创建时间:2018/11/24 14:51<p>
 * <p>更改时间:2018/11/24 14:51<p>
 * <p>版本号:1<p>
 */
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        //1.开启服务
        startService(new Intent(this, MyService.class));
    }
}

MyService.java 进行服务的一系列操作 看注释

主要是注册,注销,发送广播


import android.app.Service;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.util.Log;

import com.demo.showapp.config.Config;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>文件描述:<p>
 * <p>作者:Mr-Donkey<p>
 * <p>创建时间:2018/11/24 14:52<p>
 * <p>更改时间:2018/11/24 14:52<p>
 * <p>版本号:1<p>
 */
public class MyService extends Service {

    private static final String TAG = "MyService";

    //1.创建服务
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: ");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand: ");
        //开启线程
        new Thread() {
            @Override
            public void run() {
                super.run();
                Log.d(TAG, "run: 开启获取app信息的进程");
                //1.获取app信息
                PackageManager pm = getPackageManager();
                List<ApplicationInfo> mLists = pm.getInstalledApplications(0);
                Log.d(TAG, "run: " + mLists.get(1).loadLabel(pm));
                Intent i = new Intent();
                i.setAction(Config.BROADCAST_ACTION);
                i.putParcelableArrayListExtra(Config.APP_INFO, (ArrayList<? extends Parcelable>) mLists);
                //2.发送广播
                sendBroadcast(i);
            }
        }.start();
        return super.onStartCommand(intent, flags, startId);


    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind: ");
        return null;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnbind: ");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy: ");
        super.onDestroy();
    }
}

Config.java 全局配置类,配置一些共有的信息

public class Config {
    public static final String SEARCH_TEXT = "search_text";
    public static final String APP_INFO = "app_Info";
    public static final String BROADCAST_ACTION = "com.demo.action";
}

ListAdapter.java 基础BaseAdapter

在里面实现了SearchView的检索

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.demo.showapp.R;
import com.demo.showapp.bean.AppInfos;
import com.squareup.picasso.Picasso;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>文件描述:<p>
 * <p>作者:Mr-Donkey<p>
 * <p>创建时间:2018/11/24 16:18<p>
 * <p>更改时间:2018/11/24 16:18<p>
 * <p>版本号:1<p>
 */

/**
 * 里面实现的思路是:
 * 1.新建一个List new_number这个对象用来存储过滤后符合条件的值
 * 2.判断constraint是否为空,是否有值.满足条件的话遍历List,并且判断List的值是否有包含过滤的条件.
 * 3.如果是的话把值存到new_number.如果一个符合的都没有把temp_number的值(也就是最开始传进来的List)赋值给new_number
 * 4.新建一个FilterResults对象分别把new_number的size和value赋值给FilterResults.value和FilterResults.count,然后返回FilterResults对象
 * 5.接着在publishResults()方法中取出过滤后满足条件的值也就是results.values
 * 6.判断results.count的过滤后结果的个数是否大于0
 * 7.大于0的话把值赋值给number然后刷新界面,不满足的话把temp_number(也就是最开始传进来的值)赋值给number,然后刷新界面
 */
public class ListAdapter extends BaseAdapter implements Filterable {

    private Context mContext;
    private List<AppInfos> mAppInfos, temp_Infos;
    private LayoutInflater mLayoutInflater;
    private PackageManager pm;
    private TestFilter myFilter;

    public ListAdapter(Context context, List<AppInfos> appInfos) {
        mAppInfos = appInfos;
        temp_Infos = appInfos;
        mContext = context;
        mLayoutInflater = LayoutInflater.from(context);
        pm = context.getPackageManager();
    }

    @Override
    public int getCount() {
        return mAppInfos.size();
    }

    @Override
    public Object getItem(int position) {
        return mAppInfos.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder vh;
        if (convertView == null) {
            vh = new ViewHolder();
            convertView = mLayoutInflater.inflate(R.layout.list_item, null, false);
            vh.icon = convertView.findViewById(R.id.iv_icon);
            vh.title = convertView.findViewById(R.id.tv_title);
            convertView.setTag(vh);
        } else {
            vh = (ViewHolder) convertView.getTag();
        }
        vh.icon.setBackground(mAppInfos.get(position).getImg());
        vh.title.setText(mAppInfos.get(position).getLabel());
        return convertView;
    }

    /**
     * 实现了Filterable接口,这个方法需要覆写getFilter()方法.
     *
     * @return
     */
    @Override
    public Filter getFilter() {
        if (myFilter == null) {
            myFilter = new TestFilter();
        }
        return myFilter;
    }

    private class ViewHolder {
        private ImageView icon;
        private TextView title;
    }


    class TestFilter extends Filter {
        /**
         * 该方法主要完成对数据进行过滤的工作
         *
         * @param constraint
         * @return
         */
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {

            List<AppInfos> new_number = new ArrayList();
            if (constraint != null && constraint.toString().trim().length() > 0) {
                for (int i = 0; i < temp_Infos.size(); i++) {
                    String content = (String) temp_Infos.get(i).getLabel();
                    Drawable img = temp_Infos.get(i).getImg();
                    if (content.contains(constraint)) {
                        new_number.add(new AppInfos(content, img));
                    }
                }

            } else {
                new_number = temp_Infos;
                Log.e("MainActivity","adapter");
            }

            FilterResults filterResults = new FilterResults();
            filterResults.count = new_number.size();
            filterResults.values = new_number;
            return filterResults;
        }

        /**
         * 接收过滤后数据
         * 在该方法里面进行界面的刷新工作
         */
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {

            mAppInfos = (List<AppInfos>) results.values;

            if (results.count > 0) {
                Toast.makeText(mContext, "查找成功", Toast.LENGTH_SHORT).show();
                notifyDataSetChanged();
            } else {
                Toast.makeText(mContext, "暂无应用", Toast.LENGTH_SHORT).show();
                notifyDataSetChanged();
            }

        }
    }


}

MainActivity.java

1.注册广播,接受传来的信息

2.横竖屏的状态保存

3.按返回键后重新回来恢复状态 


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.graphics.drawable.Drawable;
import android.icu.util.LocaleData;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Filter;
import android.widget.ListView;
import android.widget.TextView;

import com.demo.showapp.adapter.ListAdapter;
import com.demo.showapp.bean.AppInfos;
import com.demo.showapp.config.Config;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private ListAdapter mAadpter;
    private ListView lv;
    private SearchView sv;
    private List<AppInfos> mInfos = new ArrayList<>();
    private String search_text = "";
    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "onReceive: 拿到broadcast发送来的信息");
            if (intent.getAction().equals(Config.BROADCAST_ACTION)) {
                List<ApplicationInfo> infos = intent.getParcelableArrayListExtra(Config.APP_INFO);
                //将应用信息装到新的集合中
                for (ApplicationInfo info : infos) {
                    CharSequence label = info.loadLabel(getPackageManager());
                    Drawable icon = info.loadIcon(getPackageManager());
                    mInfos.add(new AppInfos(label, icon));
                }
                initView();
            }

        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate: ");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /**
     * 初始化视图
     */
    private void initView() {
        lv = findViewById(R.id.main_lv);
        sv = findViewById(R.id.main_sv);
        mAadpter = new ListAdapter(this, mInfos);
        lv.setAdapter(mAadpter);
        searchViewListener();
    }

    /**
     * searchView的监听事件
     */
    public void searchViewListener() {

        sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            //提交的text
            @Override
            public boolean onQueryTextSubmit(String s) {
                Log.d(TAG, "onQueryTextSubmit: " + s);
                //Adapter的内容进行过滤.
                mAadpter.getFilter().filter(s);
                return false;
            }

            //改变的text
            @Override
            public boolean onQueryTextChange(String s) {
                Log.d(TAG, "onQueryTextChange: " + s);
                //保存全局变量
                search_text = s;
                //Adapter的内容进行过滤.
                mAadpter.getFilter().filter(s);
                return false;
            }
        });
    }


    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: ");
        //注册广播
        registerReceiver();
    }

    /**
     * 动态注册广播
     */
    private void registerReceiver() {
        //1.意图过滤
        Log.d(TAG, "registerReceiver: ");
        IntentFilter mFilter = new IntentFilter();
        mFilter.addAction(Config.BROADCAST_ACTION);
        //2.注册广播
        registerReceiver(mBroadcastReceiver, mFilter);
    }

    /**
     * 注销广播
     */
    @Override
    protected void onPause() {
        Log.d(TAG, "onPause: ");
        super.onPause();
        unregisterReceiver(mBroadcastReceiver);
    }

    @Override
    protected void onDestroy() {
        Log.d(TAG, "onDestroy: ");
        super.onDestroy();
    }

    /**
     * 返回时保存状态
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        try {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                Intent home = new Intent(Intent.ACTION_MAIN);
                home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                home.addCategory(Intent.CATEGORY_HOME);
                startActivity(home);
                return true;
            }
        } catch (Exception e) {
            //ignore

        }
        return super.onKeyDown(keyCode, event);
    }

    /**
     * 横竖屏切换保存值
     * 原理:
     * 1.横屏切换竖屏实际上是先把当前的横屏的Activity杀掉,然后重新创建一个竖屏的Activity
     * 2.我们可以使用onSaveInstanceState()方法保存数据
     * 3.它是在横屏Activity将杀死前调用,可以将须要保存的数据放入Bundle封装在系统中
     * 4.切换竖屏后这个Activity又重新被创建 这样可以在onCreate(Bundle)
     * 5.或者onRestoreInstanceState(Bundle)方法中来回复之前保存在Bundle中的数据
     * 6.这样就可以实现横竖屏界面切换数据的保存与读取
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        Log.d(TAG, "onSaveInstanceState: ");
        super.onSaveInstanceState(outState);
        outState.putString(Config.SEARCH_TEXT, search_text);
        outState.putParcelableArrayList(Config.APP_INFO, (ArrayList<? extends Parcelable>) mInfos);
    }

    /**
     * 恢复值
     */
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        Log.d(TAG, "onRestoreInstanceState: ");
        super.onRestoreInstanceState(savedInstanceState);
        if (savedInstanceState != null || !savedInstanceState.getParcelableArrayList(Config.APP_INFO).isEmpty()) {
            mInfos = savedInstanceState.getParcelableArrayList(Config.APP_INFO);
            search_text = savedInstanceState.getString(Config.SEARCH_TEXT);
            Log.d(TAG, "onRestoreInstanceState: 恢复数据");
            if (mInfos != null || !mInfos.isEmpty()) {
                initView();
                //设置默认的查找值为上次输入的值
                sv.setQuery(search_text, true);
            }
        }


    }
}

完成。需要demo留下邮箱看到后回复

猜你喜欢

转载自blog.csdn.net/qq_17846019/article/details/84454402