Android:自动为xml中的所有ID创建变量

21
我注意到Android开发中最繁琐的部分之一是布局设计,即使使用布局构建器。
在设置了图形和布局之后,将布局元素与变量关联起来非常繁琐,例如:ImageButton myButton = (ImageButton)findViewById(R.id.myButton); 在更大的布局中,这些东西很容易混淆(记住元素名称),需要以任何顺序添加更多变量会令人沮丧。
为了稍微缓解这个问题,如果我在XML中声明的所有ID都能自动关联到它们正确的变量,并且所有这些数据类型已经包含在该类中,那将非常方便。
是否已有现成的工具可以实现这一点?
例如,如果我写入:
 <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/myButton" android:id="@+id/myButton"></ImageButton>

那么我希望包含此布局的类已经具备

 import android.ImageButton;

 ImageButton myButton;

 myButton = (ImageButton)findViewById(R.id.myButton);

这是一个设置还是功能请求?我正在使用Eclipse IDE,这会非常方便。


1
我不知道有没有,但如果有的话会很酷——也许你可以写一个? :) - Jords
当然,我从未制作过自动代码监听器... 我该如何开始? Android AVD 的那部分也是开源的吗? - CQM
5个回答

35

我制作了一个工具,可以自动生成Java代码来将布局XML和程序逻辑联系在一起。

基本上,它接受一个XML布局,并为您即时生成所有必要的Java代码。支持基本成员变量、ViewHolder模式、ArrayAdapter、CursorAdapter和RoboGuice代码类型。

你可以在这里找到它:Android Layout Finder | Buzzing Android

编辑:原网站已关闭,但可以在此处找到镜像:https://dusunboy.github.io/android-layout-finder/


感谢您发布答案!请务必仔细阅读有关自我推广的FAQ。还请注意,每次链接到您自己的网站/产品时,必须发布免责声明。 - Andrew Barber
很棒的工具。将您的答案与安迪的答案结合起来,并使用@ViewById注释生成Java代码,如何?在您的工具中选择这两种方式之一可能是一个选项。 - sam
真是太棒了。做得好!我已经很久以前就在找它了 :-) - Ravindra Shekhawat
这应该被接受为答案,因为这正是最初所问的。 - L93
非常好的工具,感谢与我们分享,帮助节省时间,无需使用任何第三方库。如果您能更新它以在使用SDK 26时删除视图转换,那将非常好。 - Amr El Aswar
干得好,兄弟。 - Gokul Sreenivasan

15

尝试使用Android Annotations。它提供了有用的注释来替换样板代码。

例如,查看@ViewById 文档: 只需声明已注释的字段即可。

@ViewById
EditText myEditText;

@ViewById(R.id.myTextView)
TextView textView;

它替换了

EditText myEditText;

TextView textView;
@Override
public void onCreate(Bundle savedInstanceState) {
    [...]
    myEditText = (EditText) findViewById(R.id.myEditText);
    textView = (TextView) findViewById(R.id.myTextView);
}

看起来很不错,但我正在使用Xamarin Android进行编程,而不是本地Android,10年后似乎还没有实现 :/ - Windgate

2

自从 Android Studio 3.6 版本,官方正式添加了自动视图绑定的支持,可以为每个 XML 布局文件生成一个绑定类。这些类包含对应布局中所有具有 ID 的视图的直接引用。

您可以通过修改 build.gradle 文件来启用它。

android {
  ...
  viewBinding {
      enabled = true
  }
}

如果为模块启用了视图绑定,则为该模块包含的每个 XML 布局文件生成一个绑定类。每个绑定类都包含对根视图和具有 ID 的所有视图的引用。
例如,给定名为 result_profile.xml 的布局文件,则生成的绑定类将被称为 ResultProfileBinding
示例:
private ResultProfileBinding binding;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = ResultProfileBinding.inflate(getLayoutInflater());
    View view = binding.getRoot();
    setContentView(view);
}

请查看官方文档此处

0

这是一个功能请求。它非常好,我认为它会非常有用;另一方面,有些情况下它可能不太适用,例如如果您动态膨胀视图并且在编译时不知道特定活动将膨胀哪个视图。然而,我倾向于认为这种情况是例外而不是规则。

最简单的方法是编写一个脚本,扫描您的布局XML文件以查找具有ID的组件,并创建具有正确定义的.java文件。然后,您的活动可以从这些自动生成的类派生。就像这样:

当您的脚本处理后,它会生成一个类:

class FooBarLayoutActivityBase extends Activity ... {
  protected ImageButton myButton;

  FooBarLayoutActivityBase() {
    myButton = (ImageButton)findViewById(R.id.myButton);
  }
}

然后,您可以简单地从该基类继承以使用组件...

脚本方法简单,不需要深入研究工具链的代码 - 但您也可以直接在ADT插件中执行。


你的代码将无法正常工作,因为findViewById()方法只能在调用setContentView方法之后使用。 - Nikolay Ivanov

0

更新Android View Binding (AVB) 代码生成器,它正好做我要说的事情 :|


正则表达式!

我曾经遇到过同样的问题,试图自己解决,于是在亲爱的Android Studio中使用了正则表达式进行搜索和替换。个人使用ButterKnife进行Java注入依赖,但更重要的部分是如何自动化将XML布局中的ID转换为Java对象的过程。这有点像Android Layout Finder | Buzzing Android(该网站有更多功能,但已经过时:()的答案,但具有注释结果。

  1. 进入你的 xml 文件,使用正则表达式 (\+id/.*) 选择所有的 id。首先按下 Ctrl + F 打开搜索面板,确保勾选了 Regex 复选框。然后在文本框中输入正则表达式 (\+id/.*),接着按下 Ctrl + Alt + Shift + J 选择所有出现的结果。现在按下 Ctrl + C 将它们复制 (你知道这个快捷键)。

例如,我有这个布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layoutDirection="rtl"
    tools:context=".jobs.return_from_entrance.ReturnFromEntranceActivity"
    tools:ignore="HardcodedText">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <GridLayout
            android:id="@+id/return_entrance_grid_layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:columnCount="2"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="شماره برگشت" />

            <TextView
                android:id="@+id/return_entrance_return_entrance_number_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="123123123"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="@string/owner_of_cargo" />

            <TextView
                android:id="@+id/return_entrance_owner_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="الجی"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="تاریخ و زمان" />

            <TextView
                android:id="@+id/return_entrance_time_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="12/12/12/ 12:12"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="نوع حواله" />

            <TextView
                android:id="@+id/return_entrance_kind_of_order_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="حواله"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="خریدار" />


            <TextView
                android:id="@+id/return_entrance_buyer_name_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="علی امیدی"
                android:textColor="@android:color/black"
                android:textSize="18sp" />


            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="مقصد" />

            <TextView
                android:id="@+id/return_entrance_destination_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="آزادی"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="وزن ناخالص" />

            <TextView
                android:id="@+id/return_entrance_gross_weight_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="123"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="شماره جواز" />

            <TextView
                android:id="@+id/return_entrance_permission_number_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="126545643"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="شماره بارنامه" />

            <TextView
                android:id="@+id/return_entrance_waybill_number_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="654"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="زمان ورود" />

            <TextView
                android:id="@+id/return_entrance_enter_time_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="21/12/12 22:22"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="زمان خروج" />

            <TextView
                android:id="@+id/return_entrance_exit_time_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="21/12/12 22:22"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="محوطه بارگیری" />

            <TextView
                android:id="@+id/return_entrance_load_location_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="حیاط"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="نیاز به جرثقیل" />

            <TextView
                android:id="@+id/return_entrance_is_crane_needed_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="ندارد"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="نیاز به لیفتراک" />

            <TextView
                android:id="@+id/return_entrance_is_forklift_needed_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="ندارد"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <CheckBox
                android:id="@+id/return_entrance_internal_return_entrance_checkbox"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="@dimen/margin_large"
                android:layout_marginStart="@dimen/margin_medium"
                android:layout_marginTop="@dimen/margin_large"
                android:text="خروج داخلی" />

            <View
                android:layout_width="0dp"
                android:layout_height="0dp" />

            <CheckBox
                android:id="@+id/return_entrance_warehouse_delivery_checkbox"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="@dimen/margin_large"
                android:layout_marginStart="@dimen/margin_medium"
                android:layout_marginTop="@dimen/margin_large"
                android:text="تحویل در انبار" />

            <View
                android:layout_width="0dp"
                android:layout_height="0dp" />

            <Button
                android:id="@+id/return_entrance_location_delivery_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/margin_large"
                android:text="تحویل در محل" />

            <View
                android:layout_width="0dp"
                android:layout_height="0dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="عکس راننده" />

            <ImageView
                android:id="@+id/return_entrance_driver_image_view"
                android:layout_width="120dp"
                android:layout_height="120dp"
                android:layout_gravity="center"
                android:layout_marginTop="@dimen/item_margin"
                android:src="@drawable/ic_account_circle_black_24dp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="@string/name_of_driver" />

            <TextView
                android:id="@+id/return_entrance_name_of_driver_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="علی امیدی"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="@string/kind_of_car" />

            <TextView
                android:id="@+id/return_entrance_kind_of_car_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="وانت مزدا"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="@string/plaque" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/margin_large"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/return_entrance_plaque_2digit_text_view"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@drawable/plaque_background"
                    android:padding="10dp"
                    android:text="11"
                    android:textColor="@android:color/black"
                    android:textSize="10pt"
                    android:textStyle="bold" />

                <TextView
                    android:id="@+id/return_entrance_plaque_6digit_text_view"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@drawable/plaque_background"
                    android:padding="10dp"
                    android:text="999ج77"
                    android:textColor="@android:color/black"
                    android:textSize="10pt"
                    android:textStyle="bold" />
            </LinearLayout>
        </GridLayout>

        <Button
            android:id="@+id/return_entrance_barcode_scan_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_margin="@dimen/margin_small"
            android:drawableStart="@drawable/ic_barcode"
            android:padding="@dimen/margin_medium"
            android:text="@string/scan_barcode"
            android:textSize="18sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/return_entrance_grid_layout" />

        <android.support.v7.widget.RecyclerView
            android:id="@+id/return_entrance_cargo_list_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/return_entrance_barcode_scan_button" />

        <GridLayout
            android:id="@+id/return_entrance_bottom_grid_layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:columnCount="2"
            android:layoutDirection="rtl"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/return_entrance_cargo_list_recycler_view">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="میزان موجودی کالای قابل تحویل" />

            <TextView
                android:id="@+id/return_entrance_deliverable_availability_text_view"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/text_margin"
                android:text="50"
                android:textColor="@android:color/black"
                android:textSize="18sp" />

        </GridLayout>

        <LinearLayout
            android:id="@+id/return_entrance_bottom_linear_2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/return_entrance_bottom_grid_layout">

            <Button
                android:id="@+id/return_entrance_cost_report_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/margin_medium"
                android:text="گزارش هزینه" />

            <Button
                android:id="@+id/return_entrance_confirm_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/margin_medium"
                android:text="تایید برگشت" />

        </LinearLayout>

    </android.support.constraint.ConstraintLayout>
</android.support.v4.widget.NestedScrollView>

好多文本视图要填充,是吧?

  • 进入你的Java类并粘贴复制的ID(Ctrl + V)。这样我们就有了一堆ID,我们想把它们变成Java对象。 在我们的例子中,我的ID会像这样:

    +id/return_entrance_grid_layout"
    +id/return_entrance_return_entrance_number_text_view"
    +id/return_entrance_owner_text_view"
    +id/return_entrance_time_text_view"
    ...
    
  • 是时候进行查找和替换了!首先我们按下Ctrl + R打开查找和替换面板。(确保选中了REGEX复选框) 现在我要进行一些查找和替换操作,以获得理想的结果:

    找到:\+id/(.*)" 替换为:@BindView(R.id.$1) 这样我们就有了下面的内容:
    @BindView(R.id.return_entrance_grid_layout)
    @BindView(R.id.return_entrance_return_entrance_number_text_view)
    @BindView(R.id.return_entrance_owner_text_view)
    @BindView(R.id.return_entrance_time_text_view)
    ...
    

    现在是时候定义每个变量类型并为它们命名了。我的 XML 命名采用WHERE_DESCRIPTION_WHAT模式,(类似这样的)。因此,对于变量名称,我想要删除WHERE部分。然后定义对象类型。所以我们开始吧:

    第四步: 查找:(@BindView\(R\.id\.return_entrance_(.*)_text_view\)) 替换为:$1 TextView $2TextView; 结果将会是:
    @BindView(R.id.return_entrance_grid_layout)
    @BindView(R.id.return_entrance_return_entrance_number_text_view) TextView return_entrance_numberTextView;
    @BindView(R.id.return_entrance_owner_text_view) TextView ownerTextView;
    @BindView(R.id.return_entrance_time_text_view) TextView timeTextView;
    @BindView(R.id.return_entrance_kind_of_order_text_view) TextView kind_of_orderTextView;
    ...
    

    (只需按下Ctrl + Alt + L即可重新格式化您的代码) 名称很丑:(。所以我们将其转换为驼峰式命名法!:

    找到:TextView \b(.*)_(.*),替换为:TextView $1\u$2,结果如下:
    @BindView(R.id.return_entrance_owner_text_view)
    TextView ownerTextView;
    @BindView(R.id.return_entrance_time_text_view)
    TextView timeTextView;
    @BindView(R.id.return_entrance_kind_of_order_text_view)
    TextView kind_ofOrderTextView;
    @BindView(R.id.return_entrance_buyer_name_text_view)
    TextView buyerNameTextView;
    @BindView(R.id.return_entrance_destination_text_view)
    TextView destinationTextView;
    

    如果您重复最后一部分,任何具有多个下划线的名称,每个_将替换为下一个字符的大写字母。 因此,在此示例中,如果我执行查找:TextView \b(.*)_(.*) 替换为:TextView $1\u$2 再次,我的TextView kind_ofOrderTextView; 将变成 TextView kindOfOrderTextView;

    这可能看起来有点复杂,但当您习惯了它,它会变得非常快速和更有用!例如,在MVP中,您有一个与TextViews相同的String名称的Model,因此您可以使用类似的方法设置所有文本从Model...


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