表情面板

2015/09/11   
分类:  blog    android   

上一篇讲到了表情符号在EditText中的显示:Go

=======

这回来讲讲这个面板中的一些功能

1、面板的显示、继承于ViewPager,先上比较完整的代码

public class EmojiPanel extends ViewPager{
    private final static int EMOJI_INDEX_START = 1;
    private final static int EMOJI_INDEX_END = 93;

    private Context mContext;

    private EditText mEditText;

    public EmojiPanel(Context context) {
        this(context, null);
    }

    public EmojiPanel(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init();
    }

    public void bindEditText(EditText editText){
        mEditText = editText;
    }

    private void init(){
        List<View> views = new ArrayList<View>();
        for(int i = EMOJI_INDEX_START; i <= EMOJI_INDEX_END;){
            views.add( getGridView(i) );
            i += LocalEmojiAdapter.MAX_ITEM_COUNT;
        }
        this.setAdapter(new EmoViewPagerAdapter(views));
    }

    private View getGridView(int startIndex) {
        View view = View.inflate(mContext, R.layout.include_emo_gridview, null);
        GridView gridview = (GridView) view.findViewById(R.id.gridview);

        final LocalEmojiAdapter gridAdapter = new LocalEmojiAdapter(mContext, startIndex);

        gridview.setAdapter(gridAdapter);
        gridview.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
                Datas name = (Datas) gridAdapter.getItem(position);
                String key = name.code.toString();

                if (mEditText != null && !TextUtils.isEmpty(key)) {
                    int start = mEditText.getSelectionStart();
                    CharSequence content = mEditText.getText().insert(start, key);
                    mEditText.setText(content);
                    // 定位光标位置
                    CharSequence info = mEditText.getText();
                    if (info instanceof Spannable) {
                        Spannable spanText = (Spannable) info;
                        Selection.setSelection(spanText, start + key.length());
                    }
                }
            }
        });
        return view;
    }

    private class LocalEmojiAdapter extends SimpleAdapter {

        public final static int MAX_ITEM_COUNT = 17;

        private int mStartIndex;

        public LocalEmojiAdapter(Context context, int startIndex) {
            super(context, null, 0, null, null);
            mStartIndex = startIndex;
        }

        @Override
        public int getCount() {
            return MAX_ITEM_COUNT + 1;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if(convertView != null){
                holder =(ViewHolder) convertView.getTag();
            }else{
                convertView = View.inflate(mContext, R.layout.iv_emo, null);
                holder = new ViewHolder();
                holder.mIvImage = (ImageView) convertView.findViewById(R.id.iv_emo);

                convertView.setTag(holder);
            }
            if(position < MAX_ITEM_COUNT){
                String key = String.format("face_%03d", mStartIndex + position);
                int id = getResources().getIdentifier(key, "drawable", mContext.getPackageName());
                holder.mIvImage.setImageResource(id);
            }else{//delete button
                holder.mIvImage.setImageResource(R.drawable.icon_reply_delete);
            }
            convertView.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    if (mEditText != null) {
                        if(position < MAX_ITEM_COUNT){
                            String key = "{:" + (mStartIndex + position) + ":}";
                            int start = mEditText.getSelectionStart();
                            CharSequence content = mEditText.getText().insert(start, key);
                            mEditText.setText(content);
                            // 定位光标位置
                            CharSequence info = mEditText.getText();
                            if (info instanceof Spannable) {
                                Spannable spanText = (Spannable) info;
                                Selection.setSelection(spanText, start + key.length());
                            }
                        }else{//delete
//                          int start = mEditText.getSelectionStart();
//                          Editable content = mEditText.getText();
                            //check if the last item is emoji
//                          Matcher matcher = Pattern.compile("\\{:\\d{1,2}:\\}$", Pattern.CASE_INSENSITIVE).matcher(content);
//                          if(matcher.find()){
//                              String faceText = matcher.group();
//                              int index = content.toString().lastIndexOf(faceText);
//                              mEditText.setText( content.delete(index, index+faceText.length()) );
//                          }else{
//                          }
//                          mEditText.setText( content.delete(content.length()-1, content.length()) );
//                          CharSequence info = mEditText.getText();
//                          if (info instanceof Spannable) {
//                              Spannable spanText = (Spannable) info;
//                              
//                              if(start > spanText.length()){
//                                  start = spanText.length();
//                              }
//                              Selection.setSelection(spanText, start);
//                          }
                            mEditText.onKeyDown(KeyEvent.KEYCODE_DEL, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
                        }
                    }
                }
            });
            return convertView;
        }

        class ViewHolder {
            ImageView mIvImage;
        }
    }

}
一个表情界面:include_emo_gridview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <GridView
        android:id="@+id/gridview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:layout_marginBottom="6dp"
        android:layout_marginTop="6dp"
        android:fadingEdge="none"
        android:gravity="center"
        android:numColumns="6"
        android:scrollbars="none"
        android:horizontalSpacing="5dp"
        android:verticalSpacing="5dp" />

</LinearLayout>
一个表情:iv_emo.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
     android:padding="3dp">
     <ImageView
        android:id="@+id/iv_emo"
        android:layout_centerInParent="true"
        android:layout_width="25dp"
        android:layout_height="25dp" 
        android:scaleType="fitXY"
        />

</RelativeLayout>

代码贴完了,稍微说一些点

2、图片嵌入、删除

1)绑定了EditText (这个其实是上一篇自定义的)

public void bindEditText(EditText editText){
        mEditText = editText;
    }

2)嵌入表情

if (mEditText != null && !TextUtils.isEmpty(key)) {
        //获取光标的位置
    int start = mEditText.getSelectionStart();
    //将表情对应的文本("{:n:}")插入到text中
    CharSequence content = mEditText.getText().insert(start, key);
    mEditText.setText(content);
    // 重新定位光标位置
    CharSequence info = mEditText.getText();
    if (info instanceof Spannable) {
        Spannable spanText = (Spannable) info;
        Selection.setSelection(spanText, start + key.length());
    }
}

3)删除表情

    3.1)输入法中的删除,很自然的一个字符就删除一个,一个表情就删除一个表情(OK)

    3.2)表情面板中的删除按钮,一开始考虑是从text最后删除一个符号,或者匹配一个表情删除掉

int start = mEditText.getSelectionStart();
Editable content = mEditText.getText();
//正则表达式去判读最后一个符号是不是表情
Matcher matcher = Pattern.compile("\\{:\\d{1,2}:\\}$", Pattern.CASE_INSENSITIVE).matcher(content);
if(matcher.find()){
    String faceText = matcher.group();
    int index = content.toString().lastIndexOf(faceText);
    mEditText.setText( content.delete(index, index+faceText.length()) );
}else{
    mEditText.setText( content.delete(content.length()-1, content.length()) );
}
//然后再重新定位光标
CharSequence info = mEditText.getText();
if (info instanceof Spannable) {
    Spannable spanText = (Spannable) info;

    if(start > spanText.length()){
        start = spanText.length();
    }
    Selection.setSelection(spanText, start);
}

    3.3)很明显3.2中的方法比较麻烦,而且凌乱(貌似也有Bug)。另一种方法是于3.1的效果一致,就是直接调用系统的删除方式

//一句话搞定,我也是醉了
mEditText.onKeyDown(KeyEvent.KEYCODE_DEL, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));

4)最后封装一个输入框与表情合并的Dialog(使用WindowManager创建)

public class ReplyDialog implements View.OnClickListener{

    ...

    private WindowManager mWindowManager;
    public static LayoutParams mLayoutParams;

    private View mView;

    private OnSendListener mListener;

    private Context mContext;
    public ReplyDialog(Context context) {
        mContext = context;

        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);

        mLayoutParams = new LayoutParams();
        mLayoutParams.type = LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
        mLayoutParams.format = PixelFormat.RGBA_8888;
//      mLayoutParams.flags = LayoutParams.FLAG_ALT_FOCUSABLE_IM;
        mLayoutParams.gravity = Gravity.LEFT|Gravity.TOP;
//      mLayoutParams.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE;

        mView = View.inflate(mContext,R.layout.layout_dongtai_reply_dialog,null);
        ViewUtils.inject(this, mView);

        //
        pager_emo.bindEditText(et_dongtai_dialog_content);

        v_dongtai_input_outside.setOnClickListener(this);
        tv_dongtai_dialog_cancel.setOnClickListener(this);
        iv_dongtai_dialog_biaoqing.setOnClickListener(this);
        et_dongtai_dialog_content.setOnClickListener(this);
        btn_dongtai_dialog_send.setOnClickListener(this);

        et_dongtai_dialog_content.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN){
                    hide();
                    return true;
                }
                return false;
            }
        });
    }

    public void show(OnSendListener listener){
        mListener = listener;
        try{
            et_dongtai_dialog_content.setText("");
            mWindowManager.addView(mView, mLayoutParams);
            mView.postDelayed(new Runnable() {
                @Override
                public void run() {
//                  et_dongtai_dialog_content.requestFocus();
                    showSoftInputView();
                }
            }, 200);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public void hide(){
        try{
            pager_emo.setVisibility(View.GONE);
            mWindowManager.removeView(mView);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View v) {
        switch(v.getId()){
            case R.id.v_dongtai_input_outside:
            case R.id.tv_dongtai_dialog_cancel:
                hideSoftInputView();
                hide();
                break;

            case R.id.iv_dongtai_dialog_biaoqing:
                hideSoftInputView();
                pager_emo.setVisibility(View.VISIBLE);
                break;
            case R.id.et_dongtai_dialog_content:
                pager_emo.setVisibility(View.GONE);
    //          showSoftInputView();
                break;
            case R.id.btn_dongtai_dialog_send:
            {
                String message = et_dongtai_dialog_content.getText().toString();
                if("".equals(message)){
                    Toast.makeText(mContext, "请输入回复内容", Toast.LENGTH_LONG).show();
                    break;
                }
                mListener.onSend(message);
                hide();
                break;
            }
        }
    }

    public void hideSoftInputView() {
        InputMethodManager manager = ((InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE));
        manager.hideSoftInputFromWindow(et_dongtai_dialog_content.getWindowToken(), 0);
    }

    private void showSoftInputView() {
        InputMethodManager manager = ((InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE));
        manager.showSoftInput(et_dongtai_dialog_content, InputMethodManager.SHOW_FORCED);
    }

    public interface OnSendListener{
        public void onSend(String content);
    }

}

4.1)布局文件就不给了,仅供参考就行了

4.2)show()的时候输入法不能弹出问题

//可能是还未初始化完成,所以添加了延迟(这个地方试了好久,postDelayed在很多地方也可以用用)
mView.postDelayed(new Runnable() {
    @Override
    public void run() {
//      et_dongtai_dialog_content.requestFocus();
        showSoftInputView();
    }
}, 200);

4.3)返回按键被windowManager拦截了,没有处理

//在view中来处理这个返回键
et_dongtai_dialog_content.setOnKeyListener(new View.OnKeyListener() {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN){
        hide();
        return true;
        }
    return false;
    }
});

4.4)最后把文本内容发送出去

//加了个接口,哈哈
public interface OnSendListener{
    public void onSend(String content);
}

注意一点,这里从EditText中获取的文本其实还是表情符号对应的{:n:},而不是图片,或者图片stream

勉强的写完了


本文地址 http://www.0kai.net/blog/2015/09/11/42-emoji-panel.html,转载请注明!