步骤 2 - 实现拖放功能¶
这一步中,您将实现拖放功能。
创建拖动功能¶
在本节中,您首先使用 Kanzi Engine API 来实例化您在教程的上一步中创建的 Drag Item 预设模板。然后,您定义当用户开始拖放手势并拖动按钮时的行为。最后,您为导航栏中的每个按钮创建并配置一个拖放操作器。
要创建拖动功能:
在 Visual Studio 中,在
drag_and_drop.cpp文件的Application::onProjectLoaded函数中实例化用于可视化用户正在拖动的按钮的 Drag Item 预设:void onProjectLoaded() override { ... // Get the reference to the Drag Item prefab. ResourceManager* resourceManager = getDomain()->getResourceManager(); PrefabTemplateSharedPtr dragItemPrefab = resourceManager->acquireResource<PrefabTemplate>("kzb://drag_and_drop/Prefabs/Drag Item"); // Instantiate the Drag Item prefab. m_dragItem = dragItemPrefab->instantiate<Node2D>("Drag Item"); // Get the RootPage node using its alias. Node2DSharedPtr rootPage = screen->lookupNode<Node2D>("#RootPage"); // Add to the RootPage node the instance of the Drag Item prefab that you created. rootPage->addChild(m_dragItem); // Disable the Visible property of the Drag Item prefab instance. // You hide the Drag Item when the user is not dragging it. m_dragItem->setVisible(false); } private: ... // Define a member variable for the instantiated Drag Item. Node2DSharedPtr m_dragItem;
在
DragAndDrop类的私有部分中创建一个函数,该函数移动 Drag Item 预设实例:private: ... // Update the position of the Drag Item. void updateDragAndDrop(Vector2 dragPosition, Matrix3x3 dragWorldTransform) { // Calculate the local drag anchor that is the top-left corner of the button that the user is dragging. Vector2 localDragAnchor = dragPosition - m_dragGrabOffset; // Restrict the movement of the Drag Item to the x axis. localDragAnchor.setY(0.0f); // Calculate the global drag anchor. Vector2 globalDragAnchor = dragWorldTransform * localDragAnchor; // The structure that you use to describe the Render Transformation property of the Drag Item. SRTValue2D transform; // Set the Render Transformation property Translation property field to the global drag anchor. transform.setTranslation(globalDragAnchor); // Move the Drag Item for the amount of the dragged distance. m_dragItem->setRenderTransformation(transform); // Set the icons of the buttons. updateItems(); } ... // Define a member variable for the offset from the top-left corner of the button where the user pressed down or clicked it. Vector2 m_dragGrabOffset; };
为拖动消息添加处理程序:
要定义用户开始拖动按钮时的行为,在
DragAndDrop类的私有部分中定义DragAndDropManipulator::StartedMessage消息的处理程序:private: ... // Define the handler for the DragAndDropManipulator::StartedMessage message from 2D nodes // that have an input manipulator which generates drag-and-drop messages. // This prepares the 2D node for dragging. void onDragStarted(DragAndDropManipulator::StartedMessageArguments& messageArguments) { // Get from the message arguments the button that the user starts to drag. Node2DSharedPtr dragSourceItem = dynamic_pointer_cast<Node2D>(messageArguments.getSource()); // Get the size of the button that the user starts to drag. Vector2 dragSourceItemSize = dragSourceItem->getActualSize(); // Set the size of the Drag Item to be the same as the size of that button. m_dragItem->setSize(dragSourceItemSize.getX(), dragSourceItemSize.getY()); // Move the Drag Item to the correct position. updateDragAndDrop(messageArguments.getPoint(), dragSourceItem->getWorldTransform()); // Make the Drag Item visible. m_dragItem->setVisible(true); } ...
要定义用户拖动按钮时的行为,在
onDragStarted函数之后定义DragAndDropManipulator::MovedMessage消息的处理程序:// Define the handler for the DragAndDropManipulator::MovedMessage message from 2D nodes // that have an input manipulator which generates drag-and-drop messages. void onDragMoved(DragAndDropManipulator::MovedMessageArguments& messageArguments) { // Get from the message arguments the button that the user is dragging. Node2DSharedPtr dragSourceItem = dynamic_pointer_cast<Node2D>(messageArguments.getSource()); // Move the Drag Item and update the icons of the buttons. updateDragAndDrop(messageArguments.getPoint(), dragSourceItem->getWorldTransform()); }
创建并配置各按钮的拖放操纵器:
在
DragAndDrop类的私有部分中,添加一个为节点创建和配置拖放操作器的函数:// Create and configure a drag-and-drop manipulator for a node. void createDragAndDropManipulator(NodeSharedPtr dragSourceItem) { Domain* domain = getDomain(); // Create an input manipulator that generates drag-and-drop messages. DragAndDropManipulatorSharedPtr dragAndDropManipulator = DragAndDropManipulator::create(domain); // Add the input manipulator to the node. dragSourceItem->addInputManipulator(dragAndDropManipulator); // Set the duration of the long press before the drag-and-drop starts to 200 ms. The default is 500 ms. // This is the amount of time the user must press the node before they can start dragging it. dragAndDropManipulator->setPressDuration(chrono::milliseconds(200)); // Subscribe to the DragAndDropManipulator::StartedMessage message at the node. // The DragAndDropManipulator generates this message when the user presses the node // for the duration set by DragAndDropManipulator::setPressDuration. dragSourceItem->addMessageHandler(DragAndDropManipulator::StartedMessage, bind(&DragAndDrop::onDragStarted, this, placeholders::_1)); // Subscribe to the DragAndDropManipulator::MovedMessage message at the node. // The DragAndDropManipulator generates this message when the pointer moves. dragSourceItem->addMessageHandler(DragAndDropManipulator::MovedMessage, bind(&DragAndDrop::onDragMoved, this, placeholders::_1)); }
在
Application::onProjectLoaded函数的末尾,为每个按钮调用createDragAndDropManipulator函数:void onProjectLoaded() override { ... // Create a drag-and-drop manipulator for each button. // You get the button nodes using their aliases. createDragAndDropManipulator(screen->lookupNode<Node>("#Navigation")); createDragAndDropManipulator(screen->lookupNode<Node>("#Phone")); createDragAndDropManipulator(screen->lookupNode<Node>("#Applications")); createDragAndDropManipulator(screen->lookupNode<Node>("#Music")); createDragAndDropManipulator(screen->lookupNode<Node>("#Car")); }
构建和运行应用程序。
在应用程序中长按按钮,并在水平方向拖动。
您拖动 Drag Item 预设的一个实例。Drag Item 的左侧定位在输入指针处,并且 Drag Item 尚未显示图标。在下一节中,您将完成拖动功能。
完成拖动功能¶
这一节中,您首先设置用户拖动的按钮图标和位置。然后,在用户拖动其中一个按钮时,您重新定位按钮图标。
要完成拖动功能:
在调用
updateDragAndDrop函数之前的onDragStarted函数中,为 Drag Item 设置图标并正确定位节点:void onDragStarted(DragAndDropManipulator::StartedMessageArguments& messageArguments) { ... // Get the data context object of the button that the user starts to drag. m_draggedDataContext = dynamic_pointer_cast<DataObject>(dragSourceItem->getProperty(DataContext::DataContextProperty)); // Set the Data Context property of the Drag Item to the data context of the button. // This way you set the Drag Item to have the same icon as the button that the user starts to drag. m_dragItem->setProperty(DataContext::DataContextProperty, m_draggedDataContext); // Save the point from which the user started dragging the node, // relative to the origin (by default the top-left corner) of the node. m_dragGrabOffset = messageArguments.getPoint(); ... } ... // Define a member variable for the data context object of the button that the user drags. DataObjectSharedPtr m_draggedDataContext; };
在
updateDragAndDrop函数中,在调用updateItems()函数之前,添加代码以在用户拖动其中一个按钮时重新定位所有按钮的图标:void updateDragAndDrop(Vector2 dragPosition, Matrix3x3 dragWorldTransform) { ... // Get the global pointer position. Vector2 globalPointerPosition = dragWorldTransform * dragPosition; // Convert global coordinates to local coordinates of the Grid Layout 2D node. Vector2 hitTestPoint = *m_grid->globalToLocal(globalPointerPosition); // Get the width of the button. float cellWidth = m_grid->getActualColumnSize(0); // Calculate the index of the button in the Grid Layout 2D node. unsigned int cellIndex = 0; if (hitTestPoint.getX() > 0.0f) { cellIndex = floatToUint(hitTestPoint.getX() / cellWidth); cellIndex = min(cellIndex, static_cast<unsigned int>(m_grid->getChildCount() - 1u)); } // Remove the data object from the old position. m_rootData->removeChild(*m_draggedDataContext); // Insert the data object to the new position. m_rootData->insertChild(cellIndex, m_draggedDataContext); ... }
在
for循环中的updateItems()函数中添加if-else子句,该子句在用户拖动按钮时隐藏按钮的图标:void updateItems() { ... for (; dataIt != endDataIt; dataIt++, nodeIt++) { ... // If the button node is the one that the user is dragging, hide it. // You hide the node because you use the Drag Item to visualize the dragging of the node. if (m_draggedDataContext && itemData == m_draggedDataContext) { itemNode->setVisible(false); } else { itemNode->setVisible(true); } } }
创建放置功能¶
在前面的部分中,您实现了按钮的拖动。当用户结束拖放手势时,Drag Item 保持可见,并位于用户释放指针的确切位置。在本节中,您将添加代码,使按钮在用户放下时看起来像是落入其位置。
要创建放置功能:
要定义用户停止拖动节点并释放指针时的行为,在
onDragMoved函数之后定义DragAndDropManipulator::FinishedMessage消息的处理程序:// Define the handler for the DragAndDropManipulator::FinishedMessage message from 2D nodes // that have an input manipulator which generates drag-and-drop messages. void onDragFinished(DragAndDropManipulator::FinishedMessageArguments&) { // Hide the Drag Item prefab instance. m_dragItem->setVisible(false); // Clear the pointer to the data context object. m_draggedDataContext.reset(); // Assign the correct icons to the buttons. updateItems(); }
在
createDragAndDropManipulator函数的末尾,在节点处订阅DragAndDropManipulator::FinishedMessage消息:// Create and configure a drag-and-drop manipulator for a node. void createDragAndDropManipulator(NodeSharedPtr dragAndDropNode) { ... // Subscribe to the DragAndDropManipulator::FinishedMessage message at the node. // The DragAndDropManipulator generates this message when the user ends the drag-and-drop gesture // by releasing the pointer. dragSourceItem->addMessageHandler(DragAndDropManipulator::FinishedMessage, bind(&DragAndDrop::onDragFinished, this, placeholders::_1)); }
构建和运行应用程序。
当结束拖放手势时,Drag Item 变为不可见。
另请参阅¶
要详细了解拖放输入操纵器,请参阅 使用 Drag-And-Drop Manipulator。
要了解有关在 Kanzi 中处理用户输入的更多信息,请参阅 处理用户输入。