Qt 5.12下QML SwipeView重置Repeater模型时闪烁问题
Let's break down your problem and walk through practical solutions tailored for Qt 5.12:
Why the Repeater Flicker Happens
In Qt 5.12, SwipeView and Repeater have a synchronization gap when the underlying QAbstractListModel is reset. Here's the root cause:
- When you call
beginResetModel()andendResetModel(), theRepeaterdestroys all old delegates and spawns new ones immediately. SwipeViewdoesn't properly clean up old page components before the new ones are added, causing overlapping rendering of the first page's old and new instances—this is the flicker you're seeing.- The issue doesn't pop up with an empty initial model because there are no old components to clash with new ones. Standalone
Repeaterworks fine because it lacks SwipeView's complex page management logic.
Fix 1: Sync Repeater Model Updates with SwipeView Cleanup
You can work around the flicker by temporarily disconnecting the Repeater from the model during reset, giving SwipeView time to clear old pages. Here's the modified QML:
Item { id: root property var viewModel height: 30 * Screen.pixelDensity width: 60 * Screen.pixelDensity Rectangle { id: wrapper anchors.fill: parent color: "grey" SwipeView { id: swipe clip: true anchors.fill: parent Repeater { id: pageRepeater model: null // Start with empty model Rectangle { width: 20 height: 20 border.color: "black" border.width: 1 Text { text: index } } } } // Sync model resets with SwipeView cleanup Connections { target: root.viewModel onModelReset: { // Clear repeater first to let SwipeView remove old pages pageRepeater.model = null // Short delay ensures cleanup finishes before reassigning Qt.callLater(() => { pageRepeater.model = root.viewModel }) } } // Initialize model when root.viewModel is set Component.onCompleted: { if (root.viewModel) pageRepeater.model = root.viewModel } } }
This ensures old pages are fully removed before new ones are created, eliminating the flicker.
Fix 2: Correctly Using Instantiator (Why Your Code Failed)
Instantiator creates component instances but doesn't automatically add them to SwipeView's child list. You need to manually manage adding/removing instances. Here's the corrected code:
Rectangle { id: wrapper anchors.fill: parent color: "grey" SwipeView { id: swipe clip: true anchors.fill: parent } Instantiator { id: pageInstantiator model: root.viewModel delegate: Rectangle { width: 20 height: 20 border.color: "black" border.width: 1 Text { text: index } Component.onCompleted: console.log("Created page", index) } // Add new instances to SwipeView when created onObjectCreated: { if (object) swipe.addItem(object) } // Remove and destroy instances when model resets onObjectRemoved: { if (object) { swipe.removeItem(object) object.destroy() } } } }
Key points here:
- Use
swipe.addItem(object)to attach instantiated delegates to the SwipeView's page list. - When the model resets,
onObjectRemovedtriggers for old instances—we remove them from SwipeView and destroy them to free resources. - This gives you full control over page lifecycle, avoiding the synchronization issues with Repeater.
Alternative: Manual Page Management (Full Control)
If you want maximum control over page creation/destruction, skip Repeater/Instantiator entirely and manage pages directly when the model changes:
SwipeView { id: swipe clip: true anchors.fill: parent function updatePages(model) { // Clear all existing pages while (swipe.count > 0) { const page = swipe.itemAt(0) swipe.removeItem(page) page.destroy() } // Add new pages from model for (let i = 0; i < model.count; i++) { const page = pageComponent.createObject(swipe) page.pageIndex = i swipe.addItem(page) } } Connections { target: root.viewModel onModelReset: swipe.updatePages(root.viewModel) } Component { id: pageComponent Rectangle { property int pageIndex width: 20 height: 20 border.color: "black" border.width: 1 Text { text: pageIndex } } } }
This method is more verbose but eliminates any built-in synchronization conflicts between SwipeView and repeater components.
内容的提问来源于stack exchange,提问作者Adso4

