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,包含 ImageView 和 TextView:

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 包含 ImageView 和 TextView。
  • 自定义适配器 :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()。
相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui