无需强制转换findViewById的结果?


152

最近,我发现AndroidStudio提醒我删除一些类强制转换。我记得在过去,我们必须强制转换findViewById的结果,但是现在没有必要了。

findViewById的结果仍然是View,所以我想知道为什么我们不需要强制转换类?

我找不到提到的任何文档,有人可以找到任何文档吗?


7
因为现在是<T extends View> T findViewById(int id)
Selvin'7

您需要在View类中不存在任何操作的情况下进行强制转换,例如在ImageView的情况下,如果要使用setImageResource,则需要使用imageView强制转换findViewById
Gagan Deep

但是,如果删除了“冗余”转换,一眼就知道变量类型,我感到有些不便。
水果

Answers:


235

从API 26开始,findViewById将推理用作其返回类型,因此您不再需要强制转换。

旧定义:

View findViewById(int id)

新定义:

<T extends View> T findViewById(int id)

因此,如果您compileSdk年满26岁,则意味着您可以使用此:)


谢谢,还有另一个问题。我在sdk管理器中找不到sdk26的资源,所以请问在哪里可以找到这个新定义?
Eric Zhao

17
如果删除演员表,我们的应用仍可以在较低的设备上运行,对吗?
user1032613

17
@ user1032613:是的,这些应用程序仍然可以在较低的设备上运行,没有任何问题。
Alireza Noorali

1
如果类型错误,会抛出异常吗?
fobbymaster

1
就像布局文件中的视图是其他类型一样?是的,当然,它仍然是ClassCastException
爱德华·B

13

根据这篇文章

以下功能依赖于Java的泛型自动类型推断,从而消除了手动转换的需要:

protected <T extends View> T findViewById(@IdRes int id) {
    return (T) getRootView().findViewById(id);
}

11

在旧版本中:

AutoCompleteTextView name = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView);

从带有SDK 26的Android Studio 3.0中:

AutoCompleteTextView name = findViewById(R.id.autoCompleteTextView);

16
这没有提供问题的答案。
Wijay Sharma

1

如果您使用View类的常用属性(例如可见性)或某些常用方法(例如onClick()),Android Studio会提醒您删除转换

例如:

((ImageView) findViewById(R.id.image_car)).setVisibility(View.VISIBLE);

在这种情况下,您可以简单地编写:

findViewById(R.id.image_car).setVisibility(View.VISIBLE);

2
您仍然必须声明类型,而必须编写:findViewById <ImageView>(R.id.image_car).setVisibility(View.VISIBLE);
Slickelito

Android Studio提醒我们删除显式转换,因为它在Java的泛型自动类型推断的实现中已更改-它与您使用的方法无关。
zeroDivider

1

Android 0,清理铸造

谷歌在IO 2017中宣布的一件事是被称为``castaway''的东西:)。Android开发人员不必对findViewById()进行手动转换。例如,使用findViewById()获取文本视图的旧方法将是这样的。

TextView txtDesc = (TextView) findViewById(R.id.textViewDesc);
txtDesc.setText(getString(R.string.info_angkot_description));

虽然新的方式是这样的

TextView txtDesc = findViewById(R.id.textViewDesc);
txtDesc.setText(getString(R.string.info_angkot_description));

这是一个简单的更改。但是对于一个经验丰富的程序员来说,像这样的干净代码可以使您非常高兴,并且可以帮助您提高编码的心情:)

为此,您只需要在应用程序build.gradle中将项目的已编译sdk版本设置为版本26。

您仍然可以定位到较早的sdk版本,因此这是非侵入性的更改。

现在真正的问题是,如何清除这段时间所有使用强制转换的旧代码。尤其是当您拥有数百个活动文件时。您可以手动完成,也可以雇用实习生来完成。但是幸运的是,对于所有这些实习生,android studio已经准备好帮助我们。

当您插入插入符号(或单击冗余铸件)时,android studio将建议2个选项来处理冗余铸件。

首先,它将建议删除该多余的演员表,或者您可以选择清理代码。它将删除该文件的所有冗余转换。这样比较好,但是我们想要更多。我们不想打开每个文件,并一步一步地清理它。

使IntelliJ想法特别的一件事是称为意图动作的功能。您所要做的就是按ctrl + shift + A,然后键入clean。然后选择“代码清理”操作,然后选择整个项目范围。通过这几个简单的步骤,您的代码将变得更加整洁。

重要的一点是,您可以使用某些代码版本控制系统来执行此操作。这样,您可以比较intent操作所做的更改并还原所需的任何文件。

从原始帖子复制:

https://medium.com/@abangkis/android-0-clean-up-casting-c30acec56cef


1
问题是why,不是howThe result of findViewById is still View, so i want to know why we don't need to cast the class?
zeroDivider

“您所要做的就是按ctrl + shift + A,然后键入clean”。您所说的“清洁类型”是什么意思?如果你在这一点上开始打字,你会删除整个文件
隐形拉比

0

在的源代码中ViewGroup,存在return参数的强制转换。因此,无需再次投射:

@Nullable
public final <T extends View> T findViewById(@IdRes int id) {
    if (id == NO_ID) {
        return null;
    }
    return findViewTraversal(id);
}

@Override
protected <T extends View> T findViewTraversal(@IdRes int id) {
    if (id == mID) {
        return (T) this;  //###### cast to T
    }

    final View[] where = mChildren;
    final int len = mChildrenCount;

    for (int i = 0; i < len; i++) {
        View v = where[i];

        if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
            v = v.findViewById(id);

            if (v != null) {
                return (T) v; //###### cast to T
            }
        }
    }

    return null;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.