效果图:
思路:
- 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留下邮箱看到后回复