Android Studio 프로젝트에서 QtAbstractItemModel 사용하기
Qt Quick Android API 예제는 Android Studio 프로젝트 형식입니다
Android용 Qt Quick API 예제는 Android Studio 프로젝트 형태로 제공됩니다. 프로젝트 폴더는 Qt 설치 경로에서 찾을 수 있습니다.
예를 들어, Windows의 기본 설치 경로에서는 다음 위치에 있습니다:
C:\Qt\Examples\Qt-<patch-release-number>\platforms\android\<example-name>이 프로젝트들은 해당 Qt 버전과 호환되는 Qt Gradle 플러그인 버전을 사용하도록 이미 구성되어 있습니다.
개요

이 예제는 Android Studio 프로젝트(qtabstractitemmodel_java)와 QML 프로젝트(qtabstractitemmodel) 두 가지로 구성되어 있습니다. QML 프로젝트를 Android 프로젝트로 가져올 수 있습니다.
이 예제는 Java와 QML 간에 복잡한 데이터 유형을 처리하는 방법을 보여줍니다. 또한 QtAbstractItemModel 및 QtModelIndex Java API 클래스를 사용하는 방법을 시연합니다. QML에서는 ` TableView ` 항목을 사용하여 데이터 사용법을 보여줍니다. Java에서는 행과 열을 위한 중첩된 `ArrayList` 항목 모델로 데이터 사용법을 보여줍니다. QML의 작동 방식에 대한 자세한 내용은 Qt Qml.
예제 실행
이 예제를 실행하려면 표준 Qt for Android 설치 환경 위에 Android Studio와 Qt Tools for Android Studio가 추가로 필요합니다. Android Studio에서 qtabstractitemmodel_java 파일을 열고, Qt Tools for Android Studio의 지침에 따라 qtabstractitemmodel 을 임포트하십시오.
QML 프로젝트
QML 프로젝트 측면에서, 이 예제는 Rectangle 을 루트 객체로 사용합니다. dataModel 속성 변수는 Java 측에서 생성 및 전달된 데이터 모델을 포함합니다.
Rectangle {
id: mainRectangle
property AbstractItemModel dataModelTableView 는 데이터 모델을 표시합니다.
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
}delegate 속성에서, 모델의 각 셀 항목은 TextEdit 를 포함하는 Rectangle 으로 정의됩니다. TextEdit 의 text 속성은 QAbstractItemModel::data()을 사용하여 설정되며, 이 메서드는 지정된 역할과 인덱스를 기반으로 값을 반환합니다.
Qt Qml에서 이러한 메서드를 호출하면 실행은 Qt의 qtMainLoopThread 스레드 컨텍스트에서 이루어집니다.
delegate: Rectangle {
implicitWidth: (tableView.height > tableView.width) ? tableView.width / 10 : tableView.height / 5
implicitHeight: implicitWidth
required property var model
color: "#2CDE85"
border {color: "#00414A"; width: 2}
TextEdit {
// Calls MyDataModel::data to get data based on the roles.
// Called in Qt qtMainLoopThread thread context.
//
// After editing is finished, call MyDataModel::setData()
// to update the value of selected cell.
onEditingFinished: parent.model.edit = text
text: parent.model.display
font {pixelSize: 26; bold: true}
padding: 5
anchors.fill: parent
wrapMode: TextEdit.Wrap
horizontalAlignment: TextEdit.AlignHCenter
verticalAlignment: TextEdit.AlignVCenter
}
}TextEdit 필드를 편집하는 경우, onEditingFinished() 핸들러는 모델의 edit 역할 값을 편집된 텍스트로 설정합니다. 이렇게 하면 QAbstractItemModel::setData() 메서드가 호출되며, 여기서 셀의 편집된 텍스트가 모델의 해당 인덱스로 업데이트됩니다.
자세한 내용은 QAbstractItemModel 을 참조하십시오.
Android Studio 프로젝트
Android Studio 프로젝트(qtabstractitemmodel_java)에는 하나의 Activity 클래스 MainActivity 와 MyDataModel 클래스가 포함되어 있습니다.
데이터 모델
데이터 모델인 MyDataModel 은 QtAbstractItemModel 클래스를 상속합니다. QtAbstractItemModel 은 QAbstractItemModel 의 래퍼입니다.
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() 메서드는 숫자형 역할 값과 문자열 형태의 이름을 매칭하는 해시를 반환합니다. 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) {
if (role == ROLE_DISPLAY) {
Cell elementForEdit = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column());
return elementForEdit.getValue();
}
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_DISPLAY, "display");
roles.put(ROLE_EDIT, "edit");
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 측에서 index 에 있는 모델의 데이터가 설정될 때 호출되는 QAbstractItemModel::setData() 메서드를 재정의합니다.
/*
* Gets called when model data is edited from QML side.
* Sets the role data for the item at index to value,
* if given index is valid and if data in given index truly changed.
*/
@Override
synchronized public boolean setData(QtModelIndex index, Object value, int role) {
Cell cellAtIndex = m_dataList.get(index.row()).get(index.column());
String cellValueAtIndex = cellAtIndex.getValue();
if (!index.isValid() || role != ROLE_EDIT
|| Objects.equals(cellValueAtIndex, value.toString())) {
return false;
}
cellAtIndex.setValue(value.toString());
// Send dataChanged() when data was successfully set.
dataChanged(index, index, new int[]{role});
return true;
}이 예제는 행과 열을 추가 및 제거하기 위한 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(generateNewRow());
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();
}
}이 예제는 열을 추가 및 제거하기 위한 UI 상호작용( MainActivity )을 위해 모델 측에서 메서드를 구현합니다. 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);
generateNewColumn();
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);
for (int row = 0; row < m_dataList.size(); row++)
m_dataList.get(row).remove(columnToRemove);
m_columns -= 1;
endRemoveColumns();
}
}메인 액티비티
MainActivity 는 QML이 로드될 때 상태 업데이트를 수신하기 위해 QtQmlStatusChangeListener 인터페이스를 구현합니다. 또한 이 액티비티는 Android의 메인 액티비티이기도 합니다.
이 예제는 데이터 모델을 생성하고 초기화합니다. 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) {
if (role == ROLE_DISPLAY) {
Cell elementForEdit = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column());
return elementForEdit.getValue();
}
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_DISPLAY, "display");
roles.put(ROLE_EDIT, "edit");
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 콘텐츠가 로드되고 상태가 ‘ready’가 되면 데이터 모델을 설정합니다.
/*
* Gets called when model data is edited from QML side.
* Sets the role data for the item at index to value,
* if given index is valid and if data in given index truly changed.
*/
@Override
synchronized public boolean setData(QtModelIndex index, Object value, int role) {
Cell cellAtIndex = m_dataList.get(index.row()).get(index.column());
String cellValueAtIndex = cellAtIndex.getValue();
if (!index.isValid() || role != ROLE_EDIT
|| Objects.equals(cellValueAtIndex, value.toString())) {
return false;
}
cellAtIndex.setValue(value.toString());
// Send dataChanged() when data was successfully set.
dataChanged(index, index, new int[]{role});
return true;
}© 2026 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.