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

时间:2024-12-03 13:18:56

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

    • 一、获取可处理 `ACTION_VIEW` 的应用列表
    • 二、显示应用列表对话框
    • 三、自定义布局
    • 四、示例代码
    • 五、总结

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

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);
}

二、显示应用列表对话框

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

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:

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

说明

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

三、自定义布局

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

<!-- 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

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;
    }
}

显示对话框

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

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() 的示例:

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(),如下所示:

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

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


五、总结

  • 默认行为Intent.createChooser() 不会自动为 Intent 添加 setPackage()
  • 临时绑定:系统会根据用户选择的应用临时绑定 Intent 与目标应用。
  • 手动指定:如果需要某个特定应用处理 Intent,需要显式调用 setPackage()