안드로이드 스튜디오 프로젝트에서 QtAbstractItemModel 사용하기
개요
이 예제는 두 개의 프로젝트, 즉 안드로이드 스튜디오 프로젝트(qtabstractitemmodel_java)와 QML 프로젝트(qtabstractitemmodel)로 구성됩니다. QML 프로젝트를 안드로이드 프로젝트로 가져올 수 있습니다.
이 예제는 Java와 QML 간에 복잡한 데이터 유형을 처리하는 방법을 보여줍니다. QtAbstractItemModel
및 QtModelIndex
Java API 클래스를 사용하는 방법을 보여줍니다. QML에서는 TableView 항목으로 데이터 사용법을 보여줍니다. Java에서는 행과 열에 대한 중첩된 ArrayList 항목의 모델을 사용하여 데이터 사용법을 보여줍니다. QML 작동 방식에 대한 자세한 내용은 다음을 참조하세요. Qt Qml.
예제 실행하기
이 예제를 실행하려면 안드로이드용 표준 Qt 설치 위에 안드로이드 스튜디오와 안드로이드 스튜디오용 Qt 도구가 필요합니다. 안드로이드 스튜디오에서 qtabstractitemmodel_java를 열고 안드로이드 스튜디오용 Qt 도구의 지침에 따라 qtabstractitemmodel
.
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() const 및 QtModelIndex index(int row, int column, QtModelIndex parent), Object data(QtModelIndex qtModelIndex, int role) 메서드를 통해 row
및 column
속성을 설정합니다.
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 }
안드로이드 스튜디오 프로젝트
안드로이드 스튜디오 프로젝트(qtabstractitemmodel_java)에는 하나의 Activity 클래스 MainActivity
와 MyDataModel
클래스가 포함되어 있습니다.
데이터 모델
데이터 모델인 c MyDataModel은 QtAbstractItemModel
클래스를 확장합니다. QtAbstractItemModel
은 QAbstractItemModel 의 래퍼입니다.
MyDataModel
클래스의 메서드는 QML과 안드로이드 양쪽에서 모두 호출되므로 실행은 스레드 컨텍스트인 Qt qtMainLoopThread와 안드로이드 메인 스레드 컨텍스트 모두에서 발생합니다. MyDataModel
클래스의 메서드에서 멤버 변수에 접근할 때는 동기화를 보장해야 합니다.
먼저 이 예제에서는 간단한 행과 열 모의 데이터 세트로 모델을 초기화합니다. 이 생성자 메서드는 안드로이드 메인 스레드 컨텍스트에서 호출된다는 점에 유의하세요.
/* * 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와 안드로이드 메인 스레드 컨텍스트 모두에서 발생합니다.
/* * 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() 메서드는 숫자 역할 값과 해당 이름에 일치하는 해시를 문자열로 반환하며, QML에서는 이 역할 이름을 사용하여 모델에서 해당 데이터를 가져옵니다. 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()를 호출해야 합니다. 제거할 때도 마찬가지입니다. 메서드는 안드로이드 측에서 호출되므로 실행은 안드로이드 메인 스레드 컨텍스트에서 이루어집니다.
/* * 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
는 QtQmlStatusChangeListener
인터페이스를 구현하여 QML이 로드될 때 상태 업데이트를 가져옵니다. 또한 메인 안드로이드 활동이기도 합니다.
이 예제에서는 데이터 모델을 생성하고 초기화합니다. 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(); } }
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.