解读(五):分析KeyboardFragment, 带文字和表情的评论发表面板

解读(五):分析KeyboardFragment, 带文字和表情的评论发表面板

其实就是这个常见的功能

这个功能涉及到很多类, 我一个一个分析

KeyboardFragment类

/**
 * 底部带emotion面板的文字和表情的评论功能的Fragment
 **/
public class KeyboardFragment extends BaseTabNavFragment {
    @Bind(R.id.et_input)
    EditText mInput; //输入框
    @Bind(R.id.emotion_layout)
    LinearLayout mEmoLayout; //表情的布局
    @Bind(R.id.iv_emotion)
    ImageView mIvEmotion; //发布评论的图片
    // 回复的对象
    private Comment mReplyCmm;
    //委托类对象,它管理着键盘, emotion按钮, 输入框, emotion面板之间的相互操作
    private KeyboardActionDelegation mDelegatioin;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //加载底部emotion回复布局
        return inflater.inflate(R.layout.fragment_keyboard, container, false);
    }
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        ButterKnife.bind(this, view);
        //这个委托类对象很重要
        mDelegatioin = new KeyboardActionDelegation(mContext, mInput, mIvEmotion, mEmoLayout);
        //初始化订阅者
        initSubscribers();
        //设置ViewPager第一页选中
        mViewPager.setCurrentItem(0);
    }
    /**
     * 注册事件订阅者
     */
    private void initSubscribers() {
        // register a listener to receive a event that mean user selected a emotion
        // 注册一个监听器接收用户选择了一个emotion表情的事件
        RxBus.with(this)
                .setEvent(Events.EventEnum.DELIVER_SELECT_EMOTION) //设置过滤接收选择一个emotion表情事件
                .setEndEvent(FragmentEvent.DESTROY) //直到Fragment destroy为止
                .onNext((events -> {
                    EmotionRules emotion = events.<EmotionRules>getMessage();
                    mDelegatioin.onEmotionItemSelected(emotion); //设置所选的emotion
                })).create();
        // 接受返回事件,如果显示表情面板,隐藏!如果显示软键盘,隐藏!如果显示回复某某某,隐藏!
        RxBus.with(this)
                .setEvent(Events.EventEnum.DELIVER_GO_BACK) //接收返回事件
                .setEndEvent(FragmentEvent.DESTROY) //直到Fragment destroy为止
                .onNext((events -> {
                    if (mReplyCmm != null) { //如果回复评论不为空,重置回复评论对象
                        mInput.setHint(getResources().getString(R.string.please_say_something));
                        mReplyCmm = null;
                        return;
                    }
                    if (!mDelegatioin.onTurnBack())//如果点击了返回按钮
                        return;
                    RxBus.getInstance().send(Events.EventEnum.WE_HIDE_ALL, null); //发送一个隐藏全部的事件
                })).create();
        RxBus.with(this)
                .setEvent(Events.EventEnum.DELIVER_REPLY_SOMEONE) //接收一个回复某人的事件
                .setEndEvent(FragmentEvent.DESTROY) //直到Fragment destroy为止
                .onNext((events -> {
                    mReplyCmm = events.getMessage(); //获得评论对象
                    mInput.setHint("回复 @" + mReplyCmm.getAuthor()); //设置hint为@某人
                })).create();
        RxBus.with(this)
                .setEvent(Events.EventEnum.DELIVER_CLEAR_IMPUT) //接收一个清空输入框事件
                .setEndEvent(FragmentEvent.DESTROY)//直到Fragment destroy为止
                .onNext((events -> {
                    mInput.setHint(getResources().getString(R.string.please_say_something)); //重置输入框
                    mInput.setText(null);
                })).create();
    }
    /**
     * 设置Tab item的View
     */
    @Override
    public View setupTabItemView(String title) {
        //设置emoji笑脸图标的布局属性
        ImageView view = new ImageView(mContext);
        view.setImageResource(R.mipmap.icon_emotion_color);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                0, ViewGroup.LayoutParams.MATCH_PARENT
        );
        params.weight = 1;
        view.setLayoutParams(params);
        return view;
    }
    /**
     * 设置tab,调用的是本类中的addTab()方法
     */
    @Override
    public void onSetupTabs() {
        addTab(getResources().getString(R.string.emotion_qq), EmotionPanelFragment.class);
    }
    /**
     * 总在前面添加元素,跟默认的基类做法相悖,所以我们复写它
     */
    @Override
    public void addTab(String title, Class<? extends Fragment> fragment) {
        mTabs.add(0, new ViewPageInfo(title, Fragment.instantiate(getActivity(), fragment.getName())));
        View view = setupTabItemView(title); //创建Tab item的View
        mNavLayout.addView(view, 0); //每次都添加到前面
        mTabs.get(0).view = view;
    }
    /**
     * 删除表情或字符
     */
    @OnClick(R.id.iv_backspace)
    public void removeEmotion() {
        InputHelper.backspace(mInput); //退格
    }
    /**
     * 发送信息
     */
    @OnClick(R.id.iv_send)
    public void sendComment() {
        // 如果没有输入评论
        if (Utilities.isEmpty(mInput.getText().toString())) {
            Toast.makeText(mContext, "别闹,写点东西再发╭∩╮(︶︿︶)╭∩╮", Toast.LENGTH_SHORT).show();
            return;
        }
        // 判断是否登录
        if (AppManager.LOCAL_LOGINED_USER == null) {
            UIManager.jump2login(mContext); //调到登录
            return;
        }
        // 封装实体, 发送消息给相应的presenter
        Comment comment = new Comment();
        if (mReplyCmm == null) {
            comment.setId(-1L);
        } else {
            comment.setId(mReplyCmm.getId());
            comment.setAuthorId(mReplyCmm.getAuthorId());
        }
        comment.setContent(mInput.getText().toString());
        //创建评论的事件
        Events<Comment> events = Events.just(comment);
        events.what = Events.EventEnum.DELIVER_SEND_COMMENT; //发送评论
        RxBus.getInstance().send(events); //发送评论的事件
    }
}
  • 这个类主要是创建了底部评论功能的布局(依靠强大的父类BaseTabNavFragment,只要重写setupItemView(),onSetupTabs()和addTab()即可), 使用RxBus注册了一系列的评论相关是事件接受者, 点击评论按钮时将评论内容封装成对象使用RxBus发送出去.
  • 这个类精彩的地方是一连串的RxBus的使用. 从这里看出来RxJava真心的强大和好用.
  • 代码中有一个委托类对象, 这个对象很重要, 管理了软键盘, emotion按钮, 输入框, emotion面板的关联操作
//这个委托类对象很重要
mDelegatioin = new KeyboardActionDelegation(mContext, mInput, mIvEmotion, mEmoLayout);

KeyboardActionDelegation类

/**
 * 键盘, emotion按钮, 输入框, emotion面板之间的相互关系委派给这个类管理
 */
public class KeyboardActionDelegation {
    private ImageView mBtnEmotion; //Emotion按钮
    private EditText mInput; //输入框
    private Context mContext;
    private ViewGroup mEmotionPanel; //Emotion面板
    private boolean isShowSoftInput; //是否显示输入法
    public KeyboardActionDelegation(Context context, EditText input, ImageView button, ViewGroup view) {
        this.mBtnEmotion = button;
        this.mInput = input;
        this.mContext = context;
        this.mEmotionPanel = view;
        init();
    }
    /**
     * 初始化, 绑定事件
     */
    private void init() {
        //输入框焦点改变的监听
        mInput.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    hideEmotionPanel(); //隐藏Emotion面板
                } else {
                    hideSoftKeyboard(); //隐藏键盘
                }
            }
        });
        //输入框点击事件的监听
        mInput.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isEmotionPanelShowing())//如果Emotion面板没有显示则直接返回
                    return;
                hideEmotionPanel();//隐藏Emotion面板
            }
        });
        //Emotion表情按钮被点击的监听
        mBtnEmotion.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isEmotionPanelShowing()) {//如果Emotion面板显示
                    hideEmotionPanel(); //隐藏Emotionm面板
                } else {
                    showEmotionPanel();//显示Emotion面板
                }
            }
        });
    }
    /**
     * 显示Emotion面板
     */
    public void showEmotionPanel() {
        mBtnEmotion.setSelected(true);//设置Emotion按钮被选中, 改变了Emotion按钮的显示表情
        hideSoftKeyboard();//隐藏输入法
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mEmotionPanel.setVisibility(View.VISIBLE); //设置可见
            }
        }, 300);
    }
    /**
     * 判断Emotion面板是否显示
     */
    private boolean isEmotionPanelShowing() {
        return mEmotionPanel.getVisibility() == View.VISIBLE;
    }
    /**
     * 隐藏软键盘
     */
    private void hideSoftKeyboard() {
        DeviceManager.getSoftInputManager(mContext).hideSoftInputFromWindow(mInput.getWindowToken(), 0);
        isShowSoftInput = false;
    }
    /**
     * 隐藏表情面板
     */
    private void hideEmotionPanel() {
        mEmotionPanel.setVisibility(View.GONE);//设置不占位
        mBtnEmotion.setSelected(false);
    }
    /**
     * 是否显示输入法
     */
    public boolean isShowSoftInput() {
        return isShowSoftInput;
    }
    /**
     * Emotion面板中item被选中
     */
    public void onEmotionItemSelected(EmotionRules emotion) {
        if (mInput == null || emotion == null) {
            return;
        }
        int start = mInput.getSelectionStart();//获取选中文本的起始位置
        int end = mInput.getSelectionEnd(); //获取选中文本的结束位置
        if (start == end) { //如果未选中文本
            mInput.append(InputHelper.insertEtn(mContext, emotion)); //将Emotion表情直接追加到光标后
        } else { //如果有选中文本
            Spannable str = InputHelper.insertEtn(mContext, emotion);//获得需要插入的内容
            mInput.getText().replace(Math.min(start, end), Math.max(start, end), str, 0, str.length());//用Emotion表情替换掉选中的文本
        }
    }
    /**
     * 当使用回退键时, 让Emotion面板和输入法都隐藏
     */
    public boolean onTurnBack() {
        if (isEmotionPanelShowing()) {
            hideEmotionPanel();
            return false;
        }
        if (isShowSoftInput()) {
            hideEmotionPanel();
            return false;
        }
        return true;
    }
}
  • 这个类其实就是管理了不同的状态变化, 判断是否要显示隐藏输入法, Emotion面板等.
  • 这里面比较重要的一个方法是onEmotionItemSelected(), 也就是Emotion面板中表情被选中后的操作.
/**
 * Emotion面板中item被选中
 */
public void onEmotionItemSelected(EmotionRules emotion) {
    if (mInput == null || emotion == null) {
        return;
    }
    int start = mInput.getSelectionStart();//获取选中文本的起始位置
    int end = mInput.getSelectionEnd(); //获取选中文本的结束位置
    if (start == end) { //如果未选中文本
        mInput.append(InputHelper.insertEtn(mContext, emotion)); //将Emotion表情直接追加到光标后
    } else { //如果有选中文本
        Spannable str = InputHelper.insertEtn(mContext, emotion);//获得需要插入的内容
        mInput.getText().replace(Math.min(start, end), Math.max(start, end), str, 0, str.length());//用Emotion表情替换掉选中的文本
    }
}
  • 如果编辑框中有选中的文本,则用表情直接替换文本, 如果没有, 则直接添加到光标后面. 这个方法在上面的KeyboardFragment中被调用.
// 注册一个监听器接收用户选择了一个emotion表情的事件
RxBus.with(this)
    .setEvent(Events.EventEnum.DELIVER_SELECT_EMOTION) //设置过滤接收选择一个emotion表情事件
    .setEndEvent(FragmentEvent.DESTROY) //直到Fragment destroy为止
    .onNext((events -> {
        EmotionRules emotion = events.<EmotionRules>getMessage();
        mDelegatioin.onEmotionItemSelected(emotion); //设置所选的emotion
    })).create();
  • KeyboardActionDelegation#onEmotionItemSelected()中使用了InputHelper#insertEtn()实现了在文字中插入Emotion表情. 这里涉及到了图文混排.

InputHelper类

/**
 * 输入帮助类
 */
public class InputHelper {
    /**
     * 退格
     */
    public static void backspace(EditText input) {
        if (input == null) {
            return;
        }
        KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL);
        input.dispatchKeyEvent(event);
    }
    /**
     * 插入表情到文字中, 这里使用到了SpannableString和ImageSpan实现图文混排
     */
    @SuppressWarnings("all")
    public static Spannable insertEtn(Context context, EmotionRules emotion) {
        String remote = emotion.getRemote(); //获得表情的序号
        Spannable spannable = new SpannableString(remote); //创建文本样式
        Drawable d = context.getResources().getDrawable(emotion.getMResId()); //获得emotion的drawable
        d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); //设置边界为drawable的真实宽高
        ImageSpan iSpan = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);//获得图片样式
        //Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 前后输入的字符都不应用这种Spannable
        spannable.setSpan(iSpan, 0, remote.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        return spannable;
    }
    public static void encode(TextView view, String content) {
        view.setText("");
        StringBuilder mNormalBuilder = new StringBuilder();
        StringBuilder mEtnBuilder = new StringBuilder();
        boolean isCut = false; //是否截断
        for (int i = 0; i < content.length(); i++) {
            char unit = content.charAt(i);
            // 截断,保存在etnBuilder容器
            if (isCut) {
                // 截断期间发现新的[,将之前的缓存存入view,刷新容器
                if (unit == ‘[‘) {
                    mNormalBuilder.append(mEtnBuilder.toString());
                    mEtnBuilder.delete(0, mEtnBuilder.length());
                    mEtnBuilder.append(unit);
                    continue;
                }
                if (unit == ‘]‘) {
                    mEtnBuilder.append(unit);
                    EmotionRules rule = EmotionRules.containOf(mEtnBuilder.toString());
                    view.append(mNormalBuilder.toString());
                    if (rule != null) {
                        view.append(insertEtn(view.getContext(), rule));
                    } else {
                        view.append(mEtnBuilder.toString());
                    }
                    mNormalBuilder.delete(0, mNormalBuilder.length());
                    mEtnBuilder.delete(0, mEtnBuilder.length());
                    isCut = false;
                    continue;
                }
                mEtnBuilder.append(unit);
            } else { // --> 非截断
                if (unit == ‘[‘) {
                    mEtnBuilder.append(unit);
                    isCut = true;
                    continue;
                }
                mNormalBuilder.append(unit);
            }
        }
        view.append(mNormalBuilder.toString()); //追加到TextView的显示缓冲区
        view.append(mEtnBuilder.toString());
    }
}
  • //TODO: encode()还没看的太懂, 留个TODO占坑吧.
  • 这个类最重要的方法是InputHelper#insertEtn(). 实现了图文混排效果.
 /**
 * 插入表情到文字中, 这里使用到了SpannableString和ImageSpan实现图文混排
 */
@SuppressWarnings("all")
public static Spannable insertEtn(Context context, EmotionRules emotion) {
    String remote = emotion.getRemote(); //获得表情的序号
    Spannable spannable = new SpannableString(remote); //创建文本样式
    Drawable d = context.getResources().getDrawable(emotion.getMResId()); //获得emotion的drawable
    d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); //设置边界为drawable的真实宽高
    ImageSpan iSpan = new ImageSpan(d, ImageSpan.ALIGN_BASELINE);//获得图片样式
    //Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 前后输入的字符都不应用这种Spannable
    spannable.setSpan(iSpan, 0, remote.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    return spannable;
}
  • 这里讲解下setSpan()方法. ImageSpan 可以使用图片替换文字达到图文混排的效果,例如在一般聊天工具当中在文字和表情一起发的状态。
public void setSpan(Object what, int start, int end, int flags);
  • what传入各种Span类型的实例,start和end标记要替代的文字内容的范围,flags是用来标识在 Span 范围内的文本前后输入新的字符时是否把它们也应用这个效果,可以传入Spanned.SPAN_EXCLUSIVE_EXCLUSIVE、Spanned.SPAN_INCLUSIVE_EXCLUSIVE、Spanned.SPAN_EXCLUSIVE_INCLUSIVE、Spanned.SPAN_INCLUSIVE_INCLUSIVE几个参数,INCLUSIVE表示应用该效果,EXCLUSIVE表示不应用该效果,如Spanned.SPAN_INCLUSIVE_EXCLUSIVE表示对前面的文字应用该效果,而对后面的文字不应用该效果。

下面这个也可以算是一个模板代码了.

Drawabledrawable=mContext.getResources().getDrawable(R.drawable.new_topic_drawable);
drawable.setBounds(0,0,drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight());
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE);
spanString.setSpan(imageSpan,spanString.length()-1,spanString.length(),Spannable.SPAN_INCLUSIVE_INCLUSIVE);

EmotionPanelFragment类

这个类从名字上就可以看出是Emotion面板的Fragment.

/**
 * Emotion面板的Fragment
 **/
public class EmotionPanelFragment extends BaseTabNavFragment {
    public static final int COLUMN = 7; //7列
    public static final int ROW = 3; //3行
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        setCurrentItem(0); //设置当前选中的item
    }
    /**
     * 设置Emotion面板中的每个View, 也就是每个Emotion表情
     */
    @Override
    public View setupTabItemView(String title) {
        //每个Emotion表情就是一个带圆形背景的TextView
        TextView view = new TextView(mContext);
        int m4 = UIHelper.dip2px(mContext, 4);
        LinearLayout.LayoutParams layout = new LinearLayout.LayoutParams(m4, m4);
        int m5 = UIHelper.dip2px(mContext, 5);
        layout.setMargins(m5, m5, m5, m5); //外边距
        view.setLayoutParams(layout);
        view.setBackgroundResource(R.drawable.selector_dot_nav); //设置圆形背景
        return view;
    }
    /**
     * 填充tab中的表情, 这里使用了分页填充
     */
    @Override
    public void onSetupTabs() {
        EmotionRules[] rules = EmotionRules.values(); //获得EmotionRules预定义的枚举项
        //一页显示几条
        int page_size = COLUMN * ROW;
        //总共多少页
        int page_count = (rules.length + page_size - 1) / page_size;
        int i;
        //分页
        for (i = 0; i < page_count - 1; i++) { //如果是前page-1页,直接添加满即可
            addTab("", initView(i * page_size, (i + 1) * page_size - 1));
        }
        //最后一页添加到表情结束
        addTab("", initView(i * page_size, rules.length - 1));
    }
    /**
     * 用持有GridView的ViewFragment,填充到tab中去
     */
    public void addTab(String title, View view) {
        addTab(title, new ViewFragment(view));
    }
    /**
     * 初始化Emotion表情展示的GridView
     */
    private View initView(int start, int end) {
        final EmotionRules[] rules = Arrays.copyOfRange(EmotionRules.values(), start, end + 1);//得到一份EmotionRules的拷贝
        GridView view = new GridView(mContext); //创建一个GridView展示Emotion表情
        GridView.LayoutParams params = new GridView.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
        );
        view.setLayoutParams(params);
        view.setNumColumns(COLUMN); //设置列数
        view.setAdapter(new EmotionAdapter(rules)); //设置适配器,实现在下面
        view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Events<EmotionRules> events = Events.just(rules[position]); //封装事件实体,发送Emotion表情被选中的事件
                events.what = Events.EventEnum.DELIVER_SELECT_EMOTION;
                RxBus.getInstance().send(events); //又见RxBus,真是神器啊
            }
        });
        return view;
    }
    @Override
    public FragmentManager getGenuineFragmentManager() {
        return getChildFragmentManager();
    }
    /**
     * 一个典型的适配器
     */
    public static class EmotionAdapter extends BaseAdapter {
        private EmotionRules[] rules;
        public EmotionAdapter(EmotionRules[] rules) {
            this.rules = rules;
        }
        @Override
        public int getCount() {
            return rules.length;
        }
        @Override
        public Object getItem(int position) {
            return rules[position];
        }
        @Override
        public long getItemId(int position) {
            return 0;
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHandler handler;
            if (convertView == null) {
                convertView = new ImageView(parent.getContext());
                int m3 = UIHelper.dip2px(parent.getContext(), 3f);
                convertView.setPadding(m3, m3, m3, m3);
                handler = new ViewHandler();
                handler.iView = (ImageView) convertView;
                convertView.setTag(handler);
            } else {
                handler = (ViewHandler) convertView.getTag();
            }
            handler.iView.setImageResource(rules[position].getMResId()); //将Emotion表情设置到ImageView
            return convertView;
        }
        class ViewHandler {
            public ImageView iView;
        }
    }
}
  • 这段代码比较简单, 就是将Emotion表情填充到面板中,每个页面都是一个ViewFragment.

ViewFragment类

/**
 * 一个简单的Fragment,专门显示一个View,特别服务于轮番、ViewPager
 */
@SuppressLint("ValidFragment")
public class ViewFragment extends BaseFragment {
    private View view;
    public ViewFragment(View view) {
        this.view = view;
    }
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return view;
    }
}
  • ViewFragment内部持有了一个View对象. 可以简单的理解为将View转化为一个Fragment.
  • 在Emotion面板中, Emotion表情是使用一个EmotionRules管理的. EmotionRules枚举类制定了Emotion表情的规则. 这个命名真不错.

EmotionRules类

/**
 * 定义了emotion规则: 类型,图表,文字含义,序号
 */
public enum EmotionRules {
    EMOTION0(0, R.mipmap.smiley_0, "[微笑]", "[0]"),
    EMOTION1(0, R.mipmap.smiley_1, "[撇嘴]", "[1]"),
    EMOTION2(0, R.mipmap.smiley_2, "[色]", "[2]"),
    EMOTION3(0, R.mipmap.smiley_3, "[发呆]", "[3]"),
    EMOTION4(0, R.mipmap.smiley_4, "[得意]", "[4]"),
    EMOTION5(0, R.mipmap.smiley_5, "[流泪]", "[5]"),
    EMOTION6(0, R.mipmap.smiley_6, "[害羞]", "[6]"),
    EMOTION7(0, R.mipmap.smiley_7, "[闭嘴]", "[7]"),
    EMOTION8(0, R.mipmap.smiley_8, "[睡]", "[8]"),
    EMOTION9(0, R.mipmap.smiley_9, "[大哭]", "[9]"),
    EMOTION10(0, R.mipmap.smiley_10, "[尴尬]", "[10]"),
    EMOTION11(0, R.mipmap.smiley_11, "[发怒]", "[11]"),
    EMOTION12(0, R.mipmap.smiley_12, "[调皮]", "[12]"),
    EMOTION13(0, R.mipmap.smiley_13, "[呲牙]", "[13]"),
    EMOTION14(0, R.mipmap.smiley_14, "[惊讶]", "[14]"),
    EMOTION15(0, R.mipmap.smiley_15, "[难过]", "[15]"),
    EMOTION16(0, R.mipmap.smiley_16, "[酷]", "[16]"),
    EMOTION17(0, R.mipmap.smiley_17, "[冷汗]", "[17]"),
    EMOTION18(0, R.mipmap.smiley_18, "[抓狂]", "[18]"),
    EMOTION19(0, R.mipmap.smiley_19, "[吐]", "[19]"),
    EMOTION20(0, R.mipmap.smiley_20, "[偷笑]", "[20]"),
    EMOTION21(0, R.mipmap.smiley_21, "[可爱]", "[21]"),
    EMOTION22(0, R.mipmap.smiley_22, "[白眼]", "[22]"),
    EMOTION23(0, R.mipmap.smiley_23, "[傲慢]", "[23]"),
    EMOTION24(0, R.mipmap.smiley_24, "[饥饿]", "[24]"),
    EMOTION25(0, R.mipmap.smiley_25, "[困]", "[25]"),
    EMOTION26(0, R.mipmap.smiley_26, "[惊恐]", "[26]"),
    EMOTION27(0, R.mipmap.smiley_27, "[流汗]", "[27]"),
    EMOTION28(0, R.mipmap.smiley_28, "[憨笑]", "[28]"),
    EMOTION29(0, R.mipmap.smiley_29, "[大兵]", "[29]"),
    EMOTION30(0, R.mipmap.smiley_30, "[奋斗]", "[30]"),
    EMOTION31(0, R.mipmap.smiley_31, "[咒骂]", "[31]"),
    EMOTION32(0, R.mipmap.smiley_32, "[疑问]", "[32]"),
    EMOTION33(0, R.mipmap.smiley_33, "[嘘]", "[33]"),
    EMOTION34(0, R.mipmap.smiley_34, "[晕]", "[34]"),
    EMOTION35(0, R.mipmap.smiley_35, "[折磨]", "[35]"),
    EMOTION36(0, R.mipmap.smiley_36, "[衰]", "[36]"),
    EMOTION37(0, R.mipmap.smiley_37, "[骷髅]", "[37]"),
    EMOTION38(0, R.mipmap.smiley_38, "[敲打]", "[38]"),
    EMOTION39(0, R.mipmap.smiley_39, "[再见]", "[39]"),
    EMOTION40(0, R.mipmap.smiley_40, "[擦汗]", "[40]"),
    EMOTION41(0, R.mipmap.smiley_41, "[抠鼻]", "[41]"),
    EMOTION42(0, R.mipmap.smiley_42, "[鼓掌]", "[42]"),
    EMOTION43(0, R.mipmap.smiley_43, "[糗大了]", "[43]"),
    EMOTION44(0, R.mipmap.smiley_44, "[坏笑]", "[44]"),
    EMOTION45(0, R.mipmap.smiley_45, "[左哼哼]", "[45]"),
    EMOTION46(0, R.mipmap.smiley_46, "[右哼哼]", "[46]"),
    EMOTION47(0, R.mipmap.smiley_47, "[哈欠]", "[47]"),
    EMOTION48(0, R.mipmap.smiley_48, "[鄙视]", "[48]"),
    EMOTION49(0, R.mipmap.smiley_49, "[委屈]", "[49]"),
    EMOTION50(0, R.mipmap.smiley_50, "[快哭了]", "[50]"),
    EMOTION51(0, R.mipmap.smiley_51, "[阴险]", "[51]"),
    EMOTION52(0, R.mipmap.smiley_52, "[亲亲]", "[52]"),
    EMOTION53(0, R.mipmap.smiley_53, "[吓]", "[53]"),
    EMOTION54(0, R.mipmap.smiley_54, "[可怜]", "[54]");
    private int type;
    private int mResId;
    private String name;
    private String remote;
    EmotionRules(int type, int mResId, String name, String remote) {
        this.type = type;
        this.mResId = mResId;
        this.name = name;
        this.remote = remote;
    }
    public int getType() {
        return type;
    }
    public void setType(int type) {
        this.type = type;
    }
    public int getMResId() {
        return mResId;
    }
    public void setMResId(int mResId) {
        this.mResId = mResId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getRemote() {
        return remote;
    }
    public void setRemote(String remote) {
        this.remote = remote;
    }
    /**
     * 判断emotion的中文含义和序号中是否包含指定的字符串
     */
    public static EmotionRules containOf(String s) {
        EmotionRules[] rules = EmotionRules.values();
        for (EmotionRules item : rules) {
            if (item.getName().equals(s) || item.getRemote().equals(s))
                return item;
        }
        return null;
    }
}
  • 这个类中, 将图片表情和文字都一一对应起来. 至于里面的remote字段是什么意思, 我也只能猜测是序号相关的东西了.

    至此, 这个可以发送Emotion表情的评论面板功能已经分析完毕. to be continue …

时间: 2024-10-27 09:28:26

解读(五):分析KeyboardFragment, 带文字和表情的评论发表面板的相关文章

【Android】Android实现自定义带文字和图片的Button

在Android开发中经常会需要用到带文字和图片的button,下面来讲解一下常用的实现办法. 一.用系统自带的Button实现 最简单的一种办法就是利用系统自带的Button来实现,这种方式代码量最小.在Button的属性中有一个是drawableLeft,这个属性可以把图片设置在文字的左边,但是这种方式必须让icon的背景色是透明的,如果icon的背景色不是透明的话,会导致点击按钮时icon部分的背景色不会发生变化. 主要代码: <Button android:id="@+id/bt3

Android实现自定义带文字和图片的Button

在Android开发中经常会需要用到带文字和图片的button,下面来讲解一下常用的实现办法. 一.用系统自带的Button实现 最简单的一种办法就是利用系统自带的Button来实现,这种方式代码量最小.在Button的属性中有一个是drawableLeft,这个 属性可以把图片设置在文字的左边,但是这种方式必须让icon的背景色是透明的,如果icon的背景色不是透明的话,会导致点击按钮时icon部分的背景 色不会发生变化. 主要代码: <Button android:id="@+id/b

自定义实现带文字标题的瀑布流效果

在网上能找到的大部分资料中的瀑布流效果都是单纯的照片实现,现在我来实现一个带文字标题的.效果如下: 每个item都是由图片和文字标题两部分组成. 布局方式为ScrollView里面嵌套一个水平方向的LinearLayout,里面再嵌套两个竖直方向的LinearLayout,然后判断竖直方向的两个LinearLayout的高度,向比较低的那个Linearlayout里面添加item. 下面是代码 <ScrollView xmlns:android="http://schemas.androi

C#编程总结(五)多线程带给我们的一些思考

C#编程总结(五)多线程带给我们的一些思考 如有不妥之处,欢迎批评指正. 1.什么时候使用多线程? 这个问题,对于系统架构师.设计者.程序员,都是首先要面对的一个问题. 在什么时候使用多线程技术? 在许多常见的情况下,可以使用多线程处理来显著提高应用程序的响应能力和可用性. 上一章,我们讲了几个多线程的应用案例,主要的应用场景也做了介绍.这里不再赘述. http://www.cnblogs.com/yank/p/3232955.html 2.如何才能保证线程安全? 使用多线程,这是一个必须要弄清

iOS开发类似于呱呱卡效果,手指划过的区域形成画笔。适用于取出部分图片(截图),如截取出图片中带文字的区域部分。

HKBrushShots Demo下载地址: 类似于呱呱卡效果,手指划过的路线可以刮出痕迹. ??功能: 用于取出部分图片(截图),如截取出图片中带文字的区域部分. ??效果: 截取出图片中的“你就是我的全世界”文字区域的图片: 截取出图片中那只呆萌的小犀牛??: 更多截图效果: ??使用: 1 - 首先将工程中的“HKScreenShot”文件夹拷贝至项目中. 2 - 在需要使用的类中引入头文件: #import "HKCropView.h" #import "HKLine

TP框架中生成带背景带文字的二维码

首先下载一个phpqrcode的包放到/vendor目录下 链接:https://pan.baidu.com/s/18jV9DypYB_PHDhD6C0iedQ 提取码:qxuo 如果只是单纯生成二维码那么下面代码即可: vendor('phpqrcode.phpqrcode');//引入 $url='你要生成的东西:文字.数字.链接等'; $errorCorrectionLevel = "Q"; // 容错级别:L.M.Q.H $matrixPointSize = "3.8

微信小程序组件解读和分析:十五、switch 开关选择器

switch 开关选择器组件说明: switch,开关选择器.只能选择或者不选.这种属于表单控件或者查询条件控件. switch 开关选择器示例代码运行效果如下: 下面是WXML代码: [XML] 纯文本查看 复制代码 ? 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 <view class="secti

微信小程序组件解读和分析:五、text文本

text文本组件说明: text 文本就是微信小程序中显示出来的文本. text文本组件的示例代码运行效果如下: 下面是WXML代码: [XML] 纯文本查看 复制代码 ? 1 2 3 4 <view > <text>我是文本组件</text> </view> <text>{{text}}</text> 下面是JS代码: [JavaScript] 纯文本查看 复制代码 ? 01 02 03 04 05 06 07 08 09 10 1

Android视图状态及重绘流程分析,带你一步步深入了解View(三)

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17045157 在 前面一篇文章中,我带着大家一起从源码的层面上分析了视图的绘制流程,了解了视图绘制流程中onMeasure.onLayout.onDraw这三个最 重要步骤的工作原理,那么今天我们将继续对View进行深入探究,学习一下视图状态以及重绘方面的知识.如果你还没有看过我前面一篇文章,可以先去阅读 Android视图绘制流程完全解析,带你一步步深入了解View(二) .