switch case语句错误:case表达式必须是常量表达式。

145

昨天我的switch-case语句运行得非常完美。但是今天早上我运行代码时,Eclipse用红色下划线标出了case语句,并显示错误信息:case表达式必须是常量表达式,它是常量,我不知道发生了什么。以下是我的代码:

public void onClick(View src)
    {
        switch(src.getId()) {
        case R.id.playbtn:
            checkwificonnection();
            break;

        case R.id.stopbtn:
            Log.d(TAG, "onClick: stopping srvice");
            Playbutton.setImageResource(R.drawable.playbtn1);
            Playbutton.setVisibility(0); //visible
            Stopbutton.setVisibility(4); //invisible
            stopService(new Intent(RakistaRadio.this,myservice.class));
            clearstatusbar();
            timer.cancel();
            Title.setText(" ");
            Artist.setText(" ");
            break;

        case R.id.btnmenu:
            openOptionsMenu();
            break;
        }
    }

所有的R.id.int都被标记成了红色下划线。


你能提供R.id.playbtn的定义吗?所有东西都是静态和最终的吗? - Thomas
2
可能是您删除/修改了布局,导致这些ID不再存在或类似的情况... - Vicente Plata
R通常由IDE/开发工具生成,因此通常适用于使用的Android版本。 - cHao
我的R.id.*都很好,并且存在于Android的gen类中,也在主布局中。 - HeartlessArchangel
10个回答

305

在一个普通的Android项目中,资源R类中的常量声明如下:

public static final int main=0x7f030004;

然而,从ADT 14开始,在库项目中它们将被声明为如下所示:

public static int main=0x7f030004;
换句话说,在库项目中常量不是最终的。因此,您的代码将不再编译。
解决方法很简单:将switch语句转换为if-else语句。
public void onClick(View src)
{
    int id = src.getId();
    if (id == R.id.playbtn){
        checkwificonnection();
    } else if (id == R.id.stopbtn){
        Log.d(TAG, "onClick: stopping srvice");
        Playbutton.setImageResource(R.drawable.playbtn1);
        Playbutton.setVisibility(0); //visible
        Stopbutton.setVisibility(4); //invisible
        stopService(new Intent(RakistaRadio.this,myservice.class));
        clearstatusbar();
        timer.cancel();
        Title.setText(" ");
        Artist.setText(" ");
    } else if (id == R.id.btnmenu){
        openOptionsMenu();
    }
}

http://tools.android.com/tips/non-constant-fields

你可以使用以下方法快速将 switch 语句转换为 if-else 语句:

在 Eclipse 中
将光标移到 switch 关键字上,按下 Ctrl + 1,然后选择

Convert 'switch' to 'if-else'.

在 Android Studio 中
将光标移到 switch 关键字上,按下 Alt + Enter,然后选择

Replace 'switch' with 'if'.


1
我将我的 switch-case 语句改为 else-if 语句。这让我想到,我创建了一个新的 Android 项目并使用了 switch-case 语句,它也能正常工作。 - HeartlessArchangel
1
可能是因为你的第一个项目使用了库项目,而你的新项目没有。 - Benito Bertoli
7
至少 Eclipse 可以自动将 switch 转换为 if/else。点击 switch 关键字,然后按下 Ctrl + 1。 - Darren Cato
太棒了!我会把它加入到我的答案中。 - Benito Bertoli
3
编译器需要在编译时知道表达式的值。没有使用 final 关键字,变量可以在运行时改变。 - Benito Bertoli
显示剩余6条评论

52

取消项目属性中的“Is Library”选项对我有用。


2
右键单击您的项目名称。然后单击属性->Android。在弹出窗口的右下方是一个名为“库”的部分。如果选中了“是库”选项,请取消选中,如果您不想让您的项目成为库项目。然后进行清理和重建。如果您希望它成为库项目,则必须将开关更改为if else条件,如其他地方所述。 - VikingGlen
5
为什么一个库项目要标记为“Is Library”是有原因的。但这并不是解决问题的正确方法 - 这会破坏你的 Android 项目结构,使应该作为库的内容表现得像常规应用程序一样。 - ADTC

13

可以通过以下方式解决:

  1. 分配给整数
  2. 变量声明为final

示例:

public static final int cameraRequestCode = 999;

希望这能帮到你。


11

这个问题的简单解决方案是:

点击开关,然后按下CTL+1,它将把你的开关改为if-else块语句,并解决你的问题。


9

自ADT 14以来,R.id.*已不再声明为final static int,因此您不能在switch case结构中使用。相反,您可以使用if-else语句。


是的,我在tools.android.com上读到了这篇文章,我还尝试创建了一个新项目并使用了上面的代码,一切都很正常。怎么回事? - HeartlessArchangel
1
请查看http://tools.android.com/recent/buildchangesinrevision14中的“Library Project Revamp”部分。 - Blackbelt
7
为什么他们做出这个改变,这毫无意义。 - Andrew S

8

2023年以后的注意事项:

使用Android Gradle插件8.0.0后,所有的R-class资源默认不再声明为final/constant(因此无法在switch语句中使用)。 如果你在Android Studio中使用AGP升级向导,它将在你的gradle.properties文件中添加以下行来保持旧的行为:

android.nonFinalResIds=false

但是如果你不使用向导或者从一个新项目开始,这可能会让你感到惊讶。手动添加上述行或者使用if/else语句代替。

7
如何保留漂亮的开关而不使用 if-else 的替代方案如下:
private enum LayoutElement {
    NONE(-1),
    PLAY_BUTTON(R.id.playbtn),
    STOP_BUTTON(R.id.stopbtn),
    MENU_BUTTON(R.id.btnmenu);

    private static class _ {
        static SparseArray<LayoutElement> elements = new SparseArray<LayoutElement>();
    }

    LayoutElement(int id) {
        _.elements.put(id, this);
    }

    public static LayoutElement from(View view) {
        return _.elements.get(view.getId(), NONE);
    }

}

所以在您的代码中,您可以这样做:

public void onClick(View src) {
    switch(LayoutElement.from(src)) {
    case PLAY_BUTTTON:
        checkwificonnection();
        break;

    case STOP_BUTTON:
        Log.d(TAG, "onClick: stopping srvice");
        Playbutton.setImageResource(R.drawable.playbtn1);
        Playbutton.setVisibility(0); //visible
        Stopbutton.setVisibility(4); //invisible
        stopService(new Intent(RakistaRadio.this,myservice.class));
        clearstatusbar();
        timer.cancel();
        Title.setText(" ");
        Artist.setText(" ");
        break;

    case MENU_BUTTON:
        openOptionsMenu();
        break;
    }
}

枚举是静态的,所以它的影响非常有限。唯一需要关注的问题是涉及到双重查找(首先在内部的SparseArray上查找,然后在switch表上查找)。

话虽如此,如果需要保留id的引用并流畅地获取项目,则也可以利用此枚举...不过这是另一回事了。


在Android中,枚举类型由于内存膨胀而不被鼓励使用;这也是它们从未在AOSP中使用的主要原因 - 也是你到处看到整数的原因。 - ADTC
1
https://dev59.com/12435IYBdhLWcg3w50j4 - pablisco

3

当我在一个函数中使用switch,并且变量是在我的类中声明的时,它会抛出这个错误:

private void ShowCalendar(final Activity context, Point p, int type) 
{
    switch (type) {
        case type_cat:
            break;

        case type_region:
            break;

        case type_city:
            break;

        default:
            //sth
            break;
    }
}

问题在我将类开头的变量声明为final后得到解决:
final int type_cat=1, type_region=2, type_city=3;

1
在这种情况下,enum是比int更好的选择。方法的调用者将无法使用无效类型调用函数。 - nhahtdh
我有特定的int类型,所以如果我使用ints就可以了。 不过我想知道一个使用枚举的例子:D - aimiliano
我有特定的整数类型,所以如果我使用整数就可以了。关于枚举示例:http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html - nhahtdh
我的意思是函数中传入的int变量类型始终是这三种类型之一,因此不会出现任何问题。感谢枚举示例 :) - aimiliano
我的意思是函数中传入的整数变量类型始终是这三种类型之一,因此不会出现任何问题。这是你的假设。其他人可能会以任意数字错误地调用该函数。使用“枚举”,您无需假设,语言本身就会强制执行。 - nhahtdh
嗯,如果有人不知道如何调用函数或传递错误的整数输入,则您的论点是正确的,但是如果有人将默认分支设置为返回false,并将函数的返回值更改为布尔值,则也可以发生这种情况。无论如何,我认为最好的方法是使用由用户预设的枚举变量来调用函数。 - aimiliano

2
我想提一下,当我尝试将一个库添加到我的项目中时,遇到了同样的情况。突然间所有的switch语句都显示错误!现在我尝试删除我添加的库,但仍然无法解决问题。然而,“当我清理项目”时,所有的错误就消失了!

0

只需将您的变量声明为final


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