很长一段时间,对于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);
- }
- }