本帖最后由 2280761425 于 2019-8-8 18:19 编辑

很长一段时间,对于Inventory的判断我一直是用title来进行的,如果涉及到数据传递部分,那就只能通过各种神奇的方法进行了(比如截取title或者物品堆标记)。
然后今天海螺大佬告诉我还能用holder判断,我就试了一下,是挺方便的,只要新建一个实现了InventoryHolder的类传入createInventory里的第一个参数就行了(就是一般填null的那个),要判断就用instanceof来判断。
之后我想既然都可以自己传入对象了,那直接传递数据是不是可以?
然后我写了这样一个类:
  1. public class CustomInventoryHolder<T> implements InventoryHolder {
  2.     protected Inventory inventory;
  3.     protected T data;
  4.    
  5.     public CustomInventoryHolder(T data/*, int size, String title*/) {
  6.         this.data = data;
  7.         //更方便创建Inventory与holder的方法
  8.         //this.inventory = Bukkit.createInventory(this, size, title);
  9.     }

  10.     //实现InventoryHolder里的方法,由于不能返回null所以这里设置为持有holder的Inventory
  11.     @Override
  12.     public Inventory getInventory() {
  13.         return this.inventory;
  14.     }

  15.     public T getData() {
  16.         return this.data;
  17.     }

  18.     public void setData(T data) {
  19.         this.data = data;
  20.     }

  21.     //Inventory在这里设置。构造函数不加Inventory参数是因为在对Inventory传入holder时Inventory还未创建
  22.     public void setInventory(Inventory inventory) {
  23.         this.inventory = inventory;
  24.     }
  25. }
复制代码


这样就可以进行数据传递了,与之前的方法比较代码不知道简洁了多少(bug顺便也少了很多233)
为了方便还可以把获取holder用一个方法进行:
  1. public static CustomInventoryHolder<?> getHolder(Inventory inventory) {
  2.     InventoryHolder holder = inventory.getHolder();
  3.     if(!(holder instanceof CustomInventoryHolder)) {
  4.             return null;
  5.     }
  6.     return (CustomInventoryHolder<?>) holder;
  7. }
复制代码


这样判断一个物品栏就既可以通过CustomInventoryHolder里的data的数据也可以通过不同的继承CustomInventoryHolder的类,当然,再创建一个方法也可以

测试代码(由于是测试所以类型判断并没有那么严格):
  1. //命令里
  2. Map<String, Object> map = new HashMap<>();
  3. map.put("name", args[1]);
  4. map.put("sender", sender);
  5. map.put("count", 0);
  6. ((Player) sender).openInventory(InventoryTools.createDataInventory(sender.getServer(), map));
复制代码
  1. //事件里
  2. CustomInventoryHolder<?> holder = InventoryTools.getHolder(event.getInventory());
  3. if(holder != null) {
  4.     Map<String, Object> data = (Map<String, Object>) holder.getData();
  5.     //测试map保存不同类型数据
  6.     event.getWhoClicked().sendMessage((String) data.get("name"));
  7.     event.getWhoClicked().sendMessage(((CommandSender) data.get("sender")).getName());
  8.     //测试map数据读写
  9.     event.getWhoClicked().sendMessage(data.get("count").toString());
  10.     data.put("count", ((Integer) data.get("count") + 1));
  11. }
复制代码


InventoryTools类:
  1. public class InventoryTools {
  2.     //也是可以直接写成判断类型再强转的
  3.     public static CustomInventoryHolder<?> getHolder(Inventory inventory) {
  4.         InventoryHolder holder = inventory.getHolder();
  5.             if(!(holder instanceof CustomInventoryHolder)) {
  6.                 return null;
  7.             }
  8.             return (CustomInventoryHolder<?>) holder;
  9.         }

  10.     //简化创建过程
  11.     //已经不需要使用本方法了,可以直接用holder的构造函数创建Inventory(注释部分),感谢@810587921 提供的方法
  12.     public static <T> Inventory createDataInventory(Server server, T data) {
  13.         CustomInventoryHolder<T> holder = new CustomInventoryHolder<T>(data);
  14.         Inventory inventory = server.createInventory(holder, 54);
  15.         holder.setInventory(inventory);
  16.         return inventory;
  17.     }
  18. }
复制代码


事件传递:
既然我们已经能传入自己的holder了,那么也可以不再使用之前的方法判断Inventory了,对象的多态允许我们使用有不同方法的holder
首先需要在CustomInventoryHolder里添加一个类型为Consumer<Event/*也可以是别的类型,方便就行*/>(用类也行)的event字段
然后在事件发生时调用这个字段(在这之前需要先判断holder)
  1. holder.getEvent().accept(event/*类型就是上面指定的类型*/);
复制代码

这样就可以实现不同功能的Inventory的自动加载了

自动加载两个Inventory的代码
创建Inventory:
  1. Inventory inv1 = InventoryTools.createDataInventory(player.getServer(), data, /*这个参数就是新加的event字段*/event -> event.getWhoClicked().sendMessage("Holder Test."));//打开代码这里就省略了
  2. Inventory inv2 = InventoryTools.createDataInventory(player.getServer(), data, event -> event.getWhoClicked().giveExp(10)/*给予玩家经验*/);
复制代码

事件:
  1. public void onInventoryClick(InventoryClickEvent event) {
  2.     if(event.isCancelled()) {
  3.         return;
  4.     }
  5.     CustomInventoryHolder holder = InventoryTools.getHolder(event.getClickedInventory());
  6.     if(holder != null) {
  7.         holder.getEvent().accept(event);
  8.     }
  9. }
复制代码
[groupid=1330]PluginsCDTribe[/groupid]