社会化APP加载表情的方法

在做一些社会化APP时,用户总是青睐使用表情,下面就探究一下如何在APP中添加表情。在支持输入表情时,一般要涉及到表情框&&键盘的切换,需要有一个按钮,来触发事件!这里仅仅是一个雏形,存在一些问题,在这里暴露一下:
1.表情&&键盘切换时,会发生跳动;
2.这里的输入框,仅仅支持一个。

1.键盘弹出时,将整个页面向上移动

在AndroidManifest,activity标签中添加windowSoftInputMode=adjustResize,键盘弹出时,整个页面就会向上移动,这样咱们放置在页面底部的键盘&&表情切换的按钮,就一直可见了。

 <activity
            android:windowSoftInputMode="adjustResize"
            android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            intent-filter>
        activity>

给表情框留下位置

 <com.util.emotions.IMEBar
            android:id="@+id/ime_bar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/btn"
            android:orientation="vertical">
            
            <EditText
                android:id="@+id/et_input"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:gravity="left|top"
                android:hint="分享新鲜事..."
                android:inputType="textMultiLine"
                android:minHeight="200dp" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="#eeeeee"
                android:gravity="center_vertical"
                android:orientation="horizontal"
                android:padding="8dp">

                <ImageView
                    android:id="@+id/iv_switch"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:contentDescription="null"
                    android:src="@drawable/icon_icon" />
            LinearLayout>
            
            <FrameLayout
                android:id="@+id/fl_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        com.util.emotions.IMEBar>

R.id.fl_content就是为表情框留下的位置。当键盘不可见的时候,R.id.fl_content就填充表情;当键盘可见时,R.id.fl_content GONE.
他俩不协调就出现了跳动的问题。

键盘&&表情面板切换的关键IMEBar

package com.util.emotions;

import android.content.Context;
import android.text.Editable;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.hang.emojidemo.R;

public class IMEBar extends LinearLayout implements OnClickListener,
        OnEmojiClickListener {
    public IMEBar(Context context) {
        super(context);
        init();
    }


    public IMEBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public IMEBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public void init() {
    /*在构造方法中,IMEBar实例化尚未完成,下面的操作,必须延迟执行;new Thread? No
    only the original thread that created a view hierarchy can touch its views
    这个和只有UI线程才能修改UI,是一样的
    通过下面的方法还是比较好的
    */
        this.postDelayed(new Runnable() {
            @Override
            public void run() {
                getEditText(IMEBar.this);
                if (mEditText == null) {
                    throw new RuntimeException(
                            "must contain a EditText  for putting content in");
                }
                mEditText.setOnClickListener(IMEBar.this);
                setup();
            }
        }, 100);
    }
/*通过递归的方法,获取EditText
*/
    public void getEditText(ViewGroup viewGroup) {

        for (int j = 0; j < viewGroup.getChildCount(); j++) {
            View childView = viewGroup.getChildAt(j);
            if (childView instanceof ViewGroup) {
                getEditText((ViewGroup) childView);
            } else {
                if (childView instanceof EditText) {
                    mEditText = (EditText) childView;
                }
            }
        }
    }


    private InputMethodManager mInputMethodManager;
    private EmojiUtil mEmojiUtil;
    private EditText mEditText;
    /**
     * 放置弹出内容的区域,需要一个id为fl_content的FrameLayout
     */
    private FrameLayout mFrameContent;
    /**
     * 表情栏
     */
    private EmojiPanel mEmotionsPanel;
    private ImageView mSwitchView;

    public void setup() throws RuntimeException {
        /**
         * 1.表情管理工具
* 2.表情容器
* 3.表情开关按钮
*/
// IME manager mInputMethodManager = (InputMethodManager) getContext() .getSystemService(Context.INPUT_METHOD_SERVICE); // 表情管理 mEmojiUtil = EmojiUtil.getInstance(getContext().getApplicationContext()); // 放置弹出的容器 mFrameContent = (FrameLayout) findViewById(R.id.fl_content); if (mFrameContent == null) { throw new RuntimeException( "must specify a FrameLayout with id \"fl_content\" for putting content in"); } //进行keyboard&&icon 切换 mSwitchView = (ImageView) findViewById(R.id.iv_switch); if (mSwitchView == null) { throw new RuntimeException( "must specify a View with id \"btn_emojis\""); } // bind listener mSwitchView.setOnClickListener(this); // 表情众面板 mEmotionsPanel = new EmojiPanel(getContext()); mEmotionsPanel.setOnEmotionClickListener(this); // 面板加入布局中,并默认不可见 mEmotionsPanel.setVisibility(View.GONE); mFrameContent.addView(mEmotionsPanel); } public void onShowEmotionsPanel() { mEmotionsPanel.resetToFirstPage(); mEmotionsPanel.setVisibility(View.VISIBLE); } public boolean isEmotionsPanelShown() { return mEmotionsPanel != null && mEmotionsPanel.getVisibility() == View.VISIBLE; } public void hideEmotionsPanel() { mEmotionsPanel.setVisibility(View.GONE); } private void showKeyboard() { if (mInputMethodManager != null && mEditText != null) { mInputMethodManager.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT); } } private void hideSoftKeyInput() { if (mInputMethodManager != null && mEditText != null) { mInputMethodManager.hideSoftInputFromWindow( mEditText.getApplicationWindowToken(), 0); } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.iv_switch: { if (isEmotionsPanelShown()) { hideEmotionsPanel(); showKeyboard(); mSwitchView.setImageResource(R.drawable.icon_icon); } else { hideSoftKeyInput(); onShowEmotionsPanel(); mSwitchView.setImageResource(R.drawable.keyboard); } break; } case R.id.et_input: { // 点击输入框->隐藏表情 hideEmotionsPanel(); break; } default: break; } } @Override public void onEmotionClick(final String emojiFile) { if (mEditText == null) { return; } if (EmojiUtil.BACKSPACE.equals(emojiFile)) { Editable editable = mEditText.getText(); // 删除光标所在的前一个字符或表情 int index = mEditText.getSelectionStart(); if (index <= 0) { // 光标在最前部,不需要删除 return; } char c = editable.charAt(index - 1); if (']' == c) { String text = editable.toString(); // 排除"[开心]A]"这种情况 int nextOpenBracket = text.lastIndexOf('[', index - 2); int nextCloseBracket = text.lastIndexOf(']', index - 2); if (nextCloseBracket < nextOpenBracket) { // 删除一对儿 editable.delete(nextOpenBracket, index); return; } } // 正常删除 editable.delete(index - 1, index); } else { /* * 点击了一个表情->在光标处插入表情 */ // 将SpannableString插入到光标处 int index = mEditText.getSelectionStart(); mEditText.getText().insert( index, mEmojiUtil .getSpannableByEmojiName(getContext(), emojiFile)); } } }

4.键盘面板

public class EmojiPanel extends FrameLayout {...}

键盘面板就是一个自定义组件,在里面放入ViewPager&&添加一个小圆点,和常规的APP首页的轮播图是一个显示效果(这里还不需要自动滚动)。
讲一下小难点:

1.如果咱们将emoji表情都放在assets下面的emojis文件夹下面,如何遍历所有的文件呢?
2.如果获取assets文件夹中的某一文件呢?
这就涉及到assets文件的读写,请参考
读取assets文件
3.在每一页表情的最后一个是——删除按钮!也就是在遍历assets/emojis文件夹中的图片时,要将其过滤掉,最后直接放到最后一个按钮上。在计算将有多少页表情的时候

//mEmojiUtils.getEmojiNameArray()是不包含删除按钮的所有图片对应的图片名
  int pages = mEmojiUtils.getEmojiNameArray().length / (EMOJIS_PER_PAGE - 1);
        return mEmojiUtils.getEmojiNameArray().length % (EMOJIS_PER_PAGE - 1) == 0 ? pages : pages + 1;

在对每一个页面内的表情进行赋值时

 private String[] getArrayItems(int position) {

        String[] emojiNameList = mEmojiUtils.getEmojiNameArray();
        /* 判断当前页面要显示多少个表情
  position * (EMOJIS_PER_PAGE - 1): 是前面所有的表情页面已经显示的表情数目      
*/
String[] array = new String[Math.min(EMOJIS_PER_PAGE, emojiNameList.length - position * (EMOJIS_PER_PAGE - 1))];
        for (int j = 0; j < array.length - 1; j++) {
            array[j] = emojiNameList[(position * (EMOJIS_PER_PAGE - 1) + j)];
        }
        /*
       array数组最后一个下标为 array.length - 1
       对于最后一个面板并不是  EMOJIS_PER_PAGE-1
      */
        array[array.length - 1] = EmojiUtil.BACKSPACE;
        return array;
    }

处理用户点击表情事件

  @Override
    public void onEmotionClick(final String emojiFile) {
        if (mEditText == null) {
            return;
        }
//通过文件名进行判断,如果为删除图标,执行删除操作
        if (EmojiUtil.BACKSPACE.equals(emojiFile)) {
            Editable editable = mEditText.getText();
            // 删除光标所在的前一个字符或表情
            int index = mEditText.getSelectionStart();
            if (index <= 0) {
                // 光标在最前部,不需要删除
                return;
            }

            char c = editable.charAt(index - 1);
            /*判断要删除的是不是表情
  为了将用户的正常输入与咱们的表情区分开来,这里使用[]对表情进行了包含(eg.emoji_001对应在文本框中的文本为"[emoji_001]")          
*/
if (']' == c) {
                String text = editable.toString();
                // 排除"[开心]A]"这种情况
                int nextOpenBracket = text.lastIndexOf('[', index - 2);
                int nextCloseBracket = text.lastIndexOf(']', index - 2);
                if (nextCloseBracket < nextOpenBracket) {
                    // 删除一对儿
                    editable.delete(nextOpenBracket, index);
                    return;
                }
            }
            //如果为正常文本,删除一个字符            editable.delete(index - 1, index);
        } else {
            /*
             * 点击了一个表情->在光标处插入表情
             */
            // 将SpannableString插入到光标处
            int index = mEditText.getSelectionStart();
            mEditText.getText().insert(
                    index,
                    mEmojiUtil
                            .getSpannableByEmojiName(getContext(), emojiFile));
        }
    }

这里不但要让用户点击的文件名添加到文本上,记得要给它添加包装——[],

  /**
     * 根据emoji名字(如:emj_001)
     */
    public SpannableString getSpannableByEmojiName(Context context,
                                                   String iconName) {
        if (iconName.contains("[") && (iconName.contains("]"))) {
            iconName = iconName.substring(iconName.indexOf("[") + 1, iconName.indexOf("]"));
        }
        Bitmap emojiBmp = getEmojiIcon(iconName);
        SpannableString ss = new SpannableString("[" + iconName + "]");
        Drawable d = new BitmapDrawable(context.getResources(), emojiBmp);
               int emojiSize =
                Util.dip2px(context, 24);
        d.setBounds(0, 0, emojiSize, emojiSize);
        ImageSpan imgSpan = new ImageSpan(d);
        ss.setSpan(imgSpan, 0, iconName.length() + 2,
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        return ss;
    }

表情对应字符串[emoji_001],还原为表情

  /**
     * 将纯文本,转换成带表情的Spannable
     */
    public SpannableStringBuilder convert(Context context, CharSequence text) {
        if (TextUtils.isEmpty(text)) {
            return null;
        }
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
        //通过正则表达式对emoji表情进行过滤
        Pattern p = Pattern.compile("\\[\\S+?\\]");
        Matcher m = p.matcher(builder);
        while (m.find()) {
            int start = m.start();
            String emojiName = m.group();
            // 用表情替换原名字
            builder.replace(start, start + emojiName.length(),
                    getSpannableByEmojiName(context, emojiName));
        }
        return builder;
    }

下载地址

http://download.csdn.net/detail/guchuanhang/9564278

你可能感兴趣的:(键盘,社会化,app,表情)