您的位置:首页 > 其它

如何在QML中设计一个expandable ListView

2015-06-05 09:37 281 查看
在前面的文章“如何在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

ListModel {
ListElement {
title: "Pancakes"
picture: "content/pics/pancakes.jpg"
ingredients: "<html>
<ul>
<li> 1 cup (150g) self-raising flour
<li> 1 tbs caster sugar
<li> 3/4 cup (185ml) milk
<li> 1 egg
</ul>
</html>"
method: "<html>
<ol>
<li> Sift flour and sugar together into a bowl. Add a pinch of salt.
<li> Beat milk and egg together, then add to dry ingredients. Beat until smooth.
<li> Pour mixture into a pan on medium heat and cook until bubbles appear on the surface.
<li> Turn over and cook other side until golden.
</ol>
</html>"
}
ListElement {
title: "Fruit Salad"
picture: "content/pics/fruit-salad.jpg"
ingredients: "* Seasonal Fruit"
method: "* Chop fruit and place in a bowl."
}
ListElement {
title: "Vegetable Soup"
picture: "content/pics/vegetable-soup.jpg"
ingredients: "<html>
<ul>
<li> 1 onion
<li> 1 turnip
<li> 1 potato
<li> 1 carrot
<li> 1 head of celery
<li> 1 1/2 litres of water
</ul>
</html>"
method: "<html>
<ol>
<li> Chop vegetables.
<li> Boil in water until vegetables soften.
<li> Season with salt and pepper to taste.
</ol>
</html>"
}
ListElement {
title: "Hamburger"
picture: "content/pics/hamburger.jpg"
ingredients: "<html>
<ul>
<li> 500g minced beef
<li> Seasoning
<li> lettuce, tomato, onion, cheese
<li> 1 hamburger bun for each burger
</ul>
</html>"
method: "<html>
<ol>
<li> Mix the beef, together with seasoning, in a food processor.
<li> Shape the beef into burgers.
<li> Grill the burgers for about 5 mins on each side (until cooked through)
<li> Serve each burger on a bun with ketchup, cheese, lettuce, tomato and onion.
</ol>
</html>"
}
ListElement {
title: "Lemonade"
picture: "content/pics/lemonade.jpg"
ingredients: "<html>
<ul>
<li> 1 cup Lemon Juice
<li> 1 cup Sugar
<li> 6 Cups of Water (2 cups warm water, 4 cups cold water)
</ul>
</html>"
method: "<html>
<ol>
<li> Pour 2 cups of warm water into a pitcher and stir in sugar until it dissolves.
<li> Pour in lemon juice, stir again, and add 4 cups of cold water.
<li> Chill or serve over ice cubes.
</ol>
</html>"
}
}


在这里,我们可以看到在文字中,我们可以使用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模式。

整个项目的代码在:https://github.com/liu-xiao-guo/expandinglist
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: