问题
I have a Flickable
that includes a large number of TextField
objects laid out in a column with each TextField anchored to the bottom on the previous TextField. Everything is working fine except that when I use the tab key to navigate through these fields, eventually the focus goes to a TextField that is outside the visible rectangle of the Flickable
and then the user can't see the cursor until they scroll down the Flickable manually.
Essentially I'm looking for some kind of ".ensureVisible()" method such that when a TextField receives the focus, the Flickable is automatically scrolled so that the just-focused TextField is entirely visible.
回答1:
Have you considered a more model-ar approach to this? I mean if you use something like a ListView
you can simply change the currentItem
at which point the view will automatically scroll to it if it is out of the visible range.
Additionally, it will only load the text elements that are in the visible range, saving on some memory.
But even with your current approach it won't be that complex to ensure visibility.
Flickable {
id: flick
anchors.fill: parent
contentHeight: col.height
function ensureVisible(item) {
var ypos = item.mapToItem(contentItem, 0, 0).y
var ext = item.height + ypos
if ( ypos < contentY // begins before
|| ypos > contentY + height // begins after
|| ext < contentY // ends before
|| ext > contentY + height) { // ends after
// don't exceed bounds
contentY = Math.max(0, Math.min(ypos - height + item.height, contentHeight - height))
}
}
Column {
id: col
Repeater {
id: rep
model: 20
delegate: Text {
id: del
text: "this is item " + index
Keys.onPressed: rep.itemAt((index + 1) % rep.count).focus = true
focus: index === 0
color: focus ? "red" : "black"
font.pointSize: 40
onFocusChanged: if (focus) flick.ensureVisible(del)
}
}
}
}
The solution is quick and cruddy, but it will be trivial to put it into production shape. It is important to map to the contentItem
rather than the flickable, as the latter would give the wrong results, taking the amount of current scrolling into account. Using mapping will make the solution agnostic to whatever positioning scheme you might be using, and will also support arbitrary levels of nested objects.
回答2:
dtech's answer is spot on. It's easily combined with a nice snap-to animation and easy to modify for x-direction flickables too. Also, the user may be deliberately flicking or dragging the flickable. In my case, C++ code was controlling the text or display effects for the items in a grid layout, contained in the flickable. The flickable needed to flick nicely when the C++ code signalled it to do so, but not if the user was deliberately dragging or flicking. Here is dtech's function modified for an x-direction flickable:
function ensureVisible(item) {
if (moving || dragging)
return;
var xpos = item.mapToItem(contentItem, 0, 0).x
var ext = item.width + xpos
if ( xpos < contentX // begins before
|| xpos > contentX + width // begins after
|| ext < contentX // ends before
|| ext > contentX + width) { // ends after
// don't exceed bounds
var destinationX = Math.max(0, Math.min(xpos - width + item.width, contentWidth - width))
ensureVisAnimation.to = destinationX;
ensureVisAnimation.from = contentX;
ensureVisAnimation.start();
}
}
//This animation is for the ensure-visible feature.
NumberAnimation on contentX {
id: ensureVisAnimation
to: 0 //Dummy value - will be set up when this animation is called.
duration: 300
easing.type: Easing.OutQuad;
}
来源:https://stackoverflow.com/questions/45946637/how-to-make-flickable-ensure-the-visibility-of-an-item-inside-of-it