記事

Last Modified:

RadListViewのitemHeightを動的に設定する方法 #NativeScript

RadListView.listViewLayoutにListViewGridLayoutを設定する場合、itemHeightを素直に書くことができない問題がある。 端的に言うとiosとandroidで方法が違うのでそれぞれの処理を書かないといけない模様。

基本構造として以下のXMLを与えてみる。

<lv:RadListView
    xmlns:lv="nativescript-ui-listview"
    xmlns="http://www.nativescript.org/tns.xsd">
  <lv:RadListView.listViewLayout>
    <lv:ListViewGridLayout />
  </lv:RadListView.listViewLayout>
  <lv:RadListView.itemTemplate>
    <AbsoluteLayout>
    </AbsoluteLayout>
  </lv:RadListView.itemTemplate>
</lv:RadListView>

これをiosで実行すると Warning: When using 'listViewLayout' of type ListViewGridLayout it is recommended to set it's 'itemHeight' property. となり、当然アイテムの高さは固定されない。 かと言ってitemHeightを設定すると、

    <lv:ListViewGridLayout itemHeight="200" />

今度はandroidで Warning: Setting the 'itemHeight' property of 'ListViewGridLayout' is not supported by the Android platform. となり、これまたアイテムの高さが固定されない。 どうやらandroidではアイテム自身が高さを調整しないといけないらしい。

これらを満たすために、 ios:,android:の接頭辞を付けてRadListView.bindingContextを見るようにしてみる。

<lv:RadListView
    loaded="onLoaded"
    layoutChanged="onLayoutChanged"
    xmlns:lv="nativescript-ui-listview"
    xmlns="http://www.nativescript.org/tns.xsd">
  <lv:RadListView.listViewLayout>
    <lv:ListViewGridLayout ios:itemHeight="{{ $parents['RadListView'].itemHeight }}" />
  </lv:RadListView.listViewLayout>
  <lv:RadListView.itemTemplate>
    <AbsoluteLayout android:height="{{ $parents['RadListView'].itemHeight }}">
    </AbsoluteLayout>
  </lv:RadListView.itemTemplate>
</lv:RadListView>
import { EventData, Observable } from 'tns-core-modules/data/observable';
import { RadListView } from 'nativescript-ui-listview';

class MyViewModel extends Observable {
  itemHeight = 0;
}

export function onLoaded(args: EventData): void {
  const view = args.object as RadListView;
  const vm = new MyViewModel();
  view.bindingContext = vm;
}

export function onLayoutChanged(args: EventData): void {
  const view = args.object as RadListView;
  const vm = view.bindingContext as MyViewModel;
  vm.itemHeight = ...;
}

ところがこれではiosでダメなのです。こちらのIssueにある通り $parents を使わずに直接バインドしろとのこと。

一応解決

色々やり方はあると思うけど、比較的シンプルなのは同じMyViewModelを見る方法だと思うのでそのように実装した。

<lv:RadListView
    loaded="onLoaded"
    layoutChanged="onLayoutChanged"
    xmlns:lv="nativescript-ui-listview"
    xmlns="http://www.nativescript.org/tns.xsd">
  <lv:RadListView.listViewLayout>
    <lv:ListViewGridLayout ios:itemHeight="{{ itemHeight }}" />
  </lv:RadListView.listViewLayout>
  <lv:RadListView.itemTemplate>
    <AbsoluteLayout android:height="{{ $parents['RadListView'].itemHeight }}">
    </AbsoluteLayout>
  </lv:RadListView.itemTemplate>
</lv:RadListView>
import { EventData, Observable } from 'tns-core-modules/data/observable';
import { RadListView } from 'nativescript-ui-listview';

class MyViewModel extends Observable {
  private _itemHeight = 0;
  get itemHeight(): number {
    return this._itemHeight;
  }
  set itemHeight(value: number) {
    this._itemHeight = value;
    this.notifyPropertyChange('itemHeight', this._itemHeight); // for ios
  }
}

export function onLoaded(args: EventData): void {
  const view = args.object as RadListView;
  const vm = new MyViewModel();

  view.bindingContext = vm;
  view.listViewLayout.bindingContext = vm; // for ios
}

export function onLayoutChanged(args: EventData): void {
  const view = args.object as RadListView;
  const vm = view.bindingContext as MyViewModel;

  vm.itemHeight = ...;
}

ところがこれはiosの警告が消えないのです。どうやらios:itemHeight が動的だからっぽいです。 実害は無いんですが、ちょっと気持ち悪いのでバインドしない方法でもやってみました。

解決

<lv:RadListView
    loaded="onLoaded"
    layoutChanged="onLayoutChanged"
    xmlns:lv="nativescript-ui-listview"
    xmlns="http://www.nativescript.org/tns.xsd">
  <lv:RadListView.listViewLayout>
    <lv:ListViewGridLayout ios:itemHeight="1" /><!-- 0 だとやはり警告されるので -->
  </lv:RadListView.listViewLayout>
  <lv:RadListView.itemTemplate>
    <AbsoluteLayout android:height="{{ $parents['RadListView'].itemHeight }}">
    </AbsoluteLayout>
  </lv:RadListView.itemTemplate>
</lv:RadListView>
import * as application from 'tns-core-modules/application';
import { EventData, Observable } from 'tns-core-modules/data/observable';
import { RadListView, ListViewGridLayout } from 'nativescript-ui-listview';

class MyViewModel extends Observable {
  itemHeight = 0;
}

export function onLoaded(args: EventData): void {
  const view = args.object as RadListView;
  const vm = new MyViewModel();

  view.bindingContext = vm;
}

export function onLayoutChanged(args: EventData): void {
  const view = args.object as RadListView;
  const vm = view.bindingContext as MyViewModel;

  vm.itemHeight = ...;
  if (application.ios) {
    (vm.listViewLayout as ListViewGridLayout).itemHeight = vm.itemHeight;
  }
}

あれー…こっちの方がシンプルかもしれない…。