📜 ⬆️ ⬇️

Android application on QML: Picker

Is it possible to write an ordinary mobile application on Qt Quick? Not a game, but a traditional application? If six months ago I had serious doubts about the feasibility of this enterprise, now there is no doubt left - you can!

Of course, many problems awaited (and await) on this path, most of which are described here . At the moment, there is already a decent amount of developments, I hope this article will begin the cycle of systematization and documentation of experience. Let's start with something simple and necessary, namely, the widget of choosing a digital value, in English called Picker. This is used in Android when you need to enter a date, time, or some specific value.


Under the hood


It is logical that in order to repeat, you must first dissect the original widget and understand what parts it consists of. So what do we have?
')
1) The basis is a scrollable list (marked in blue ), the selected element of which is located in the center of the visible part. So as a basis we will use the standard ListView. In order to realize the choice of the central element, we need:


The resulting code
import QtQuick 2.0 Rectangle { id: rootRect property double itemHeight: 8*mm property alias model: listView.model signal indexChanged(int value) function setValue(value) { listView.currentIndex = value listView.positionViewAtIndex(value, ListView.Center); } ListView { id: listView clip: true anchors.fill: parent contentHeight: itemHeight*3 delegate: Item { property var isCurrent: ListView.isCurrentItem id: item height: itemHeight width: listView.width Rectangle { anchors.fill: parent Text { text: model.text font.pixelSize: 3*mm anchors.centerIn: parent } MouseArea { anchors.fill: parent onClicked: { rootRect.gotoIndex(model.index) } } } } onMovementEnded: { var centralIndex = listView.indexAt(listView.contentX+1,listView.contentY+itemHeight+itemHeight/2) gotoIndex(centralIndex) indexChanged(currentIndex) } } function gotoIndex(inIndex) { var begPos = listView.contentY; var destPos; listView.positionViewAtIndex(inIndex, ListView.Center); destPos = listView.contentY; anim.from = begPos; anim.to = destPos; anim.running = true; listView.currentIndex = inIndex } NumberAnimation { id: anim; target: listView; property: "contentY"; easing { type: Easing.OutInExpo; overshoot: 50 } } function next() { gotoIndex(listView.currentIndex+1) } function prev() { gotoIndex(listView.currentIndex-1) } } 
It should be noted that in the original lists are often cyclical, however, the resulting qml-clone still allows using only regular ones.

2) On top of the list are separators (marked in orange ). Required to visually highlight the selected item. They are implemented by trivial rectangles of the desired color, with a given offset (respectively, by the height of one and two elements).

3) To give the effect of illumination of the upper and lower elements (marked in green ), an image with a gradient from white to transparent is used. Also superimposed on top, with the positioning of the problems, too, no.

Code of the second and third elements
 import QtQuick 2.0 import "../Global" Rectangle { property alias model: pickerList.model signal indexSelected(int value) function setValue(value) { pickerList.setValue(value) } width: 10*mm height: 25*mm ACPickerList { id: pickerList width: parent.width height: parent.height onIndexChanged: { indexSelected(value) } } Image { id: upShadow sourceSize.height: 10*mm sourceSize.width: 10*mm source: "qrc:/img/images/icons/pickerShadowUp.svg" anchors { top: parent.top } } Image { id: downShadow sourceSize.height: 10*mm sourceSize.width: 10*mm source: "qrc:/img/images/icons/pickerShadowDown.svg" anchors { bottom: parent.bottom } } Rectangle { id: topSelector width: parent.width height: parseInt(0.3*mm) color: ACGlobal.style.holoLightBlue anchors { top: parent.top topMargin: pickerList.itemHeight } } Rectangle { id: bottomSelector width: parent.width height: parseInt(0.3*mm) color: ACGlobal.style.holoLightBlue anchors { top: parent.top topMargin: pickerList.itemHeight*2 } } } 

Timing


So, we now have the widget itself, it remains to give an example of use. A full-fledged date selection dialogue is a topic for a separate article (but those who wish can easily see it today here ). Therefore, we will practice on cats with something simpler, for example, we will create pickers as a blank for the dialogue of timing. We need them for two whole hours and minutes, respectively. In the middle, between them, there should be a separator ":". To perform this task, you need to fill the model with the values ​​of hours and minutes, that is, generate values ​​from 0 to 23 and from 0 to 59. If the value is less than 10, you need to supplement it with the leading zero. In order to be able to select the extreme elements of the list, you need to add blank plugs at the beginning and end of the model.

  Rectangle { ACPicker { id: hoursPicker model: ListModel { id: hoursModel Component.onCompleted: { append({ value: -1, text: " " }) for(var i = 0; i <= 23; i++){ var norm = i.toString(); if( i < 10 ) norm = "0" + i append({ value: i, text: norm }) } append({ value: -1, text: " " }) } } anchors { right: center.left rightMargin: 1*mm verticalCenter: parent.verticalCenter } } Text { id: center text:":" font.pixelSize: 3*mm anchors.centerIn: parent } ACPicker { id: minutesPicker model: ListModel { id: minutesModel Component.onCompleted: { append({ value: -1, text: " " }) for(var i = 0; i <= 59; i++){ var norm = i.toString(); if( i < 10 ) norm = "0" + i append({ value: i, text: norm }) } append({ value: -1, text: " " }) } } anchors { left: center.right leftMargin: 1*mm verticalCenter: parent.verticalCenter } } anchors.fill: parent } 

Sources of the project entirely.

Source: https://habr.com/ru/post/240431/


All Articles