Android StudioプロジェクトでQtAbstractItemModelを使う

概要

この例では、Android Studioプロジェクト(qtabstractitemmodel_java)とQMLプロジェクト(qtabstractitemmodel)の2つのプロジェクトから構成されています。QMLプロジェクトをAndroidプロジェクトにインポートすることができます。

この例では、JavaとQMLの間で複雑なデータ型を扱う方法を示しています。この例では、QtAbstractItemModel およびQtModelIndex Java API クラスの使い方を示しています。QMLでは、TableView 。Javaでは、行と列を入れ子にしたArrayListアイテムのモデルでデータの使い方を示します。QMLがどのように動作するかについては、Qt Qmlを参照してください。

例の実行

このサンプルを実行するには、Android StudioとQt Tools for Android Studioが必要です。Android Studio で qtabstractitemmodel_java を開き、Qt Tools for Android Studioの指示に従ってqtabstractitemmodel.QML プロジェクトをインポートしてください。

QML プロジェクト

QMLプロジェクト側では、Rectangle をルートオブジェクトとしています。dataModel プロパティ変数には、Java 側で作成・配信されたデータモデルが格納されています。

Rectangle {
    id: mainRectangle

    property AbstractItemModel dataModel

TableView にはデータモデルが表示されます。 プロパティでは、モデルの各セル項目を で定義しています。delegate Text

    TableView {
        id: tableView

        model: mainRectangle.dataModel

        anchors {fill: parent; margins: 20}
        columnSpacing: 4
        rowSpacing: 6
        boundsBehavior: TableView.OvershootBounds
        clip: true

        ScrollBar.vertical: ScrollBar {
           policy: ScrollBar.AsNeeded
        }
        ScrollBar.horizontal: ScrollBar{
           policy: ScrollBar.AsNeeded
        }

デリゲートでは、モデルのQHash<int, QByteArray> QAbstractItemModel::roleNames() constQtModelIndex index(int row, int column, QtModelIndex parent)Object data(QtModelIndex qtModelIndex, int role) メソッドを通して、rowcolumn プロパティを設定しています。

QMLからこれらのメソッドを呼び出すことは、QtのqtMainLoopThreadスレッドコンテキストで実行されることを意味します。

詳しい説明はQAbstractItemModel を参照してください。

    TableView {
        id: tableView

        model: mainRectangle.dataModel

        anchors {fill: parent; margins: 20}
        columnSpacing: 4
        rowSpacing: 6
        boundsBehavior: TableView.OvershootBounds
        clip: true

        ScrollBar.vertical: ScrollBar {
           policy: ScrollBar.AsNeeded
        }
        ScrollBar.horizontal: ScrollBar{
           policy: ScrollBar.AsNeeded
        }

Android Studioプロジェクト

Android Studioプロジェクト(qtabstractitemmodel_java)には、ActivityクラスMainActivityMyDataModel があります。

データモデル

データモデルであるc MyDataModelは、QtAbstractItemModel クラスを継承しています。QtAbstractItemModelQAbstractItemModel のラッパーです。

MyDataModel クラスのメソッドは QML 側と Android 側の両方から呼び出されるため、Qt の qtMainLoopThread と Android のメインスレッドの両方のスレッドコンテキストで実行されます。MyDataModel クラスのメソッドでメンバ変数にアクセスする際には、同期を確保する必要があります。

まず、この例では単純な行と列のモックデータセットでモデルを初期化します。このコンストラクタ・メソッドはAndroidメイン・スレッド・コンテキストで呼び出されることに注意してください。

/*
* Initializes the two-dimensional array list with following content:
* [] [] [] [] 1A 1B 1C 1D
* [] [] [] [] 2A 2B 2C 2D
* [] [] [] [] 3A 3B 3C 3D
* [] [] [] [] 4A 4B 4C 4D
* Threading: called in Android main thread context.
*/
public MyDataModel() {

この例では、さまざまな目的のためにQtAbstractItemModel メソッドをオーバーライドしています。columnCount()メソッドとrowCount()メソッドは、モデル内のそれぞれのカウントを返します。各rowCount()の実行は、Qt qtMainLoopThreadとAndroidメインスレッドコンテキストの両方のスレッドコンテキストで発生します。

/*
* Returns the count of columns.
* Threading: called in Android main thread context.
* Threading: called in Qt qtMainLoopThread thread context.
*/
@Override
synchronized public int columnCount(QtModelIndex qtModelIndex) {
    return m_columns;
}

/*
* Returns the count of rows.
* Threading: called in Android main thread context.
* Threading: called in Qt qtMainLoopThread thread context.
*/
@Override
synchronized public int rowCount(QtModelIndex qtModelIndex) {
    return m_dataList.size();
}

data()メソッドは、JavaからQMLへ、ロールとインデックスに基づいたモデルデータを提供します。roleNames()メソッドは、ロールの数値とその名前を文字列でマッチングしたハッシュを返します。index()メソッドは、新しいモデルのインデックスを返します。parent()メソッドはインデックスの親を返すべきです。しかし、この例では親インデックスを持たないデータに焦点を当てているため、このメソッドをオーバーライドし、空の QtModelIndex() を返します。メソッドは QML から呼び出され、Qt の qtMainLoopThread スレッドコンテキストで実行されます。

/*
* Returns the data to QML based on the roleNames
* Threading: called in Qt qtMainLoopThread thread context.
*/
@Override
synchronized public Object data(QtModelIndex qtModelIndex, int role) {
    switch (role) {
        case ROLE_ROW:
            Cell elementForRow = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column());
            String row = String.valueOf(elementForRow.getRow());
            return row;
        case ROLE_COLUMN:
            Cell elementForColumn = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column());
            String column = elementForColumn.getColumn();
            return column;
        default:
            Log.w(TAG, "data unrecognized role: " + role);
            return null;
    }
}

/*
* Defines what string i.e. role in QML side gets the data from Java side.
* Threading: called in Qt qtMainLoopThread thread context.
*/
@Override
synchronized public HashMap<Integer, String> roleNames() {
    HashMap<Integer, String> roles = new HashMap<>();
    roles.put(ROLE_ROW, "row");
    roles.put(ROLE_COLUMN, "column");
    return roles;
}

/*
* Returns a new index model.
* Threading: called in Qt qtMainLoopThread thread context.
*/
@Override
synchronized public QtModelIndex index(int row, int column, QtModelIndex parent) {
    return createIndex(row, column, 0);
}

/*
* Returns a parent model.
* Threading: not used called in this example.
*/
@Override
synchronized public QtModelIndex parent(QtModelIndex qtModelIndex) {
    return new QtModelIndex();
}

この例では、MainActivity UI インタラクションで行や列を追加したり削除したりするためのメソッドをモデル側に実装しています。beginInsertRow()のように、モデルインデックスを更新するために行の開始、終了、挿入、削除を呼び出します。この例ではQtAbstractItemModel を使用しているため、モデルに新しい行を挿入するたびに beginInsertRows() と endInsertRows() を呼び出す必要があります。削除も同様です。これらのメソッドはAndroid側から呼び出されるため、実行はAndroidのメインスレッドコンテキストで行われます。

/*
* Adds a row.
* Threading: called in Android main thread context.
*/
synchronized public void addRow() {
    if (m_columns > 0 && m_dataList.size() < MAX_ROWS_AND_COLUMNS) {
        beginInsertRows(new QtModelIndex(), m_dataList.size(), m_dataList.size());
        m_dataList.add(generateRow());
        endInsertRows();
    }
}

/*
* Removes a row.
* Threading: called in Android main thread context.
*/
synchronized public void removeRow() {
    if (m_dataList.size() > 1) {
        beginRemoveRows(new QtModelIndex(), m_dataList.size() - 1, m_dataList.size() - 1);
        m_dataList.remove(m_dataList.size() - 1);
        endRemoveRows();
    }
}

この例では、MainActivity UIインタラクションで列の追加と削除を行うためのメソッドをモデル側に実装しています。beginRemoveColumn()のように、モデルのインデックスを更新するために、列の開始、終了、挿入、削除を呼び出します。行の追加と削除のメソッドと同じように、コンテキストの意識が適用されます。

/*
* Adds a column.
* Threading: called in Android main thread context.
*/
synchronized public void addColumn() {
    if (!m_dataList.isEmpty() && m_columns < MAX_ROWS_AND_COLUMNS) {
        beginInsertColumns(new QtModelIndex(), m_columns, m_columns);
        generateColumn();
        m_columns += 1;
        endInsertColumns();
    }
}

/*
* Removes a column.
* Threading: called in Android main thread context.
*/
synchronized public void removeColumn() {
    if (m_columns > 1) {
        int columnToRemove = m_columns - 1;
        beginRemoveColumns(new QtModelIndex(), columnToRemove, columnToRemove);
        m_columns -= 1;
        endRemoveColumns();
    }
}

メインアクティビティ

MainActivity QMLがロードされたときにステータスの更新を取得するために、 インターフェースを実装しています。また、Androidのメインアクティビティでもあります。QtQmlStatusChangeListener

この例では、データモデルを作成し、初期化しています。QtQuickViewも参照してください。

private final MyDataModel m_model = new MyDataModel();

この例では、UIボタンとそのリスナーを設定し、ユーザーがUIを介してモデルと対話できるようにしています。

/*
* Returns the count of columns.
* Threading: called in Android main thread context.
* Threading: called in Qt qtMainLoopThread thread context.
*/
@Override
synchronized public int columnCount(QtModelIndex qtModelIndex) {
    return m_columns;
}

/*
* Returns the count of rows.
* Threading: called in Android main thread context.
* Threading: called in Qt qtMainLoopThread thread context.
*/
@Override
synchronized public int rowCount(QtModelIndex qtModelIndex) {
    return m_dataList.size();
}

QMLコンテンツの読み込みを開始します。読み込みは、ready のステータスが更新されるまで、バックグラウンドで行われます。

/*
* Returns the data to QML based on the roleNames
* Threading: called in Qt qtMainLoopThread thread context.
*/
@Override
synchronized public Object data(QtModelIndex qtModelIndex, int role) {
    switch (role) {
        case ROLE_ROW:
            Cell elementForRow = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column());
            String row = String.valueOf(elementForRow.getRow());
            return row;
        case ROLE_COLUMN:
            Cell elementForColumn = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column());
            String column = elementForColumn.getColumn();
            return column;
        default:
            Log.w(TAG, "data unrecognized role: " + role);
            return null;
    }
}

/*
* Defines what string i.e. role in QML side gets the data from Java side.
* Threading: called in Qt qtMainLoopThread thread context.
*/
@Override
synchronized public HashMap<Integer, String> roleNames() {
    HashMap<Integer, String> roles = new HashMap<>();
    roles.put(ROLE_ROW, "row");
    roles.put(ROLE_COLUMN, "column");
    return roles;
}

/*
* Returns a new index model.
* Threading: called in Qt qtMainLoopThread thread context.
*/
@Override
synchronized public QtModelIndex index(int row, int column, QtModelIndex parent) {
    return createIndex(row, column, 0);
}

/*
* Returns a parent model.
* Threading: not used called in this example.
*/
@Override
synchronized public QtModelIndex parent(QtModelIndex qtModelIndex) {
    return new QtModelIndex();
}

この例では、QML コンテンツがロードされ、ステータスが準備完了になると、データモデルを設定します。

/*
* Adds a row.
* Threading: called in Android main thread context.
*/
synchronized public void addRow() {
    if (m_columns > 0 && m_dataList.size() < MAX_ROWS_AND_COLUMNS) {
        beginInsertRows(new QtModelIndex(), m_dataList.size(), m_dataList.size());
        m_dataList.add(generateRow());
        endInsertRows();
    }
}

/*
* Removes a row.
* Threading: called in Android main thread context.
*/
synchronized public void removeRow() {
    if (m_dataList.size() > 1) {
        beginRemoveRows(new QtModelIndex(), m_dataList.size() - 1, m_dataList.size() - 1);
        m_dataList.remove(m_dataList.size() - 1);
        endRemoveRows();
    }
}

本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。