码迷,mamicode.com
首页 > 其他好文 > 详细

如何在QML中设计一个expandable ListView

时间:2015-06-05 10:12:50      阅读:204      评论:0      收藏:0      [点我收藏+]

标签:

在前面的文章“如何在QML中使用ListView并导航到其它页面中”中,我们已经介绍了各种在ListView中导航到其它页面的方法。在这篇文章中,我来介绍如何建立一个expandable的ListView。通过这样的方法,ListView可以不用导航到其它的页面中,但是它可以通过状态的控制占据整个页面,而得到显示。


首先我们可以使用Ubuntu SDK来创建一个最简单的“QML App with Simple UI (qmlproject)”项目。我们的Main.qml非常简单:

Main.qml

import QtQuick 2.4
import Ubuntu.Components 1.2

/*!
    \brief MainView with a Label and Button elements.
*/

MainView {
    // objectName for functional testing purposes (autopilot-qt5)
    objectName: "mainView"

    // Note! applicationName needs to match the "name" field of the click manifest
    applicationName: "expandinglist.liu-xiao-guo"

    /*
     This property enables the application to change orientation
     when the device is rotated. The default is false.
    */
    //automaticOrientation: true

    // Removes the old toolbar and enables new features of the new header.
//    useDeprecatedToolbar: false

    width: units.gu(60)
    height: units.gu(85)

    Page {
        id: mainpage
        title: i18n.tr("expandinglist")
        flickable: null

        ListView {
            id: listView
            anchors.fill: parent
            clip: true
            model: RecipesModel {}
            delegate: RecipesDelegate {}
        }
    }
}


就像上面的代码显示的那样,我们需要一个model。为此,我们创建了如下的RecipesModel.qml文件:

RecipesModel.qml

import QtQuick 2.0
import Ubuntu.Components 1.2

// Delegate for the recipes.  This delegate has two modes:
    // 1. List mode (default), which just shows the picture and title of the recipe.
    // 2. Details mode, which also shows the ingredients and method.
//Component {
//    id: recipeDelegate
//! [0]
    Item {
        id: recipe

        // Create a property to contain the visibility of the details.
        // We can bind multiple element's opacity to this one property,
        // rather than having a "PropertyChanges" line for each element we
        // want to fade.
        property real detailsOpacity : 0
//! [0]
        width: ListView.view.width
        height: units.gu(10)

        // A simple rounded rectangle for the background
        Rectangle {
            id: background
            x: 2; y: 2; width: parent.width - x*2; height: parent.height - y*2
            color: "ivory"
            border.color: "orange"
            radius: 5
        }

        // This mouse region covers the entire delegate.
        // When clicked it changes mode to 'Details'.  If we are already
        // in Details mode, then no change will happen.
//! [1]
        MouseArea {
            anchors.fill: parent
            onClicked: {
                console.log("recipe.y: " + recipe.y );
                console.log("origin.y: " + listView.originY );
                recipe.state = 'Details';
            }
        }

        // Lay out the page: picture, title and ingredients at the top, and method at the
        // bottom.  Note that elements that should not be visible in the list
        // mode have their opacity set to recipe.detailsOpacity.

        Row {
            id: topLayout
            x: 10; y: 10; height: recipeImage.height; width: parent.width
            spacing: 10

            Image {
                id: recipeImage
                width: units.gu(8); height: units.gu(8)
                source: picture
            }
//! [1]
            Column {
                width: background.width - recipeImage.width - 20; height: recipeImage.height
                spacing: 5

                Text {
                    text: title
                    font.bold: true; font.pointSize: units.gu(2)
                }

                SmallText {
                    text: "Ingredients"
                    font.bold: true
                    opacity: recipe.detailsOpacity
                }

                SmallText {
                    text: ingredients
                    wrapMode: Text.WordWrap
                    width: parent.width
                    opacity: recipe.detailsOpacity
                }
            }
        }

//! [2]
        Item {
            id: details
            x: 10; width: parent.width - 20

            anchors { top: topLayout.bottom; topMargin: 10; bottom: parent.bottom; bottomMargin: 10 }
            opacity: recipe.detailsOpacity
//! [2]
            SmallText {
                id: methodTitle
                anchors.top: parent.top
                text: "Method"
                font.pointSize: 12; font.bold: true
            }

            Flickable {
                id: flick
                width: parent.width
                anchors { top: methodTitle.bottom; bottom: parent.bottom }
                contentHeight: methodText.height
                clip: true

                Text { id: methodText; text: method; wrapMode: Text.WordWrap; width: details.width }
            }

            Image {
                anchors { right: flick.right; top: flick.top }
                source: "content/pics/moreUp.png"
                opacity: flick.atYBeginning ? 0 : 1
            }

            Image {
                anchors { right: flick.right; bottom: flick.bottom }
                source: "content/pics/moreDown.png"
                opacity: flick.atYEnd ? 0 : 1
            }
//! [3]
        }

        // A button to close the detailed view, i.e. set the state back to default ('').
        TextButton {
            y: 10
            anchors { right: background.right; rightMargin: 10 }
            opacity: recipe.detailsOpacity
            text: "Close"

            onClicked: recipe.state = '';
        }

        states: State {
            name: "Details"

            PropertyChanges { target: background; color: "white" }
            PropertyChanges { target: recipeImage; width: 130; height: 130 } // Make picture bigger
            PropertyChanges { target: recipe; detailsOpacity: 1; x: 0 } // Make details visible
            PropertyChanges { target: recipe; height: listView.height } // Fill the entire list area with the detailed view

            // Move the list so that this item is at the top.
            PropertyChanges { target: recipe.ListView.view; explicit: true;
                contentY: {
                    console.log("listView.contentY: " + listView.contentY);
                    return recipe.y + listView.contentY;
                }
            }

            // Disallow flicking while we're in detailed view
            PropertyChanges { target: recipe.ListView.view; interactive: false }
        }

        transitions: Transition {
            // Make the state changes smooth
            ParallelAnimation {
                ColorAnimation { property: "color"; duration: 500 }
                NumberAnimation { duration: 300; properties: "detailsOpacity,x,contentY,height,width" }
            }
        }
//    }
//! [3]
}



在这里,我们可以看到在文字中,我们可以使用html格式来格式化我们的文字。这对我们多样的显示是非常有用的。


我们最关键的设计在于RecipesDelegate.qml文件:

RecipesDelegate.qml

import QtQuick 2.0
import Ubuntu.Components 1.2

// Delegate for the recipes.  This delegate has two modes:
    // 1. List mode (default), which just shows the picture and title of the recipe.
    // 2. Details mode, which also shows the ingredients and method.
//Component {
//    id: recipeDelegate
//! [0]
    Item {
        id: recipe

        // Create a property to contain the visibility of the details.
        // We can bind multiple element's opacity to this one property,
        // rather than having a "PropertyChanges" line for each element we
        // want to fade.
        property real detailsOpacity : 0
//! [0]
        width: ListView.view.width
        height: units.gu(10)

        // A simple rounded rectangle for the background
        Rectangle {
            id: background
            x: 2; y: 2; width: parent.width - x*2; height: parent.height - y*2
            color: "ivory"
            border.color: "orange"
            radius: 5
        }

        // This mouse region covers the entire delegate.
        // When clicked it changes mode to 'Details'.  If we are already
        // in Details mode, then no change will happen.
//! [1]
        MouseArea {
            anchors.fill: parent
            onClicked: {
                console.log("recipe.y: " + recipe.y );
                console.log("origin.y: " + listView.originY );
                recipe.state = 'Details';
            }
        }

        // Lay out the page: picture, title and ingredients at the top, and method at the
        // bottom.  Note that elements that should not be visible in the list
        // mode have their opacity set to recipe.detailsOpacity.

        Row {
            id: topLayout
            x: 10; y: 10; height: recipeImage.height; width: parent.width
            spacing: 10

            Image {
                id: recipeImage
                width: units.gu(8); height: units.gu(8)
                source: picture
            }
//! [1]
            Column {
                width: background.width - recipeImage.width - 20; height: recipeImage.height
                spacing: 5

                Text {
                    text: title
                    font.bold: true; font.pointSize: units.gu(2)
                }

                SmallText {
                    text: "Ingredients"
                    font.bold: true
                    opacity: recipe.detailsOpacity
                }

                SmallText {
                    text: ingredients
                    wrapMode: Text.WordWrap
                    width: parent.width
                    opacity: recipe.detailsOpacity
                }
            }
        }

//! [2]
        Item {
            id: details
            x: 10; width: parent.width - 20

            anchors { top: topLayout.bottom; topMargin: 10; bottom: parent.bottom; bottomMargin: 10 }
            opacity: recipe.detailsOpacity
//! [2]
            SmallText {
                id: methodTitle
                anchors.top: parent.top
                text: "Method"
                font.pointSize: 12; font.bold: true
            }

            Flickable {
                id: flick
                width: parent.width
                anchors { top: methodTitle.bottom; bottom: parent.bottom }
                contentHeight: methodText.height
                clip: true

                Text { id: methodText; text: method; wrapMode: Text.WordWrap; width: details.width }
            }

            Image {
                anchors { right: flick.right; top: flick.top }
                source: "content/pics/moreUp.png"
                opacity: flick.atYBeginning ? 0 : 1
            }

            Image {
                anchors { right: flick.right; bottom: flick.bottom }
                source: "content/pics/moreDown.png"
                opacity: flick.atYEnd ? 0 : 1
            }
//! [3]
        }

        // A button to close the detailed view, i.e. set the state back to default ('').
        TextButton {
            y: 10
            anchors { right: background.right; rightMargin: 10 }
            opacity: recipe.detailsOpacity
            text: "Close"

            onClicked: recipe.state = '';
        }

        states: State {
            name: "Details"

            PropertyChanges { target: background; color: "white" }
            PropertyChanges { target: recipeImage; width: 130; height: 130 } // Make picture bigger
            PropertyChanges { target: recipe; detailsOpacity: 1; x: 0 } // Make details visible
            PropertyChanges { target: recipe; height: listView.height } // Fill the entire list area with the detailed view

            // Move the list so that this item is at the top.
            PropertyChanges { target: recipe.ListView.view; explicit: true;
                contentY: {
                    console.log("listView.contentY: " + listView.contentY);
                    return recipe.y + listView.contentY;
                }
            }

            // Disallow flicking while we're in detailed view
            PropertyChanges { target: recipe.ListView.view; interactive: false }
        }

        transitions: Transition {
            // Make the state changes smooth
            ParallelAnimation {
                ColorAnimation { property: "color"; duration: 500 }
                NumberAnimation { duration: 300; properties: "detailsOpacity,x,contentY,height,width" }
            }
        }
//    }
//! [3]
}


在这个delegate里,它有两个状态:

  • 默认的List模式。在这种模式下,它只显示一个图片及title
  • 详细模式。在这种模式下,除了显示上面的图片和title以外,还显示model中的ingredients及method
在详细模式下的状态为:

       states: State {
            name: "Details"

            PropertyChanges { target: background; color: "white" }
            PropertyChanges { target: recipeImage; width: 130; height: 130 } // Make picture bigger
            PropertyChanges { target: recipe; detailsOpacity: 1; x: 0 } // Make details visible
            PropertyChanges { target: recipe; height: listView.height } // Fill the entire list area with the detailed view

            // Move the list so that this item is at the top.
            PropertyChanges { target: recipe.ListView.view; explicit: true;
                contentY: {
                    console.log("listView.contentY: " + listView.contentY);
                    return recipe.y + listView.contentY;
                }
            }

            // Disallow flicking while we're in detailed view
            PropertyChanges { target: recipe.ListView.view; interactive: false }
        }

在这里,一定要注意:

            PropertyChanges { target: recipe.ListView.view; explicit: true;
                contentY: {
                    console.log("listView.contentY: " + listView.contentY);
                    return recipe.y + listView.contentY;
                }
            }

可以帮我们把当前的项移到ListView的窗口中。

我们运行我们的应用:

技术分享  技术分享  技术分享

在上面的第二个图中,点击“Close”按钮,就可以回到List模式。

整个项目的代码在:git clone https://gitcafe.com/ubuntu/expandinglist.git


如何在QML中设计一个expandable ListView

标签:

原文地址:http://blog.csdn.net/ubuntutouch/article/details/46371923

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!