web-dev-qa-db-ja.com

Android DataBinding&MVVM-異なるレイアウトフォルダー内のレイアウトファイルに同じ名前を使用する

私はデータバインディングとMVVMを備えたアプリを開発してきました。

横向きモードでアプリに別のレイアウトを使用しようとしています。私が持っています:

layout/fragment_content.xml
layout-land/fragment_content.xml

どちらのレイアウトも同じビューで外観が異なり、次のように同じビューモデルからフィードを取得します。

<layout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto">

<data class="MyBinding">

    <variable
        name="viewModel"
        type="com.myapp.package.viewModel.VMFirst"/>

    <variable
        name="controlModel"
        type="com.myapp.package.viewModel.VMSecond"/>
</data>

<DIFFERENT CONTENT HERE>

すべてのビューとIDは両方のレイアウトに存在します。

問題は、コンパイルされないことです。エラーは単に"cannot find symbol method getViewModel"および他の変数のゲッター。

私がこれまでに試したこと:

  1. Layoutおよびlayout-landフォルダーの使用(失敗、エラーは上記で説明されています)

  2. レイアウトエイリアスの使用 レイアウトエイリアスの使用 ここで見つけました 問題199344:データバインディングがレイアウトエイリアスで機能しません 。このアプローチを試している間、xmlファイルでは何も変更しませんでした。これも失敗しました。エラーはCould not write to com.myapp.package.databinding.MyBinding

複数のレイアウトファイルでデータバインディングdataタグを使用することはできませんか?データバインディングを使用しているときに、状態ごとに異なるレイアウトを使用するには、何を使用する必要がありますか?ありがとう!

編集:削除class="MyBinding"エラーは変更されませんでした。

16
Mel

誰かがこの質問を検索した場合、2年後に同じことを試みましたが、今ではすべて正常に機能していることがわかりました。

layoutactivity_mainの下にレイアウトファイルlayout_sw600dpを作成しました。 layoutリソースの下のレイアウトは次のとおりです。

<layout 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">

    <variable
        name="small_variable"
        type="Integer"/>

    <androidx.constraintlayout.widget.ConstraintLayout
        Android:id="@+id/myRoot"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        tools:context=".MainActivity">

        <View
            Android:id="@+id/small_square"
            Android:layout_width="60dp"
            Android:layout_height="60dp"
            Android:background="@Android:color/holo_blue_bright"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

これはlayout_sw600dpフォルダの下のレイアウトです:

<?xml version="1.0" encoding="utf-8"?>

<layout 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">

    <variable
        name="big_variable"
        type="Long"/>

    <androidx.constraintlayout.widget.ConstraintLayout
        Android:id="@+id/myRoot"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        tools:context=".MainActivity">

        <View
            Android:id="@+id/big_square"
            Android:layout_width="60dp"
            Android:layout_height="60dp"
            Android:background="@Android:color/holo_blue_bright"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

どちらにもビューがありますが、それぞれに異なるIDがあります:small_squarebig_square

私は携帯電話とタブレットでプロジェクトを実行しています。これが私の発見です:

  • DataBindingは、異なるレイアウトフォルダー内の同じ名前のすべてのレイアウトファイルの下に[〜#〜] all [〜#〜]ビューと変数を含む実装を作成します。
  • すべてのレイアウトに存在するビューはnull許容ではなく、他のすべてのビューはnull許容です。上記のXMLでは、myRootnull許容ビューではありません Kotlinからのバインディングを使用する場合、big_squareおよびsmall_squarenull許容ビュー。変数は、すべてのレイアウトに存在するかどうかに関係なくnull許容です(これは予想される動作です)。
  • ファイルごとに異なるバインディングクラスに名前を付けることはできません。同じである必要があります(上記の例ではMainBinding、または定義しない場合はデフォルトでLayoutResourceName + Binding)。
  • バインディング実装のビューと変数の名前はキャメルケースです。つまり、私のsmall_variablesmall_squareはコード側でbinding.smallVariablebinding.smallSquareでした。
  • Kotlinを使用すると、binding.bigSquare?.operationのようなビューを使用できます。これは、タブレットや電話、ビューがnullかどうかを事前に確認する必要がないので便利です。
  • ヒントとして、フィールドが含まれているレイアウトが使用されない場合でも、bindingフィールドを割り当てることができます。コードでbinding.smallVariable = 3と言うと、割り当てが行われ、値が保存されます。注意するのは良いことだと思います。
4
Mel

私はアプリでMVVMを頻繁に使用しており、その周りにライブラリも構築しています。

私は、すべてのXMLに単一のViewModelがあるという規則に従います。また、viewmodel変数の名前はすべてのXMLで同じです。

したがって、あなたの場合、VMFirstVMSecondを含む別のViewModelクラスを作成できます。

public class ParentVM {
   VMFirst first;
   VMSecond second;
}

両方のXML(ポートレートとランドスケープ)は同じ名前になります。たとえば、activity_main.xml

<layout>
    <data>
      <variable 
          type="ParentViewModel"
          name="vm"/>
    </data>

その場合、MainActivityコードでチェックする必要はありません。

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ViewDataBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    binding.setVariable(BR.vm, new ParentViewModel());
}

これは機能します。

単一のViewModelの利点

実際、私はすべてのxmlで同じ変数名に従うので、基本クラスMvvmActivity自体にバインディングロジックを含めることができます。したがって、私のすべての活動は次のようになります。

public class MainActivity extends MvvmActivity {

    @NonNull
    @Override
    protected ViewModel createViewModel() {
        return new MainViewModel();
    }

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }
}

MvvmActivityの実装: MvvmActivity.Java

一定のデータバインディング変数を維持するもう1つの利点は、XML自体でRecyclerViewまたはViewPagerアダプターをセットアップできることです。詳細については、 XMLからのRecyclerViewのセットアップ を参照してください。

2
Manas Chaudhari

デフォルトでは、Bindingクラスはレイアウトファイルの名前に基づいて生成され、Pascalケースに変換され、「Binding」という接尾辞が付けられます。上記のレイアウトファイルはmain_activity.xmlであったため、生成クラスはMainActivityBindingでした。 -- バインディングデータ

コンパイル時に生成されます。

したがって、Javaコードで別のレイアウトを選択してください。

layout/
R.layout.activity_main
R.layout.activity_main_tablet

values/
    <bool name="is_mobile">true</bool>
    <bool name="is_tablet">false</bool>
values-w820dp/
    <bool name="is_mobile">false</bool>
    <bool name="is_tablet">true</bool>



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if(getResources().getBoolean(R.bool.is_mobile)) {
        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);            
    } else {
        ActivityMainTabletBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main_tablet);   
    }

}
0
qinmiao