Android 自定义应用选择器对话框

Android 自定义应用选择器对话框

一、获取可处理 ACTION_VIEW 的应用列表

java 复制代码
public List<ResolveInfo> getAppsForActionView(Context context, Uri fileUri) {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(fileUri);
    intent.setType("*/*"); // Adjust the MIME type as needed

    PackageManager packageManager = context.getPackageManager();
    return packageManager.queryIntentActivities(intent, 0);
}

二、显示应用列表对话框

创建并显示一个对话框,其中包含应用列表。

java 复制代码
public void showAppChooserDialog(Context context, Uri fileUri) {
    List<ResolveInfo> apps = getAppsForActionView(context, fileUri);
    PackageManager packageManager = context.getPackageManager();

    AlertDialog.Builder builder = new AlertDialog.Builder(context);
    builder.setTitle("Select an application");

    ListAdapter adapter = new ArrayAdapter<String>(
        context,
        android.R.layout.select_dialog_item,
        apps.stream().map(info -> info.loadLabel(packageManager).toString()).collect(Collectors.toList())
    ) {
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View view = super.getView(position, convertView, parent);
            TextView text = view.findViewById(android.R.id.text1);
            text.setCompoundDrawablesWithIntrinsicBounds(
                apps.get(position).loadIcon(packageManager),
                null, null, null
            );
            text.setCompoundDrawablePadding(16);
            return view;
        }
    };

    builder.setAdapter(adapter, (dialog, which) -> {
        ResolveInfo selectedApp = apps.get(which);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(fileUri);
        intent.setType("*/*");
        intent.setPackage(selectedApp.activityInfo.packageName);
        context.startActivity(intent);
    });

    builder.show();
}

使用方法

调用 showAppChooserDialog 方法并传递文件的 Uri:

java 复制代码
Uri fileUri = Uri.parse("file://path/to/your/file"); // Adjust the file path
showAppChooserDialog(this, fileUri);

说明

  • 自定义对话框 :使用 AlertDialog 来创建一个应用选择对话框。
  • 列表适配器ArrayAdapter 用于显示应用名称和图标。
  • 启动选定应用:用户选择应用后,启动对应的应用来处理文件。
  • fileUri:替换为实际需要打开的文件 URI。

三、自定义布局

创建一个布局文件 app_item.xml,包含 ImageViewTextView

xml 复制代码
<!-- res/layout/app_item.xml -->
<LinearLayout xmlns:android="<http://schemas.android.com/apk/res/android>"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="8dp">

    <ImageView
        android:id="@+id/app_icon"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_marginEnd="8dp" />

    <TextView
        android:id="@+id/app_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:textSize="16sp" />
</LinearLayout>

自定义适配器

创建自定义适配器 AppListAdapter

java 复制代码
public class AppListAdapter extends ArrayAdapter<ResolveInfo> {
    private final List<ResolveInfo> apps;
    private final PackageManager packageManager;
    private final int resource;

    public AppListAdapter(Context context, int resource, List<ResolveInfo> apps) {
        super(context, resource, apps);
        this.apps = apps;
        this.packageManager = context.getPackageManager();
        this.resource = resource;
    }

    @NonNull
    @Override
    public View getView(int position, View convertView, @NonNull ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(getContext()).inflate(resource, parent, false);
        }

        ResolveInfo appInfo = apps.get(position);

        ImageView appIcon = convertView.findViewById(R.id.app_icon);
        TextView appName = convertView.findViewById(R.id.app_name);

        appIcon.setImageDrawable(appInfo.loadIcon(packageManager));
        appName.setText(appInfo.loadLabel(packageManager));

        return convertView;
    }
}

显示对话框

在你的活动或片段中使用自定义适配器:

java 复制代码
public void showAppChooserDialog(Context context, Uri fileUri) {
    List<ResolveInfo> apps = getAppsForActionView(context, fileUri);

    AppListAdapter adapter = new AppListAdapter(context, R.layout.app_item, apps);

    AlertDialog.Builder builder = new AlertDialog.Builder(context);
    builder.setTitle("Select an application");
    builder.setAdapter(adapter, (dialog, which) -> {
        ResolveInfo selectedApp = apps.get(which);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setData(fileUri);
        intent.setType("*/*");
        intent.setPackage(selectedApp.activityInfo.packageName);
        context.startActivity(intent);
    });

    AlertDialog dialog = builder.create();
    dialog.show();

    // 设置对话框背景
    dialog.getWindow().setBackgroundDrawableResource(android.R.color.background_light);
}

说明

  • 自定义布局app_item.xml 包含 ImageViewTextView
  • 自定义适配器AppListAdapter 负责为每个应用加载图标和名称。
  • 对话框背景 :使用 setBackgroundDrawableResource() 设置对话框背景颜色。你可以根据需要替换为其他颜色资源。

在 Android 中,当使用 Intent.createChooser() 创建一个应用选择器(chooser)时,系统不会自动为传入的 Intent 设置 setPackage()

详细解释:

  1. Intent.createChooser() 的行为
    • 当调用 Intent.createChooser() 时,系统会弹出一个对话框,显示所有能够处理该 Intent 的应用程序列表。
    • 用户选择一个应用后,系统会将该应用的信息(如其包名)与原始 Intent 结合,启动该应用来处理 Intent
  2. 是否自动设置 setPackage()
    • 系统并不会自动为原始的 Intent 添加 setPackage()
    • 系统会临时将用户选择的应用与 Intent 绑定,但不会直接修改或传递 setPackage() 的值到原始 Intent 中。
    • 如果你需要显式指定 setPackage(),需要在代码中自己设置它。
  3. 为什么 setPackage() 很重要
    • 使用 setPackage() 可以强制指定某个应用来处理 Intent,避免系统显示多个应用选择器。
    • 如果使用了 Intent.createChooser()setPackage() 通常不需要,因为用户会手动选择应用。

四、示例代码

以下是一个使用 Intent.createChooser() 的示例:

java 复制代码
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "Hello, World!");

// 创建一个 chooser
Intent chooser = Intent.createChooser(intent, "Choose an app");
startActivity(chooser);
  • 在这里,系统不会为 intent 添加 setPackage(),而是根据用户选择的应用临时处理。

如果需要手动设置 setPackage()

如果你需要显式指定某个应用,可以在 Intent 上调用 setPackage(),如下所示:

java 复制代码
intent.setPackage("com.example.specificapp"); // 替换为目标应用的包名
startActivity(intent);

这样会直接跳转到指定应用,而不会弹出应用选择器。


五、总结

  • 默认行为Intent.createChooser() 不会自动为 Intent 添加 setPackage()
  • 临时绑定 :系统会根据用户选择的应用临时绑定 Intent 与目标应用。
  • 手动指定 :如果需要某个特定应用处理 Intent,需要显式调用 setPackage()
相关推荐
潜龙95271 小时前
第3.2.3节 Android动态调用链路的获取
android·调用链路
追随远方1 小时前
Android平台FFmpeg音视频开发深度指南
android·ffmpeg·音视频
撰卢3 小时前
MySQL 1366 - Incorrect string value:错误
android·数据库·mysql
恋猫de小郭3 小时前
Flutter 合并 ‘dot-shorthands‘ 语法糖,Dart 开始支持交叉编译
android·flutter·ios
牛马程序小猿猴3 小时前
15.thinkphp的上传功能
android
林家凌宇3 小时前
Flutter 3.29.3 花屏问题记录
android·flutter·skia
时丶光4 小时前
Android 查看 Logcat (可纯手机方式 无需电脑)
android·logcat
血手人屠喵帕斯4 小时前
事务连接池
android·adb
恋猫de小郭5 小时前
React Native 前瞻式重大更新 Skia & WebGPU & ThreeJS,未来可期
android·javascript·flutter·react native·react.js·ios
一人一萧十只猫�6 小时前
MySQL 从入门到精通(三):日志管理详解 —— 从排错到恢复的核心利器
android·mysql·adb