ZK框架:ListItemRenderer内按钮绑定Composer方法的实现问题
在ZK ListItemRenderer中绑定按钮点击到Composer方法的解决方案
我之前也碰到过这个场景——在自定义ListItemRenderer里生成的按钮,要触发Composer里的业务方法,因为Renderer和Composer属于不同的组件上下文,直接绑定会有点麻烦,这里给你几个实用的解决办法:
方法一:直接获取Composer实例并添加事件监听器
这种方式最直接,在Renderer的render方法里拿到目标Composer的引用,然后给按钮手动添加点击事件监听器,直接调用Composer的方法:
Renderer代码
package pkg; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zul.Button; import org.zkoss.zul.Listcell; import org.zkoss.zul.Listitem; import org.zkoss.zul.ListitemRenderer; import org.zkoss.zul.Window; public class MyRenderer implements ListitemRenderer<YourDataModel> { @Override public void render(Listitem item, YourDataModel data, int index) throws Exception { // 先渲染列表项的其他内容(比如对应listheader的单元格) Listcell nameCell = new Listcell(data.getName()); item.appendChild(nameCell); // 创建操作按钮所在的单元格 Listcell btnCell = new Listcell(); Button actionBtn = new Button("执行操作"); // 获取绑定到window的Composer实例 Window myWin = (Window) item.getPage().getFellow("mywin"); MyComposer composer = (MyComposer) myWin.getComposer(); // 给按钮添加点击事件,调用Composer的方法并传递当前数据 actionBtn.addEventListener(Events.ON_CLICK, new EventListener<Event>() { @Override public void onEvent(Event event) throws Exception { composer.handleButtonClick(data); } }); btnCell.appendChild(actionBtn); item.appendChild(btnCell); } }
Composer代码
package pkg; import org.zkoss.zk.ui.select.SelectorComposer; import org.zkoss.zk.ui.select.annotation.Wire; import org.zkoss.zul.Messagebox; import org.zkoss.zul.Window; import org.zkoss.zul.Listbox; public class MyComposer extends SelectorComposer<Window> { // 可以根据需要Wire组件,比如listbox @Wire private Listbox mylbx; // 供Renderer按钮调用的方法 public void handleButtonClick(YourDataModel data) { // 这里写你的业务逻辑,比如弹窗提示、修改数据等 Messagebox.show("触发了按钮点击,对应数据:" + data.getName()); } }
方法二:使用事件转发(Event Forwarding)
如果不想直接在Renderer里持有Composer的引用,可以把按钮的点击事件转发到window或者listbox,然后在Composer里监听转发后的事件,同时传递数据:
Renderer代码
@Override public void render(Listitem item, YourDataModel data, int index) throws Exception { // ...其他单元格渲染逻辑 Button actionBtn = new Button("执行操作"); // 把当前数据存到按钮的属性中,方便后续获取 actionBtn.setAttribute("targetData", data); // 转发按钮的onClick事件到window,自定义事件名为onItemBtnClick actionBtn.setForward("onClick=mywin.onItemBtnClick"); btnCell.appendChild(actionBtn); item.appendChild(btnCell); }
Composer代码
package pkg; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.select.SelectorComposer; import org.zkoss.zk.ui.select.annotation.Listen; import org.zkoss.zul.Button; import org.zkoss.zul.Messagebox; import org.zkoss.zul.Window; public class MyComposer extends SelectorComposer<Window> { // 监听转发过来的事件 @Listen("onItemBtnClick = #mywin") public void handleItemBtnClick(Event event) { // 获取触发事件的按钮 Button sourceBtn = (Button) event.getTarget(); // 取出之前存的数据 YourDataModel data = (YourDataModel) sourceBtn.getAttribute("targetData"); // 执行业务逻辑 Messagebox.show("通过事件转发触发,数据:" + data.getName()); } }
注意事项
- 如果你不想硬编码window的id(
mywin),可以通过item.getListbox().getParent()的方式向上遍历组件树找到对应的window,避免耦合。 - 确保你的数据模型
YourDataModel是可以安全传递的,如果是复杂对象,注意不要有序列化问题(事件转发时如果跨页面才需要序列化,同页面没问题)。 - 如果项目已经在使用MVVM模式,也可以给按钮设置
setCommand和setData,然后在ViewModel里用@Command和@BindingParam接收,这种方式更符合MVVM的解耦思想。
内容的提问来源于stack exchange,提问作者Alvaro Pedraza
相关产品推荐
相关产品推荐

