社会化APP加载表情的方法

Posted 码农000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了社会化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>
            <!-- 表情/keyboard-->
            <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.表情管理工具<br/>
         * 2.表情容器<br/>
         * 3.表情开关按钮<br/>
         */

        // 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加载表情的方法的主要内容,如果未能解决你的问题,请参考以下文章

js 怎么把emoji表情给过滤掉

超级有用的9个PHP代码片段

UWP通过机器学习加载ONNX进行表情识别

android webview不加载片段

Android EditText禁止输入Emoji表情

Android EditText禁止输入Emoji表情