make Toast in Android by QML

2019-03-21 16:05发布

I have not started my learning Android developing by QML ,I just curious that Toasts seems to be a Android specific widget ,while Qt for Android seems doesn't have a ready-made corresponding widget, so how do you implement a Toast in Android by QML ?

标签: qt qml
4条回答
Explosion°爆炸
2楼-- · 2019-03-21 16:29

I write this method (calling from Qt)

public static void notify(String s)
{
m_instance.setNote(s);
int duration = Toast.LENGTH_SHORT;        
m_instance.runOnUiThread(new Runnable() {

            public void run() {
                Toast.makeText(m_instance, m_instance.getNote(), Toast.LENGTH_SHORT).show();
                Log.i(QtApplication.QtTAG,"run run Toast.makeText");

            }
    });

}

You have to save context on OnCreate Event:

  public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);

     baseContext = getApplicationContext();
      m_instance = this;
}

credit to victorrbravo

查看更多
再贱就再见
3楼-- · 2019-03-21 16:35

Maybe something like this..

This is for InfoBanner.qml

import QtQuick 2.2
Loader {
    id: messages

    function displayMessage(message) {
        messages.source = "";
        messages.source = Qt.resolvedUrl("InfoBannerComponent.qml");
        messages.item.message = message;
    }

    width: parent.width
    anchors.bottom: parent.top
    z: 1
    onLoaded: {
        messages.item.state = "portrait";
        timer.running = true
        messages.state = "show"
    }

    Timer {
        id: timer

        interval: 2500
        onTriggered: {
            messages.state = ""
        }
    }

    states: [
        State {
            name: "show"
            AnchorChanges { target: messages; anchors { bottom: undefined; top: parent.top } }
            PropertyChanges { target: messages; anchors.topMargin: 100 }
        }
    ]

    transitions: Transition {
        AnchorAnimation { easing.type: Easing.OutQuart; duration: 300 }
    }
}

This is for InfoBannerComponent.qml

import QtQuick 2.2

Item {
    id: banner

    property alias message : messageText.text

    height: 70

    Rectangle {
        id: background

        anchors.fill: banner
        color: "darkblue"
        smooth: true
        opacity: 0.8
    }

    Text {
        font.pixelSize: 24
        renderType: Text.QtRendering
        width: 150
        height: 40
        id: messageText


        anchors.fill: banner
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        wrapMode: Text.WordWrap

        color: "white"
    }

    states: State {
        name: "portrait"
        PropertyChanges { target: banner; height: 100 }
    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
            messages.state = ""
        }
    }
}

This is for main.qml

import QtQuick 2.3
import QtQuick.Window 2.2

Window {
    visible: true
    width: 360
    height: 360

    MouseArea {
        anchors.fill: parent
        onClicked: {
            Qt.quit();
        }
    }

    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }

    InfoBanner {
        id: messages
    }

    Component.onCompleted: messages.displayMessage("Hello World");
}

credit to marxian at marxoft dot co dot uk

查看更多
兄弟一词,经得起流年.
4楼-- · 2019-03-21 16:35

A similar but IMHO a more elegant approach compared to fpermana's answer is given below.

Toast.qml, can be used standalone and can be reused in a non-selfdestroying manner:

import QtQuick 2.0

/**
* @brief An Android-like timed message text in a box that selfdestroys when finished if desired
*/
Rectangle{

    /**
    * Public
    */

    /**
    * @brief Shows this Toast
    *
    * @param {string} text Text to show
    * @param {real} duration Duration to show in milliseconds, defaults to 3000
    */
    function show(text, duration){
        theText.text = text;
        if(typeof duration !== "undefined"){
            if(duration >= 2*fadeTime)
                time = duration;
            else
                time = 2*fadeTime;
            }
        else
            time = defaultTime;
        anim.start();
    }

    property bool selfDestroying: false ///< Whether this Toast will selfdestroy when it is finished

    /**
    * Private
    */

    id: root

    property real time: defaultTime
    readonly property real defaultTime: 3000
    readonly property real fadeTime: 300

    property real margin: 10

    width: childrenRect.width + 2*margin
    height: childrenRect.height + 2*margin
    radius: margin

    anchors.horizontalCenter: parent.horizontalCenter

    opacity: 0
    color: "white"

    Text{
        id: theText
        text: ""

        horizontalAlignment: Text.AlignHCenter
        x: margin
        y: margin
    }

    SequentialAnimation on opacity{
        id: anim

        running: false

        NumberAnimation{
            to: 0.9
            duration: fadeTime
        }
        PauseAnimation{
            duration: time - 2*fadeTime
        }
        NumberAnimation{
            to: 0
            duration: fadeTime
        }

        onRunningChanged:{
            if(!running && selfDestroying)
                root.destroy();
        }
    }
}

ToastManager.qml, creates and organizes Toasts if more than one are shown simultaneously:

import QtQuick 2.0

/**
 * @brief Manager that creates Toasts dynamically
 */
Column{

    /**
     * Public
     */

    /**
     * @brief Shows a Toast
     *
     * @param {string} text Text to show
     * @param {real} duration Duration to show in milliseconds, defaults to 3000
     */
    function show(text, duration){
        var toast = toastComponent.createObject(root);
        toast.selfDestroying = true;
        toast.show(text, duration);
    }

    /**
     * Private
     */

    id: root

    z: Infinity
    spacing: 5
    anchors.centerIn: parent

    property var toastComponent

    Component.onCompleted: toastComponent = Qt.createComponent("Toast.qml")
}

main.qml that uses the ToastManager:

import QtQuick 2.0

ApplicationWindow{
    visible: true

    /* other components of the application */

    ToastManager{ id: toast }

    onSomeEvent: toast.show("Some event happened")
    onImportantEvent: toast.show("An important event happened!", 5000)
}
查看更多
在下西门庆
5楼-- · 2019-03-21 16:53

I've improved on Ayberk Özgür's answer by making it more similar to the Android implementation while still working the same on iOS. This implementation creates black toast with white text that appears at the bottom of the screen. It also supports multiple simultaneous toasts using ToastManager with the newest toast at the bottom and uses nice animations.

The code is available on Github and copied below for convenience:

Toast.qml, can be used standalone and can be reused in a non-selfdestroying manner:

// Toast.qml
import QtQuick 2.0

/**
  * @brief An Android-like timed message text in a box that self-destroys 
  * when finished if desired
  */
Rectangle {

    /**
      * Public
      */

    /**
      * @brief Shows this Toast
      *
      * @param {string} text Text to show
      * @param {real} duration Duration to show in milliseconds, defaults to 3000
      */
    function show(text, duration) {
        message.text = text;
        if (typeof duration !== "undefined") { // checks if parameter was passed
            time = Math.max(duration, 2 * fadeTime);
        }
        else {
            time = defaultTime;
        }
        animation.start();
    }

    // whether this Toast will self-destroy when it is finished
    property bool selfDestroying: false  

    /**
      * Private
      */

    id: root

    readonly property real defaultTime: 3000
    property real time: defaultTime
    readonly property real fadeTime: 300

    property real margin: 10

    anchors {
        left: parent.left
        right: parent.right
        margins: margin
    }

    height: message.height + margin
    radius: margin

    opacity: 0
    color: "#222222"

    Text {
        id: message
        color: "white"
        wrapMode: Text.Wrap
        horizontalAlignment: Text.AlignHCenter
        anchors {
            top: parent.top
            left: parent.left
            right: parent.right
            margins: margin / 2
        }
    }

    SequentialAnimation on opacity {
        id: animation
        running: false 


        NumberAnimation {
            to: .9
            duration: fadeTime
        } 

        PauseAnimation {
            duration: time - 2 * fadeTime
        } 

        NumberAnimation {
            to: 0
            duration: fadeTime
        }

        onRunningChanged: {
            if (!running && selfDestroying) {
                root.destroy();
            }
        }
    }
}

ToastManager.qml, creates and organizes Toasts if more than one are shown simultaneously:

// ToastManager.qml
import QtQuick 2.0

/**
  * @brief Manager that creates Toasts dynamically
  */
ListView {
    /**
      * Public
      */

    /**
      * @brief Shows a Toast
      *
      * @param {string} text Text to show
      * @param {real} duration Duration to show in milliseconds, defaults to 3000
      */
    function show(text, duration) {
        model.insert(0, {text: text, duration: duration});
    }

    /**
      * Private
      */

    id: root

    z: Infinity
    spacing: 5
    anchors.fill: parent
    anchors.bottomMargin: 10
    verticalLayoutDirection: ListView.BottomToTop

    interactive: false

    displaced: Transition {
        NumberAnimation {
            properties: "y"
            easing.type: Easing.InOutQuad
        }
    }

    delegate: Toast {
        Component.onCompleted: {
            if (typeof duration === "undefined") {
                show(text);
            }
            else {
                show(text, duration);
            }
        }
    }

    model: ListModel {id: model}
}

main.qml that uses the ToastManager:

// main.qml
import QtQuick 2.0
import QtQuick.Controls 2.0

ApplicationWindow {
    visible: true
    height: 640
    width: 480
    id: root

    ToastManager {
        id: toast
    }

    Timer {
        interval: 1000
        repeat: true
        running: true
        property int i: 0
        onTriggered: {
            toast.show("This timer has triggered " + (++i) + " times!");
        }
    }

    Timer {
        interval: 3000
        repeat: true
        running: true
        property int i: 0
        onTriggered: {
            toast.show("This important message has been shown " + (++i) + " times.", 5000);
        }
    }
}
查看更多
登录 后发表回答