如何以编程方式将活动主题设置为对话框样式?

17

问题

如何通过编程的方式(而不是修改 AndroidManifext.xml 文件)设置一个 Activity 的主题,使其看起来像一个对话框

注:只要不需要修改 AndroidManifext.xml 文件就可以在普通活动和对话框之间切换,我可以修改它。

我尝试过的

根据这个 stackoverflow 回答,我尝试了以下方法:

public class DialogActivity extends Activity
{

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        setTheme(android.R.style.Theme_DeviceDefault_Dialog);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog);
        Log.d(TAG,"Build.VERSION.SDK_INT: "+Build.VERSION.SDK_INT); // 23
    }
}

但它最终会使背景中的所有内容变黑

我还看到了这个stackoverflow答案,并尝试了:

public class DialogActivity extends Activity
{

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        setTheme(android.R.style.Theme_DeviceDefault_Dialog);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog);
        getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
    }
}

但最终会使所有东西变成黑色。怎么办?谢谢。


可能重复:https://dev59.com/jnI-5IYBdhLWcg3wMFMf。也许你也会对这个感兴趣:https://github.com/pingpongboss/StandOut。 - T D Nguyen
@NguyenDoanTung 谢谢。我也看到了那篇帖子,但是大多数答案都涉及修改 AndroidManifest.xml,而我不想这样做。我尝试了 https://dev59.com/jnI-5IYBdhLWcg3wMFMf#22216966 中提到的第一种方法,但是像我在问题中提到的那样,它并没有产生令人满意的结果。另外,谢谢你提供的 github 链接,我查看了它,但是我真的希望有一个更简单的解决方案。 - Eric
@Eric 神奇地解决了这个问题,非常感谢,已发送赏金。必须在清单文件中设置 Theme.AppCompat.Dialog。你只能在 onCreate 中设置自己的样式。这也是我在这里提出的问题的答案:https://dev59.com/iMDqa4cB1Zd3GeqPZUB8#67045156 - Fattie
4个回答

32

背景

如果前台Activity的主题在其AndroidManifest.xml中设置为对话框,那么该Activity后面的Activity将会被绘制出来;否则,Android操作系统不会绘制其后面的Activity(这样可以节省内存,因为通常也不会被看到)。

为了利用这一点,我们在清单文件中将Activity的主题设置为对话框,这样Android操作系统就会绘制它后面的Activity,但随后,在运行时,我们可以通过编程方式将Activity的主题设置为任何我们喜欢的样式。

示例代码在Github上

我制作了一个示例,并将其放在Github上。

教程

第一步:styles.xml中为你的应用程序创建两个自定义主题。一个用于普通Activity,另一个用于对话框Activity。对于自定义的对话框主题,重要的是要从基础对话框主题继承。在我的例子中,父级主题是Base.Theme.AppCompat.Light.Dialog.FixedSize。这是我的styles.xml

<resources>

    <!-- custom normal activity theme -->    
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    </style>

    <!-- custom dialog activity theme -->
    <style name="AppTheme.Dialog" parent="Base.Theme.AppCompat.Light.Dialog.FixedSize">
        <!-- removing the dialog's action bar -->
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

</resources>

第二步:AndroidManifest.xml文件中,将需要处理的Activity的主题设置为任意对话框主题。这会使安卓操作系统认为该Activity是一个对话框,因此它会在其后面绘制Activity,而不会将其变成黑色。在我的案例中,我使用了Theme.AppCompat.Dialog。下面是我的AndroidManifest.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.eric.questiondialog_artifact">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name">
        <activity
            android:name=".DialogActivity"
            android:label="@string/app_name"
            android:theme="@style/Theme.AppCompat.Dialog"> <-- IMPORTANT!!! -->
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

步骤3:在实际活动中,以编程方式将主题设置为普通活动的主题或对话框的主题。我的 DialogActivity.java 如下:

package com.example.eric.questiondialog_artifact;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;

public class DialogActivity extends AppCompatActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        setTheme(R.style.AppTheme_Dialog); // can either use R.style.AppTheme_Dialog or R.style.AppTheme as deined in styles.xml
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog);
    }
}

你能否检查一下这个解决方案,应用目标 API 27 及以上版本。我在全屏主题下遇到了崩溃问题:“只有全屏活动才能请求方向”。当我将全屏设置为默认值并在运行时更改为对话框主题时,背景会变成黑色。如果预设对话框主题,则全屏会崩溃。 - Trung Nguyen
我刚在Android Pie (API 28)上测试了这个解决方案。当以全屏或对话框的形式通过编程方式启动Activity时,它似乎可以正常工作而不会崩溃。 - Eric
你能在Github上添加一个示例项目吗?谢谢。 - Trung Nguyen
1
@NamTrung 嘿,我终于开始创建这个答案的示例了。 - Eric
1
对于任何想改变活动尺寸的人,他必须在“style”中删除.FixedSize并更改“LinearLayout”的参数。 - Mohamed Ben Romdhane
显示剩余8条评论

2

如果您只是需要一个带有透明背景的主题来进行活动,只需使用以下内容:

<style name="Theme.Transparent" parent="android:Theme">
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:backgroundDimEnabled">false</item>
</style>

在你的AndroidManifest文件中应用这个样式就可以了。


0

虽然我来晚了,但是对于未来的用户来说,你需要在setTheme()之后调用下面的代码。这样做可以让这个活动背后的活动再次显示出来。一旦所有这样的活动都被重绘,

// setTheme()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            try {
            Method getActivityOptions = Activity.class.getDeclaredMethod("getActivityOptions");
            getActivityOptions.setAccessible(true);
            Object options = getActivityOptions.invoke(activity);

            Class<?>[] classes = Activity.class.getDeclaredClasses();
            Class<?> translucentConversionListenerClazz = null;
            for (Class clazz : classes) {
                if (clazz.getSimpleName().contains("TranslucentConversionListener")) {
                    translucentConversionListenerClazz = clazz;
                }
            }
            Method convertToTranslucent = Activity.class.getDeclaredMethod("convertToTranslucent",
                    translucentConversionListenerClazz, ActivityOptions.class);
            convertToTranslucent.setAccessible(true);
            convertToTranslucent.invoke(activity, null, options);
        } catch (Throwable t) {
        }
        } else {
            try {
            Class<?>[] classes = Activity.class.getDeclaredClasses();
            Class<?> translucentConversionListenerClazz = null;
            for (Class clazz : classes) {
                if (clazz.getSimpleName().contains("TranslucentConversionListener")) {
                    translucentConversionListenerClazz = clazz;
                }
            }
            Method method = Activity.class.getDeclaredMethod("convertToTranslucent",
                    translucentConversionListenerClazz);
            method.setAccessible(true);
            method.invoke(activity, new Object[] {
                null
            });
        } catch (Throwable t) {
        }
        }

-1

dailog.setMessage(...);之前尝试这些代码

Dialog id  = new AlertDialog.Builder(this,AlertDialog.THEME_DEVICE_DEFAULT_DARK);

Dialog ID = new AlertDialog.Builder(this,AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);

//Default theme 

尝试使用以下代码来设置旧主题:

Dialog ID = new AlertDialog.Builder(this,AlertDialog.THEME_TRADITIONAL);

尝试使用以下代码来设置KITKAT主题:
Dialog ID = new AlertDialog.Builder(this,AlertDialog.THEME_DEVICE_DEFAULT_DARK); //Dark


Dialog ID = new AlertDialog.Builder(this,AlertDialog.THEME_HOLO_LIGHT);

尝试这些实用的代码: 示例
    dialog = new AlertDialog.Builder(this);
            dialog = new AlertDialog.Builder(this,AlertDialog.THEME_DEVICE_DEFAULT_DARK);
            dialog.setTitle("HAI");
            dialog.setMessage("look");
            dialog.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
Toast toast= Toast.makeText(getApplicationContext(), "This is exmaple theme", Toast.LENGTH_LONG);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接