web-dev-qa-db-ja.com

androidカスタムビューでのデータバインディング

Androidデータバインディングガイド では、アクティビティまたはフラグメント内のバインディング値について説明していますが、カスタムビューでデータバインディングを実行する方法はありますか?

私は次のようなことをしたいです:

<LinearLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <com.mypath.MyCustomView
        Android:id="@+id/my_view"
        Android:layout_width="match_parent"
        Android:layout_height="40dp"/>

</LinearLayout>

my_custom_view.xml

<layout>

<data>
    <variable
        name="myViewModel"
        type="com.mypath.MyViewModelObject" />
</data>

<LinearLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <TextView
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="@{myViewModel.myText}" />

</LinearLayout>

</layout>

カスタムビューでカスタム属性を設定してこれを行うことは可能に見えますが、バインドする値がたくさんある場合、これはすぐに面倒になります。

私がやろうとしていることを達成する良い方法はありますか?

40
Dave

カスタムビューで、通常はレイアウトを展開します。通常は、設定する属性のセッターを提供します。

private MyCustomViewBinding mBinding;
public MyCustomView(...) {
    ...
    LayoutInflater inflater = (LayoutInflater)
        context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mBinding = MyCustomViewBinding.inflate(inflater);
}

public void setMyViewModel(MyViewModelObject obj) {
    mBinding.setMyViewModel(obj);
}

次に、レイアウトでそれを使用します:

<layout xmlns...>
    <data>
        <variable
            name="myViewModel"
            type="com.mypath.MyViewModelObject" />
    </data>

    <LinearLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent">

        <com.mypath.MyCustomView
            Android:id="@+id/my_view"
            app:myViewModel="@{myViewModel}"
            Android:layout_width="match_parent"
            Android:layout_height="40dp"/>

    </LinearLayout>
</layout>

上記では、setMyViewModelという名前のセッターが存在するため、app:myViewModelの自動バインディング属性が作成されます。

54
George Mount

まず、このカスタムビューが既に<include>アクティビティなどの別のレイアウトで。タグが予期しない値であるという例外が発生します。データバインディングは既にバインディングを実行しているので、設定は完了です。

onFinishInflateを使用してバインドを実行しようとしましたか? (Kotlinの例)

override fun onFinishInflate() {
    super.onFinishInflate()
    this.dataBinding = MyCustomBinding.bind(this)
}

ビューでバインディングを使用する場合、プログラムで作成することはできません。少なくとも、可能な場合でも両方をサポートすることは非常に複雑です。

6
androidguy

georgeによって提示されたソリューションに従って Androidスタジオはカスタムビューをレンダリングできなくなりました。その理由は、次のコード:

public MyCustomView(...) {
    ...
    LayoutInflater inflater = (LayoutInflater)
        context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mBinding = MyCustomViewBinding.inflate(inflater);
}

バインディングはインフレーションを処理すると思いますが、グラフィカルエディターはそれを気に入らなかったと思います。

私の特定のユースケースでは、ビューモデル全体ではなく、単一のフィールドをバインドしたかったのです。私は(kotlin incoming)を思いつきました:

class LikeButton @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

    val layout: ConstraintLayout = LayoutInflater.from(context).inflate(R.layout.like_button, this, true) as ConstraintLayout

    var numberOfLikes: Int = 0
      set(value) {
          field = value
          layout.number_of_likes_tv.text = numberOfLikes.toString()
      }
}

「いいね」ボタンは、画像とテキストビューで構成されています。テキストビューには、データバインドを介して設定するいいね!の数が保持されます。

NumberOfLikesのセッターを次のxmlの属性として使用することにより、データバインディングは自動的に関連付けを行います。

<views.LikeButton
  Android:id="@+id/like_btn"
  Android:layout_width="wrap_content"
  Android:layout_height="wrap_content"
  app:numberOfLikes="@{story.numberOfLikes}" />

さらに読む: https://medium.com/google-developers/Android-data-binding-custom-setters-55a25a7aea47

4
Upvote

ここにはすでにいくつかの良い答えがありますが、私が信じている最も簡単なものを提供したいと思いました。

他のレイアウトと同様に、周囲のレイアウトタグでカスタムコントロールを作成します。たとえば、次のツールバーを参照してください。これは各アクティビティクラスで使用されます

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

<data>
    <variable name="YACustomPrefs" type="com.appstudio35.yourappstudio.models.YACustomPreference" />
</data>

<Android.support.design.widget.CoordinatorLayout
    Android:layout_width="match_parent"
    Android:layout_height="?attr/actionBarSize">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:theme="@style/YATheme.AppBarOverlay">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            Android:background="@color/colorPrimary"
            app:popupTheme="@style/YATheme.PopupOverlay"/>

    </Android.support.design.widget.AppBarLayout>

</Android.support.design.widget.CoordinatorLayout>

これで、このカスタムレイアウトはすべてのアクティビティの子になります。 onCreateバインディング設定では、単純にそのように扱います。

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    binding.yaCustomPrefs = YACustomPreference.getInstance(this)
    binding.toolbarMain?.yaCustomPrefs = YACustomPreference.getInstance(this)
    binding.navHeader?.yaCustomPrefs = YACustomPreference.getInstance(this)

    binding.activity = this
    binding.iBindingRecyclerView = this
    binding.navHeader?.activity = this

    //local pointer for notify txt badge
    txtNotificationCountBadge = txtNotificationCount

    //setup notify if returned from background so we can refresh the drawer items
    AppLifeCycleTracker.getInstance().addAppToForegroundListener(this)

    setupFilterableCategories()
    setupNavigationDrawer()
}

親をするのと同時に子供のコンテンツを設定し、それはすべてドット表記のアクセスを通じて行われます。ファイルがレイアウトタグで囲まれ、名前を付けていれば、簡単です。

これで、カスタムクラスに独自のコードインフレーションが関連付けられている場合、onCreateまたはコンストラクターで独自のバインディングを簡単に実行できますが、状況はわかります。独自のクラスがある場合は、コンストラクタで次のものをスローして、名前付きバインディングクラスに一致させます。 Pascal casedのレイアウトファイルの命名規則に従っているため、簡単に検索して自動入力できます。

    LayoutInflater inflater = (LayoutInflater)
    context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mBinding = NameOfCustomControlBinding.inflate(inflater);

お役に立てば幸いです。

0
Sam