Fyne的列表小部件无法正确显示项目。

huangapple go评论105阅读模式
英文:

Fyne List widget doesn't display items correctly

问题

我正在尝试在一个listwithdata小部件中显示字母表。我正在使用自定义的可点击标签作为小部件来显示在列表中。不知何故,当小部件加载时,一切都正常显示。但是当我开始滚动时,字母开始以完全随机的顺序显示,我无法弄清楚原因。以下是一个完全可工作的代码来重现这个错误。

点击操作正常工作,并给我提供了正确的字母(不是显示的字母,而是应该显示的字母)。

我猜我一定在某个地方做错了,但我找不出问题所在。

如果有人能帮忙,将不胜感激!

英文:

I'm trying to display the alphabet in a listwithdata widget. I am using a custom clickable label as the widget to display in the list.
For some reason everything displays fine when the widget loads. But when I start scrolling the letters starts being displayed in a completely random order and I cant figure out why.
Here is a fully working code to reproduce the bug.

package main

import (
	"fmt"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/data/binding"
	"fyne.io/fyne/v2/widget"
)

func makeAlphabet() []string {
	var alphabet []string
	for ch := 'A'; ch <= 'Z'; ch++ {
		alphabet = append(alphabet, string(ch))
	}
	return alphabet
}

type TapLabel struct {
	*widget.Label //composition

	//function pointers to set to get events
	OnTapped func(string)
}

func (mc *TapLabel) Tapped(pe *fyne.PointEvent) {
	if mc.OnTapped != nil {
		mc.OnTapped(mc.Text)
	}
}

func NewTapLabel(text string, tappedLeft func(string)) *TapLabel {
	return &TapLabel{widget.NewLabel(text), tappedLeft}
}

func alphabetToBrands(letter string) {
	fmt.Println(letter)
}

func main() {
	app := app.New()
	window := app.NewWindow("tac_hub")
	window.Resize(fyne.NewSize(200,200))

	rawData := makeAlphabet()
	data := binding.BindStringList(&rawData)
	list := widget.NewListWithData(
		data,
		func() fyne.CanvasObject {
			return NewTapLabel("template", alphabetToBrands)
		},
		func(i binding.DataItem, o fyne.CanvasObject) {
			o.(*TapLabel).Bind(i.(binding.String))
		},
	)

	window.SetContent(list)
	window.ShowAndRun()
}

The click action works correctly and gives me the correct letter (not the one that is displayed but the one that should be displayed).
I'm guessing I must be doing something wrong somewhere but I can't figure out what.

If anyone can help it would be appreciated!

答案1

得分: 2

使用自定义小部件的解决方案

这是基于 Fyne 的扩展小部件教程。特别是你调用了 ExtendBaseWidget

> ExtendBaseWidget 用于扩展小部件以利用 BaseWidget 的功能。

你仍然嵌入了 widget.Label,但不使用指针(使用指针也可以,但文档使用的是非指针示例),这样必要的方法就会被提升到你的小部件中:

func NewTapLabel(text string, tappedLeft func(string)) *TapLabel {
    label := &TapLabel{}
    label.ExtendBaseWidget(label)
    label.SetText(text)
    label.OnTapped = tappedLeft
    return label
}

然后,你需要更改更新函数,将实际的绑定字符串设置为标签的文本,而不仅仅是像在原始示例中重新绑定数据:

func(i binding.DataItem, o fyne.CanvasObject) {
    s, _ := i.(binding.String).Get()
    o.(*TapLabel).SetText(s)
}

至于为什么如果省略调用 ExtendBaseWidget 会出现错误,我猜测是因为缺少基本小部件的实现,而这个方法会设置它。


不使用自定义小部件的解决方案

list := widget.NewListWithData(
    data,
    func() fyne.CanvasObject {
        return &widget.Label{Text: "template"}
    },
    func(i binding.DataItem, o fyne.CanvasObject) {
        o.(*widget.Label).Bind(i.(binding.String))
    },
)
list.OnSelected = func(id widget.ListItemID) {
    fmt.Println(rawData[id])
}

放弃你的自定义 TapLabel,而是使用 &widget.Label{Text: "template"},然后使用 list.OnSelected 并在数据切片周围进行闭包,以打印被点击的项。

英文:

Solution with custom widget:

This is based on Fyne Extending Widgets tutorial. In particular you call ExtendBaseWidget:

> ExtendBaseWidget is used by an extending widget to make use of BaseWidget functionality.

You still embed widget.Label, but not as a pointer (using a pointer also works, but the documentation uses the non-pointer example), so that the necessary methods are promoted into your widget:

func NewTapLabel(text string, tappedLeft func(string)) *TapLabel {
    label := &TapLabel{}
    label.ExtendBaseWidget(label)
    label.SetText(text)
    label.OnTapped = tappedLeft
    return label
}

And then you change the update function to set the actual bound string to the label, instead of just binding the data again as in your original example:

        func(i binding.DataItem, o fyne.CanvasObject) {
            s, _ := i.(binding.String).Get()
            o.(*TapLabel).SetText(s)
        },

As of why the bug occurs if you omit calling ExtendBaseWidget, I suspect it's because it was missing the base widget implementation, which this method sets.

<hr>

Solution without custom widget:

    list := widget.NewListWithData(
        data,
        func() fyne.CanvasObject {
            return &amp;widget.Label{Text: &quot;template&quot;}
        },
        func(i binding.DataItem, o fyne.CanvasObject) {
            o.(*widget.Label).Bind(i.(binding.String))
        },
    )
    list.OnSelected = func(id widget.ListItemID) {
        fmt.Println(rawData[id])
    }

Dropping your custom TapLabel in favor of &amp;widget.Label{Text: &quot;template&quot;}, then to print the tapped item you use list.OnSelected and close around the data slice.

答案2

得分: 0

看起来这里的参数 o.(*TapLabel).Bind(i.(binding.String)) 在滚动操作发生时会发生变化。在该函数中添加一个日志来观察 i.(binding.String) 的值。

请查看这个示例:列表示例

英文:

Looks like the argument here o.(*TapLabel).Bind(i.(binding.String)) changes as the scroll action happens. Add a log to observe the i.(binding.String) value in that function as you scroll

Take a look at this: list example

答案3

得分: 0

据我所见,自定义小部件的构造函数没有调用ExtendBaseWidget,这对于正确渲染是必需的。我认为这也回答了@blackgreen的问题。

正如接受的答案中所指出的那样,您不应该使用其他小部件的构造函数嵌入它们。

英文:

As far as I can see the constructor for the custom widget did not call ExtendBaseWidget which is required for rendering to work correctly. I think that answers @blackgreen question too.

You should not embed other widgets using their constructor as noted in the accepted answer.

huangapple
  • 本文由 发表于 2021年11月10日 05:39:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/69905449.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定