# 自定义消息

以掷骰子为例

首先，先定义一个自定义消息的类型

```java
public interface CustomAttachmentType {
    // 多端统一
    int Guess = 1;
    int SnapChat = 2;
    int Sticker = 3;
    int RTS = 4;
    int Crops=5;//骰子
}
```

第二步，定义一个自定义消息附件的基类，负责解析你的自定义消息的公用字段，比如类型等 。

>  注意: 实现 MsgAttachment 接口的成员都要实现 Serializable。

```java
public abstract class CustomAttachment implements MsgAttachment {
    protected int type;
    CustomAttachment(int type) {
        this.type = type;
    }
    public void fromJson(JSONObject data) {
        if (data != null) {
            parseData(data);
        }
    }
    @Override
    public String toJson(boolean send) {
        return CustomAttachParser.packData(type, packData());
    }
    public int getType() {
        return type;
    }
    protected abstract void parseData(JSONObject data);
    protected abstract JSONObject packData();
}
```

第三步，继承这个基类，实现“骰子”的附件类型。

> 注意，成员变量都要实现 Serializable。

```java
public class CrapsAttachment extends CustomAttachment{
    public enum Craps {
        one(1, "1"),
        two(2, "2"),
        three(3, "3"),
        four(4,"4"),
        five(5,"5"),
        six(6,"6"),
        ;
        private int value;
        private String desc;
        Craps(int value, String desc) {
            this.value = value;
            this.desc = desc;
        }
        static Craps enumOfValue(int value) {
            for (Craps direction : values()){
                if (direction.getValue() == value) {
                    return direction;
                }
            }
            return one;
        }
        public int getValue() {
            return value;
        }
        public String getDesc() {
            return desc;
        }
    }
    private Craps value;
    public CrapsAttachment() {
        super(CustomAttachmentType.Guess);
        random();
    }
    @Override
    protected void parseData(JSONObject data) {
        value = Craps.enumOfValue(data.getIntValue("value"));
    }
    @Override
    protected JSONObject packData() {
        JSONObject data = new JSONObject();
        data.put("value", value.getValue());
        return data;
    }
    private void random() {
        int value = new Random().nextInt(6) + 1;
        this.value = Craps.enumOfValue(value);
    }
    public Craps getValue() {
        return value;
    }
}
```

第四步，实现自定义消息的附件解析器。

```java
public class CustomAttachParser implements MsgAttachmentParser {
    private static final String KEY_TYPE = "type";
    private static final String KEY_DATA = "data";
    @Override
    public MsgAttachment parse(String json) {
        CustomAttachment attachment = null;
        try {
            JSONObject object = JSON.parseObject(json);
            int type = object.getInteger(KEY_TYPE);
            JSONObject data = object.getJSONObject(KEY_DATA);
            switch (type) {
                case CustomAttachmentType.Crops:
                    attachment = new CrapsAttachment();
                default:
                    attachment = new DefaultCustomAttachment();
                    break;
            }
            if (attachment != null) {
                attachment.fromJson(data);
            }
        } catch (Exception e) {

        }
        return attachment;
    }
    public static String packData(int type, JSONObject data) {
        JSONObject object = new JSONObject();
        object.put(KEY_TYPE, type);
        if (data != null) {
            object.put(KEY_DATA, data);
        }
        return object.toJSONString();
    }
}
```

第五步，将自定义消息展示UI上（当然这里显示的文字，如果想要显示成图片，可以参考demo）

```java
public class MsgViewHolderCrops extends MsgViewHolderText{
    @Override
    protected String getDisplayText() {
        //实例化一个attachment
CrapsAttachment attachment = (CrapsAttachment)message.getAttachment();
        return attachment.getValue().getDesc() + "点！";
    }
}
```

第六步，发送自定义消息

```java
public class CrapsAction extends BaseAction{
    public CrapsAction(){
        super(R.drawable.message_plus_crops_selector, R.string.input_panel_crops);
    }
    @Override
    public void onClick() {
        CrapsAttachment attachment = new CrapsAttachment();
        IMMessage message = MessageBuilder.createCustomMessage(
                getAccount(), getSessionType(), attachment.getValue().getDesc(), attachment
        );
        sendMessage(message);
    }
}
```

第七步，将该附件解析器注册到 SDK 中。为了保证生成历史消息时能够正确解析自定义附件，注册一般应放在 Application 的 onCreate 中完成

```java
NIMClient.getService(MsgService.class).registerCustomAttachmentParser(new CustomAttachParser());
```

第八步，注册扩展消息类型的显示ViewHolder，由于这里使用我们UIKIT，所以也需要注册到Application的onCreate中

```java
NimUIKit.registerMsgItemViewHolder(CrapsAttachment.class, MsgViewHolderCrops.class);
```

第九步，添加“骰子”的按钮到“+”号中，Demo是在SessionHelper.java里面，定制的单聊界面。

```java
ArrayList<BaseAction> actions = new ArrayList<>();
actions.add(new CrapsAction());
```