很长一段时间,对于Inventory的判断我一直是用title来进行的,如果涉及到数据传递部分,那就只能通过各种神奇的方法进行了(比如截取title或者物品堆标记)。
然后今天海螺大佬告诉我还能用holder判断,我就试了一下,是挺方便的,只要新建一个实现了InventoryHolder的类传入createInventory里的第一个参数就行了(就是一般填null的那个),要判断就用instanceof来判断。
之后我想既然都可以自己传入对象了,那直接传递数据是不是可以?
然后我写了这样一个类:
- public class CustomInventoryHolder<T> implements InventoryHolder {
 -     protected Inventory inventory;
 -     protected T data;
 -     
 -     public CustomInventoryHolder(T data/*, int size, String title*/) {
 -         this.data = data;
 -         //更方便创建Inventory与holder的方法
 -         //this.inventory = Bukkit.createInventory(this, size, title);
 -     }
 
-     //实现InventoryHolder里的方法,由于不能返回null所以这里设置为持有holder的Inventory
 -     @Override
 -     public Inventory getInventory() {
 -         return this.inventory;
 -     }
 
-     public T getData() {
 -         return this.data;
 -     }
 
-     public void setData(T data) {
 -         this.data = data;
 -     }
 
-     //Inventory在这里设置。构造函数不加Inventory参数是因为在对Inventory传入holder时Inventory还未创建
 -     public void setInventory(Inventory inventory) {
 -         this.inventory = inventory;
 -     }
 - }
 
这样就可以进行数据传递了,与之前的方法比较代码不知道简洁了多少(bug顺便也少了很多233)
为了方便还可以把获取holder用一个方法进行:
- public static CustomInventoryHolder<?> getHolder(Inventory inventory) {
 -     InventoryHolder holder = inventory.getHolder();
 -     if(!(holder instanceof CustomInventoryHolder)) {
 -             return null;
 -     }
 -     return (CustomInventoryHolder<?>) holder;
 - }
 
这样判断一个物品栏就既可以通过CustomInventoryHolder里的data的数据也可以通过不同的继承CustomInventoryHolder的类,当然,再创建一个方法也可以
测试代码(由于是测试所以类型判断并没有那么严格):
- //命令里
 - Map<String, Object> map = new HashMap<>();
 - map.put("name", args[1]);
 - map.put("sender", sender);
 - map.put("count", 0);
 - ((Player) sender).openInventory(InventoryTools.createDataInventory(sender.getServer(), map));
 
- //事件里
 - CustomInventoryHolder<?> holder = InventoryTools.getHolder(event.getInventory());
 - if(holder != null) {
 -     Map<String, Object> data = (Map<String, Object>) holder.getData();
 -     //测试map保存不同类型数据
 -     event.getWhoClicked().sendMessage((String) data.get("name"));
 -     event.getWhoClicked().sendMessage(((CommandSender) data.get("sender")).getName());
 -     //测试map数据读写
 -     event.getWhoClicked().sendMessage(data.get("count").toString());
 -     data.put("count", ((Integer) data.get("count") + 1));
 - }
 
InventoryTools类:
- public class InventoryTools {
 -     //也是可以直接写成判断类型再强转的
 -     public static CustomInventoryHolder<?> getHolder(Inventory inventory) {
 -         InventoryHolder holder = inventory.getHolder();
 -             if(!(holder instanceof CustomInventoryHolder)) {
 -                 return null;
 -             }
 -             return (CustomInventoryHolder<?>) holder;
 -         }
 
-     //简化创建过程
 -     //已经不需要使用本方法了,可以直接用holder的构造函数创建Inventory(注释部分),感谢@810587921 提供的方法
 -     public static <T> Inventory createDataInventory(Server server, T data) {
 -         CustomInventoryHolder<T> holder = new CustomInventoryHolder<T>(data);
 -         Inventory inventory = server.createInventory(holder, 54);
 -         holder.setInventory(inventory);
 -         return inventory;
 -     }
 - }
 
事件传递:
既然我们已经能传入自己的holder了,那么也可以不再使用之前的方法判断Inventory了,对象的多态允许我们使用有不同方法的holder
首先需要在CustomInventoryHolder里添加一个类型为Consumer<Event/*也可以是别的类型,方便就行*/>(用类也行)的event字段
然后在事件发生时调用这个字段(在这之前需要先判断holder)
- holder.getEvent().accept(event/*类型就是上面指定的类型*/);
 
这样就可以实现不同功能的Inventory的自动加载了
自动加载两个Inventory的代码
创建Inventory:
- Inventory inv1 = InventoryTools.createDataInventory(player.getServer(), data, /*这个参数就是新加的event字段*/event -> event.getWhoClicked().sendMessage("Holder Test."));//打开代码这里就省略了
 - Inventory inv2 = InventoryTools.createDataInventory(player.getServer(), data, event -> event.getWhoClicked().giveExp(10)/*给予玩家经验*/);
 
事件:
- public void onInventoryClick(InventoryClickEvent event) {
 -     if(event.isCancelled()) {
 -         return;
 -     }
 -     CustomInventoryHolder holder = InventoryTools.getHolder(event.getClickedInventory());
 -     if(holder != null) {
 -         holder.getEvent().accept(event);
 -     }
 - }
 
