Android上的Qt:我们如何赋予作者冥想应用程序以新生

简短的抒情介绍-在2017年,我碰巧对冥想非常感兴趣。一连串的事件促进了这种情况,这是有利的,但不是很好。多年以来,我一直对清醒梦产生兴趣,但在此之前,我不必专门处理经典的冥想。这些天很多故事从酒吧开始兴趣爱好始于在Google上进行搜索,因此我以这种方式开始了。几乎立即,发现了最流行的冥想应用程序-平静和顶空。


第一个是一个很好的起点(对初学者的出色的教育冥想),第二个我觉得对自己没有用,我不喜欢演示文稿。两者都取消了有偿关税计划(对于俄罗斯联邦,我必须说这是非常昂贵的)。也许我只是不属于需要付费以鼓励自己做某事的人类别:)继续学习Google Play时,我遇到了两个与我精神相近的免费应用程序。第一个是“让我们沉思”-我仍在使用它,第二个将在本文的正文中进行讨论。


应用


因此,经过漫长的搜索,发现了一个完全不起眼的应用程序,然后,如果我没有记错的话,它被称为“冥想。安东诺夫·亚历山大”。事实证明,有可能听到其中四位作者的沉思,实际上是由亚历山大录制和创作的,我们后来与他们见面并结交了朋友。他从字面上用临时工具组装了应用程序,这就像使用WebView的临时SPA,没有任何框架,几乎是裸HTML格式,而Java则最少。它看起来很一般,并且根本没有某些功能(例如,您无法浏览记录,而只能从头开始将其打开)。由于我真的很喜欢内容本身,因此我向亚历山大提供了免费的帮助,帮助他们升级应用程序,因此,可以这么说,“在“他们帮助我的基础上”给我一些东西,我会帮助。“在本文的正文中,我将尝试告诉您我们在开发过程中遇到了哪些问题,做出了哪些决定以及最终发生了什么!我希望本文的各个诀窍能够对任何人都有用,但有趣的是:)


发展的第一阶段


因此,我们为自己设定了目标:


  • 保留原始应用程序功能
  • 改善UI应用程序和用户UX
  • 最小的实现复杂度

t


综上所述,有必要快速创建一个具有相当适度功能的应用程序(目前),对于具有PHP / HTML编程经验的人来说,其代码是可以理解的。坦率地说,我认为选择不赞成Qt是因为:


  • 我已经在Qt上有很多开发经验(对于Symbian,MeeGo,Ubuntu Phone以及对Android都有一点经验);
  • 在桌面上进行透明开发的可能性,然后在目标设备上进行最终检查;
  • 可以使用纯QML创建应用程序,而无需使用C ++。读者当然知道,但是我会澄清-这是一种类似于JavaScript的标记语言,业余爱好者也可以理解。
  • iOS ( ).

, — , .


UI


, , . , , . , . ( ):



"" anchors, Row, Column Repeater. , UI. , ( ):


Button {
id: mainButton
anchors {
    left: parent.left
    right: parent.right
}
height: btnLayout.height + 30
Material.background: "white"
onClicked: stackView.push(Qt.resolvedUrl("qrc:/qml/MeditationListPage.qml"))

Column {
    id: btnLayout
    spacing: 10
    anchors {
        top: parent.top
        topMargin: 15
        left: parent.left
        right: parent.right
        margins: 10
    }

    Row {
        anchors.horizontalCenter: parent.horizontalCenter
        spacing: (mainButton.width - 4 * 50) / 6
        Repeater {
            model: meditationModel

            RoundedIcon {
                source: Qt.resolvedUrl("qrc:/img/my%1.png".arg(model.index))
                color: model.color
                width: 50
                height: 50
            }
        }
    }

    Label {
        text: qsTr("")
        font.pointSize: 14
        color: "dimgrey"
        anchors.horizontalCenter: parent.horizontalCenter
    }

    Label {
        text: "        ,     "
        anchors {
            left: parent.left
            right: parent.right
        }
        horizontalAlignment: Text.AlignHCenter
        Material.foreground: Material.Grey
        font.pixelSize: 12
        wrapMode: Text.WrapAtWordBoundaryOrAnywhere
    }
}
}

UI , HDPI Qt . , Qt: QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);. " ".


Material design, Android Qt Quick Controls 2 . https://doc.qt.io/qt-5/qtquickcontrols2-material.html. , Material design. UI , attached property Material.accent .




mp3- , QRC. 10-15 . — , 15 . , , pro-:


CONFIG += resources_big

, , , .



, " ". Shorts, , . DarkModeShader.qml:


ShaderEffect {
    fragmentShader: "
        uniform lowp sampler2D source;
        uniform lowp float qt_Opacity;
        varying highp vec2 qt_TexCoord0;

        void main() {
            lowp vec4 p = texture2D(source, qt_TexCoord0);
            p.r = min(0.8, (1.0 - p.r) * 0.8 + 0.1);
            p.g = min(0.8, (1.0 - p.g) * 0.8 + 0.1);
            p.b = min(0.8, (1.0 - p.b) * 0.8 + 0.1);
            gl_FragColor = vec4(vec3(dot(p.rgb, vec3(0.299, 0.587, 0.114))), p.a) * qt_Opacity;
        }
    "
}

:


StackView {
    // ...
    layer.effect: DarkModeShader { }
    layer.enabled: optionsKeeper.isNightMode
}

.. , isNightMode. Qt / ( , ).


, — . - , — !



Qt Multimedia, Audio. mp3, , — , :


Audio {
    id: audioPlayback
    source: meditAudioSource
}
// ...
Slider {
    anchors {
        left: parent.left
        right: playBtn.left
        verticalCenter: parent.verticalCenter
    }
    from: 0
    to: audioPlayback.duration
    value: audioPlayback.position
    onMoved: audioPlayback.seek(value)
    Material.accent: meditColor
}

Settings Qt.labs.settings (, , labs):


import Qt.labs.settings 1.0
// ...  boilerplate- ,        .
property Settings settings: Settings {
    property bool isNightMode: false
}

2018 , .



, , 2020, , - , — ( ). , — — :)



, JSON- . . HTTP- QML- XMLHttpRequest ( JSON QML). .



. QML LocalStorage, SQLite. JS-, QML, :


// databasemodule.js
.pragma library // I hope this will prevent the waste of memory.
.import QtQuick.LocalStorage 2.0 as SQL

function getMeditations() {
    ...
}

// TransferManager.qml
import "databasemodule.js" as DB
...
var syncedItems = DB.getMeditations()

:


var db = SQL.LocalStorage.openDatabaseSync("AMeditation", "", "Main DB", 100000)
...
db.transaction(function(tx) {
    dbResult = tx.executeSql("SELECT * FROM meditations")
    console.log("meditations SELECTED: ", dbResult.rows.length)
})

.. , transaction callback. ( , Qt ).
. openDatabaseSync . , ( , ). . , , , . [" ", " ", " "]:


var migrations = [
    {'from': "", 'to': "1.0", 'ops': function(transaction) {
        transaction.executeSql("CREATE TABLE meditations ( \
            id  INTEGER PRIMARY KEY 
            ...
            status  TEXT);")
    }}
    ,{'from': "1.0", 'to': "1.1", 'ops': function(transaction) {
        transaction.executeSql("ALTER TABLE meditations ADD quality TEXT;")
    }}
]

, ( — , - ).


C++


QML, C++. . Ubuntu Phone. . QML — . C++- QML :


engine.rootContext()->setContextProperty("networkManager", new NetworkManager());

QML - :


//  .
var isSucces = networkManager.download(downloadUrl, currentDownload.localUrl)
...
//  .
Connections {
    target: networkManager

    onDownloadOperationProgress: {
        d.currentDownload.current = current
        d.currentDownload.total = total
    }
    ...
}

C++-, QML- singletone :


// cpputils.h
class CppUtils : public QObject
{
    Q_OBJECT
public:
    explicit CppUtils(QObject *parent = nullptr);
    ~CppUtils();

    Q_INVOKABLE bool removeFile(const QString& fileName) const;
    static QObject *cppUtilsSingletoneProvider(QQmlEngine *engine, QJSEngine *scriptEngine);
};

// main.cpp
qmlRegisterSingletonType<CppUtils>("AMeditation.CppUtils", 1, 0, "CppUtils", CppUtils::cppUtilsSingletoneProvider);

.. , cppUtilsSingletoneProvider, Q_INVOKABLE — "" QML. QML :


import AMeditation.CppUtils 1.0
// ...
CppUtils.removeFile(cd.localUrl)


, - . — . , - , . , . QML (QNetworkAccessManager). . :


// cachingnetworkmanagerfactory.h
class CachingNetworkAccessManager : public QNetworkAccessManager
{
public:
    CachingNetworkAccessManager(QObject *parent = 0);
protected:
    QNetworkReply* createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData = 0);
};

class CachingNetworkManagerFactory : public QQmlNetworkAccessManagerFactory
{
public:
    CachingNetworkManagerFactory();
    QNetworkAccessManager *create(QObject *parent);
};

// cachingnetworkmanagerfactory.cpp

QNetworkReply* CachingNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
{
    QNetworkRequest req(request);
    req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork);
    return QNetworkAccessManager::createRequest(op, req, outgoingData);
}

QNetworkAccessManager *CachingNetworkManagerFactory::create(QObject *parent) {
    QNetworkAccessManager* manager = new CachingNetworkAccessManager(parent);

    QNetworkDiskCache* cache = new QNetworkDiskCache(manager);
    cache->setCacheDirectory(QString("%1/network").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
    manager->setCache(cache);
    return manager;
}

.. setCache , createRequest . — , .


Android


SDK NDK. Linux- , Windows - (, , QtCreator 4.12 NDK, ). NDK Clang. arm_v7 (32 ), arm_v8a (64 ; Google play 2019 ). Google play.


, , .



  • Qt pet-, Android. ;
  • AMeditation ( UI " 2.1. ") 10+ 250 , ( , , ).
  • , , !
  • iOS .

总的来说,在Qt上进行开发很有趣,免费并且为灵魂做些事情!


PS亲爱的哈布罗夫斯克市民,我希望您不要将其视为广告-该应用程序基本上是免费的并且是非商业性的,我想向有兴趣的人介绍它及其创建历史,就像分享成功找到的开发方法一样。
PSS该应用程序的双胞胎版本为1.1,带有较旧的条目,另外还有5k +下载和100条评论。我们可能会很快将其从商店中取出。



All Articles