简介
实现内容:点击图片打开一个Dialog,Dialog中显示设备上的app,选中一个添加到RecyclerView中,点击后可以打开app。
1.引入依赖
因为,需要保存数据到数据库,所以这里需要用到Room,在build.gradle
//Room
implementation 'androidx.room:room-runtime:2.2.5'
annotationProcessor 'androidx.room:room-compiler:2.2.5'
2.创建实体类、dao和database
2.1 app.java
@Entity
public class App {
@PrimaryKey(autoGenerate = true)
public int id;
@ColumnInfo(defaultValue = "name")
public String name;
@ColumnInfo(defaultValue = "icon")
public String icon;
@ColumnInfo(defaultValue = "pck")
public String pck;
@Override
public String toString() {
return "App{" +
"name='" + name + '\'' +
", icon=" + icon +
", pck='" + pck + '\'' +
'}';
}
}
2.2 AppDao.java
@Dao
public interface AppDao {
@Insert
void save(App...app);
@Query("select * from app")
List<App> queryAll();
}
2.3 AppDatabase.java
@Database(entities = {App.class},version = 1,exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract AppDao appDao();
}
3.创建显示所有App的dialog和对应的RecyclerView适配器
3.1 AppDialog.java
/**
* 显示设备上所有app的dialog
*/
public class AppDialog extends AlertDialog {
//设置静态事件
private static onClickDialog onClickDialog;
//声明AppDao
private AppDao appDao;
//创建onClickDialog接口
public interface onClickDialog{
void click(int status);
}
//设置一个回调方法
public void setOnClickApp(onClickDialog onClick){
this.onClickDialog = onClick;
}
public AppDialog(@NonNull Context context) {
super(context);
//初始化数据库
initDatabase();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.app_dialog);
//绑定UI
RecyclerView rvc = findViewById(R.id.rvc);
//获取所有的app集合list
List<PackageInfo> list = getAllApps(0);
//声明适配器并传入list集合
DialogAdapterTest adapter = new DialogAdapterTest(getContext(),list);
//设置布局,2列,垂直布局
rvc.setLayoutManager(new GridLayoutManager(getContext(),2,RecyclerView.VERTICAL,false));
//填入适配器
rvc.setAdapter(adapter);
}
/**
* 获得所有的APPS
* @param filter 选择筛选的类型,0,1,2
* @return app集合
*/
@SuppressWarnings("static-access")
public List<PackageInfo> getAllApps(int filter) {
List<PackageInfo> apps = new ArrayList<PackageInfo>();
PackageManager pManager = getContext().getPackageManager();
// 获取手机内所有应用
List<PackageInfo> packlist = pManager.getInstalledPackages(0);
for (int i = 0; i < packlist.size(); i++) {
PackageInfo pak = (PackageInfo) packlist.get(i);
String packageName = pak.applicationInfo.packageName;
switch (filter) {
case 0: //所有应用
if (!filterErrorPackage(pManager, packageName)) {
apps.add(pak);
}
break;
case 1: //第三方应用
if ((pak.applicationInfo.flags & pak.applicationInfo.FLAG_SYSTEM) <= 0) {
if (!filterErrorPackage(pManager, packageName)) {
apps.add(pak);
}
}
break;
case 2: //系统自带应用
if ((pak.applicationInfo.flags & pak.applicationInfo.FLAG_SYSTEM) != 0) {
if (!filterErrorPackage(pManager, packageName)) {
apps.add(pak);
}
}
break;
default:
break;
}
}
return apps;
}
/**
* 保存选中的app的对应数据到数据库
* @param name app名
* @param icon app的icon
* @param pck app的包名
*/
public void go(String name,String icon,String pck){
App app = new App();
app.icon = icon;
app.name = name;
app.pck = pck;
appDao.save(app);
//回调状态 1
AppDialog.onClickDialog.click(1);
// onClickDialog.click(1);
}
/**
* 初始化数据库
*/
public void initDatabase(){
AppDatabase database = Room.databaseBuilder(getContext(),
AppDatabase.class,
"app")
.allowMainThreadQueries()
.build();
appDao = database.appDao();
}
/**
* 判断当前包是否可以打开
* @return
*/
public boolean filterErrorPackage(PackageManager pManager, String packageName) {
Intent intent = new Intent();
intent = pManager.getLaunchIntentForPackage(packageName);
//如果该程序不可启动(像系统自带的包,有很多是没有入口的)会返回NULL
if (intent == null) {
return true;
}
return false;
}
}
3.2 app_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvc"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
3.3 DialogAdapterTest.java
/**
* AppDialog的RecyclerView适配器
*/
public class DialogAdapterTest extends RecyclerView.Adapter<DialogAdapterTest.MyHolder> {
private Context mContext;
private List<PackageInfo> mList = new ArrayList<>();
private PackageManager pm = null;
private AppDialog appDialog;
public DialogAdapterTest(Context context, List<PackageInfo> list) {
this.mContext = context;
this.mList = list;
//通知刷新数据
notifyDataSetChanged();
}
@NonNull
@Override
public DialogAdapterTest.MyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.item_list, null);
pm = mContext.getPackageManager();
DialogAdapterTest.MyHolder myHolder = new MyHolder(view);
return myHolder;
}
//可能int position下会冒红线,在 @Override上面加上这个就行了 -> @SuppressLint("RecyclerView")
@Override
public void onBindViewHolder(@NonNull DialogAdapterTest.MyHolder holder, int position) {
holder.iv_image.setImageDrawable(pm.getApplicationIcon(mList.get(position).applicationInfo));
holder.tv_text.setText(pm.getApplicationLabel(mList.get(position).applicationInfo));
//app点击事件
holder.iv_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
appDialog = new AppDialog(mContext);
//调用appDialog的go方法存入app名、图片、包名到数据库
appDialog.go(
(String) pm.getApplicationLabel(mList.get(position).applicationInfo),
DrawableToString(pm.getApplicationIcon(mList.get(position).applicationInfo)),
mList.get(position).applicationInfo.packageName
);
}
});
}
@Override
public int getItemCount() {
return mList.size();
}
/**
* 内部类MyHolder,声明和绑定iv_image,tv_text
*/
public static class MyHolder extends RecyclerView.ViewHolder {
ImageView iv_image;
TextView tv_text;
public MyHolder(@NonNull View itemView) {
super(itemView);
iv_image = itemView.findViewById(R.id.iv_image);
tv_text = itemView.findViewById(R.id.tv_text);
}
}
/**
* 将drawable转化成字符串
* @param drawable 要转换成String类型的Drawable文件
* @return
*/
public synchronized static String DrawableToString(Drawable drawable) {
if (drawable != null) {
Bitmap bitmap = Bitmap
.createBitmap(
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
drawable.draw(canvas);
int size = bitmap.getWidth() * bitmap.getHeight() * 4;
// 创建一个字节数组输出流,流的大小为size
ByteArrayOutputStream baos = new ByteArrayOutputStream(size);
// 设置位图的压缩格式,质量为100%,并放入字节数组输出流中
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
// 将字节数组输出流转化为字节数组byte[]
byte[] imagedata = baos.toByteArray();
return Base64.encodeToString(imagedata, Base64.DEFAULT);
}
return " ";
}
}
4.创建主页面和对应的适配器
4.1创建主页面AddAppActivity .java
/**
* 主页面
*/
public class AddAppActivity extends AppCompatActivity {
//声明AppDao
private AppDao appDao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_app);
initDatabase();
//绑定UI
RecyclerView app = findViewById(R.id.app);
//声明adapter
MainAdapterTest adapter = new MainAdapterTest(this);
//查询数据库传入App的集合
adapter.setAppList(appDao.queryAll());
//设置布局,2列,垂直布局
app.setLayoutManager(new GridLayoutManager(this,2,RecyclerView.VERTICAL,false));
//填入适配器
app.setAdapter(adapter);
}
/**
* 初始化数据库
*/
public void initDatabase(){
AppDatabase database = Room.databaseBuilder(this,
AppDatabase.class,
"app")
.allowMainThreadQueries()
.build();
appDao = database.appDao();
}
}
4.2 activity_add_app.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"
tools:context=".AddAppActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/app"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
4.3 MainAdapterTest.java
/**
* AddAppActivity的RecyclerView的适配器
*/
public class MainAdapterTest extends RecyclerView.Adapter<MainAdapterTest.MyHolder> {
//声明上下文
private Context mContext;
//声明一个App的list集合
private List<App> appList = new ArrayList<>();
//声明包管理器
private PackageManager packageManager = null;
//声明AppDao
private AppDao appDao;
public MainAdapterTest(Context context) {
this.mContext = context;
initDatabase();
}
public void setAppList(List<App> list){
this.appList = list;
Log.d("TAG", "当前app数量: " + list.size());
//通知刷新数据
notifyDataSetChanged();
}
@NonNull
@Override
public MainAdapterTest.MyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
packageManager = mContext.getPackageManager();
View view;
switch (viewType) {
case 0:
//添加应用的view
view = LayoutInflater.from(mContext).inflate(R.layout.item_list, null);
break;
case 1:
//打开应用的view
view = LayoutInflater.from(mContext).inflate(R.layout.item_list_add, null);
break;
default:
view = LayoutInflater.from(mContext).inflate(R.layout.item_list_add, null);
}
MainAdapterTest.MyHolder myHolder = new MyHolder(view);
return myHolder;
}
//可能int position下会冒红线,在 @Override上面加上这个就行了 -> @SuppressLint("RecyclerView")
@Override
public void onBindViewHolder(@NonNull MainAdapterTest.MyHolder holder, int position) {
//appList有数据时,设置对应app的文字和图片
if (getItemViewType(position) == 1) {
holder.iv_image.setImageDrawable(StringToDrawable(appList.get(position).icon));
holder.tv_text.setText(appList.get(position).name);
//对应app的点击事件
holder.iv_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//打开对应的app
startAPK(appList.get(position).pck);
}
});
} else {
//添加app的点击事件
holder.iv_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//声明AppDialog
AppDialog dialog = new AppDialog(mContext);
//显示dialog
dialog.show();
//AppDialog的回调事件
dialog.setOnClickApp(new AppDialog.onClickDialog() {
@Override
public void click(int status) {
if (status!=0){
//查询保存到数据库的所有App
setAppList(appDao.queryAll());
//关闭dialog
dialog.dismiss();
}
}
});
}
});
}
}
@Override
public int getItemViewType(int position) {
//未添加app时返回0
if (appList == null || appList.size() == 0 || position == appList.size()) return 0;
return 1;
}
@Override
public int getItemCount() {
//未添加app时设置appList.size()为1,添加过则始终+1,保证加号存在
return appList == null ? 1 : appList.size() + 1;
}
/**
* 内部类MyHolder,声明和绑定iv_image,tv_text
*/
public static class MyHolder extends RecyclerView.ViewHolder {
ImageView iv_image;
TextView tv_text;
public MyHolder(@NonNull View itemView) {
super(itemView);
iv_image = itemView.findViewById(R.id.iv_image);
tv_text = itemView.findViewById(R.id.tv_text);
}
}
/**
* 根据包名打开第三方app
* @param pck 包名
*/
private void startAPK(String pck){
if (packageManager!=null){
Intent intent = packageManager.getLaunchIntentForPackage(pck);
if (intent != null){
try {
mContext.getApplicationContext().startActivity(intent);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
/**
* 初始化数据库
*/
public void initDatabase() {
AppDatabase database = Room.databaseBuilder(mContext,
AppDatabase.class,
"app")
.allowMainThreadQueries()
.build();
appDao = database.appDao();
}
/**
* 将字符串转化成Drawable
* @param icon 要转换成Drawable类型的String字符串
* @return Drawable类型
*/
public synchronized static Drawable StringToDrawable(String icon) {
if (icon == null || icon.length() < 10)
return null;
byte[] img = Base64.decode(icon.getBytes(), Base64.DEFAULT);
Bitmap bitmap;
if (img != null) {
bitmap = BitmapFactory.decodeByteArray(img, 0, img.length);
@SuppressWarnings("deprecation")
Drawable drawable = new BitmapDrawable(bitmap);
return drawable;
}
return null;
}
}
4.4 item_list_add.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal">
<ImageView
android:id="@+id/iv_image"
android:layout_width="50dp"
android:background="@color/black"
android:layout_height="50dp"/>
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"/>
</LinearLayout>
(两个适配器用的item_list.xml是同一个 ) (^_^)
5.备忘录
如果是直接以创建Java 文件的形式创建AddAppActivity,不要忘记去Androidmanifest.xml里去注册你的Activity
<activity
android:name=".AddAppActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
6.结果展示
7.参考(抄袭)链接
gaosunqiong大佬的在 Dialog中显示所有应用程序
灬布衣丶公爵丨大佬的 Android–字符串和Drawable之间互相转化
8.补充、拓展
当然啦,你可以在添加app的时候根据该app的包名对数据库进行查询,如果已保存过这个app,则返回,不进行添加,并使用吐司弹出 “该app以添加” 的提示框