简体   繁体   English

在android中添加自定义图像作为表情符号

[英]add custom image as Emoji in android

how to add custom image as emoji in android InputMethodService.如何在 android InputMethodService 中添加自定义图像作为表情符号。 i have tried using我试过使用

ImageGetter imageGetter = new ImageGetter() {
                public Drawable getDrawable(String source) {    
                    StringTokenizer st = new StringTokenizer(str, ".");
                    Drawable d = new BitmapDrawable(getResources(),emoticons[Integer.parseInt(st.nextToken()) - 1]);
                    d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
                    return d;
                }
            };

            Spanned cs = Html.fromHtml("<img src ='"+ str +"'/>", imageGetter, null);


        getCurrentInputConnection().commitText(cs,1);

but only getting但只有得到在此处输入图片说明a obj text image. obj 文本图像。 is there any way to add custom emoji to message(whatsapp /twitter) in android有什么方法可以在android中将自定义表情符号添加到消息(whatsapp / twitter)中

Edit :编辑 :

in my application if i type hello using my custom keyboard i have to insert a custom hello picture in edit field for every app installed in phone.在我的应用程序中,如果我使用自定义键盘输入 hello 我必须在编辑字段中为手机中安装的每个应用程序插入自定义 hello 图片。

can it achieved ?能实现吗?

Edit : 2编辑:2

i have checked it using convert mage to base64 also but nothing happen with Tag我也使用 convert mage to base64 检查过它,但没有发生任何标记

ImageGetter imageGetter = new ImageGetter() {
                @Override
                public Drawable getDrawable(String source) {    
                    byte [] encodeByte=Base64.decode("iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",Base64.DEFAULT);
                    Bitmap bitmap=BitmapFactory.decodeByteArray(encodeByte, 0, encodeByte.length);      
                    Drawable d = new BitmapDrawable(bitmap);

                    Log.e("Chk", "Height : "+d.getIntrinsicHeight());
                    Log.e("Chk", "Width : " + d.getIntrinsicWidth());                       

                    return d;
                }
            };

            Spanned cs = Html.fromHtml("<img src='data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='/>", imageGetter,null);

            Log.e("Chk", "12 : " + 12);


        getCurrentInputConnection().setComposingText(cs,1);

Edit : 3编辑:3

I am using this InputMethodService class in my custom keyboard application.我在我的自定义键盘应用程序中使用这个 InputMethodService 类。

package com.example.android.softkeyboard;

import java.util.ArrayList;
import java.util.List;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.text.Html;
import android.text.Html.ImageGetter;
import android.text.Spanned;
import android.text.method.MetaKeyKeyListener;
import android.util.Base64;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;

public class SoftKeyboard extends InputMethodService implements
        KeyboardView.OnKeyboardActionListener {
    static final boolean DEBUG = false;

    static final boolean PROCESS_HARD_KEYS = true;

    private KeyboardView mInputView;
    private CandidateView mCandidateView;
    private CompletionInfo[] mCompletions;

    private StringBuilder mComposing = new StringBuilder();
    private boolean mPredictionOn;
    private boolean mCompletionOn;
    private int mLastDisplayWidth;
    private boolean mCapsLock;
    private long mLastShiftTime;
    private long mMetaState;

    private Keyboard mSymbolsKeyboard;
    private Keyboard mSymbolsShiftedKeyboard;
    private Keyboard mQwertyKeyboard;

    private String mWordSeparators;

    StringBuilder strBldrFrEmoji;

    private void makeKeyboards() {
        if (mQwertyKeyboard != null) {
            // Configuration changes can happen after the keyboard gets
            // recreated,
            // so we need to be able to re-build the keyboards if the available
            // space has changed.
            int displayWidth = getMaxWidth();
            if (displayWidth == mLastDisplayWidth)
                return;
            mLastDisplayWidth = displayWidth;
        }
        mQwertyKeyboard = new LatinKeyboard(this, R.xml.qwerty);
        mSymbolsKeyboard = new LatinKeyboard(this, R.xml.symbols);
        mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols_shift);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        makeKeyboards();
        mWordSeparators = getResources().getString(R.string.word_separators);
    }

    @Override
    public View onCreateInputView() {
        // We call makeKeyboards() here to regenerate them if needed due to
        // a configuration change.
        makeKeyboards();
        mInputView = (KeyboardView) getLayoutInflater().inflate(R.layout.input,
                null);
        mInputView.setOnKeyboardActionListener(this);
        mInputView.setKeyboard(mQwertyKeyboard);
        return mInputView;
    }

    @Override
    public View onCreateCandidatesView() {
        mCandidateView = new CandidateView(this);
        mCandidateView.setService(this);
        return mCandidateView;
    }

    @Override
    public void onStartInputView(EditorInfo attribute, boolean restarting) {
        super.onStartInputView(attribute, restarting);

        // Reset our state. We want to do this even if restarting, because
        // the underlying state of the text editor could have changed in any
        // way.
        mComposing.setLength(0);
        updateCandidates();

        if (!restarting) {
            // Clear shift states.
            mMetaState = 0;
        }

        mPredictionOn = false;
        mCompletionOn = false;
        mCompletions = null;
        Keyboard keyboard;

        // We are now going to initialize our state based on the type of
        // text being edited.
        switch (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) {
        case EditorInfo.TYPE_CLASS_NUMBER:
        case EditorInfo.TYPE_CLASS_DATETIME:
            // Numbers and dates default to the symbols keyboard, with
            // no extra features.
            keyboard = mSymbolsKeyboard;
            break;

        case EditorInfo.TYPE_CLASS_PHONE:
            // Phones will also default to the symbols keyboard, though
            // often you will want to have a dedicated phone keyboard.
            keyboard = mSymbolsKeyboard;
            break;

        case EditorInfo.TYPE_CLASS_TEXT:

            // This is general text editing. We will default to the
            // normal alphabetic keyboard, and assume that we should
            // be doing predictive text (showing candidates as the
            // user types).
            keyboard = mQwertyKeyboard;
            mPredictionOn = true;

            // We now look for a few special variations of text that will
            // modify our behavior.
            int variation = attribute.inputType
                    & EditorInfo.TYPE_MASK_VARIATION;
            if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD) {
                // Do not display predictions / what the user is typing
                // when they are entering a password.
                mPredictionOn = false;
            }

            if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
                    || variation == EditorInfo.TYPE_TEXT_VARIATION_URI) {
                // Our predictions are not useful for e-mail addresses
                // or URIs.
                mPredictionOn = false;
            }

            if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
                // If this is an auto-complete text view, then our predictions
                // will not be shown and instead we will allow the editor
                // to supply their own. We only show the editor's
                // candidates when in fullscreen mode, otherwise relying
                // own it displaying its own UI.
                mPredictionOn = false;
                mCompletionOn = isFullscreenMode();
            }

            // We also want to look at the current state of the editor
            // to decide whether our alphabetic keyboard should start out
            // shifted.
            updateShiftKeyState(attribute);
            break;

        default:
            // For all unknown input types, default to the alphabetic
            // keyboard with no special features.
            keyboard = mQwertyKeyboard;

        }

        // Apply the selected keyboard to the input view.
        if (mInputView != null) {
            mInputView.setKeyboard(keyboard);
            mInputView.closing();
        }
    }

    @Override
    public void onFinishInput() {
        super.onFinishInput();

        // Clear current composing text and candidates.
        mComposing.setLength(0);
        updateCandidates();

        // We only hide the candidates window when finishing input on
        // a particular editor, to avoid popping the underlying application
        // up and down if the user is entering text into the bottom of
        // its window.
        setCandidatesViewShown(false);

        if (mInputView != null) {
            mInputView.closing();
        }
    }

    @Override
    public void onUpdateSelection(int oldSelStart, int oldSelEnd,
            int newSelStart, int newSelEnd, int candidatesStart,
            int candidatesEnd) {

        // If the current selection in the text view changes, we should
        // clear whatever candidate text we have.
        if (mComposing.length() > 0
                && (newSelStart != candidatesEnd || newSelEnd != candidatesEnd)) {
            mComposing.setLength(0);
            updateCandidates();
            InputConnection ic = getCurrentInputConnection();
            if (ic != null) {
                ic.finishComposingText();

            }
        }
    }

    @Override
    public void onDisplayCompletions(CompletionInfo[] completions) {
        if (mCompletionOn) {
            mCompletions = completions;
            if (completions == null) {
                setSuggestions(null, false, false);
                return;
            }

            List<String> stringList = new ArrayList<String>();
            for (int i = 0; i < (completions != null ? completions.length : 0); i++) {
                CompletionInfo ci = completions[i];
                if (ci != null)
                    stringList.add(ci.getText().toString());
            }
            setSuggestions(stringList, true, true);
        }
    }

    private boolean translateKeyDown(int keyCode, KeyEvent event) {

        mMetaState = MetaKeyKeyListener.handleKeyDown(mMetaState, keyCode,
                event);
        int c = event.getUnicodeChar(MetaKeyKeyListener
                .getMetaState(mMetaState));
        mMetaState = MetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState);
        InputConnection ic = getCurrentInputConnection();
        if (c == 0 || ic == null) {
            return false;
        }

        boolean dead = false;

        if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) {
            dead = true;
            c = c & KeyCharacterMap.COMBINING_ACCENT_MASK;
        }

        if (mComposing.length() > 0) {
            char accent = mComposing.charAt(mComposing.length() - 1);
            int composed = KeyEvent.getDeadChar(accent, c);

            if (composed != 0) {
                c = composed;
                mComposing.setLength(mComposing.length() - 1);
            }
        }

        onKey(c, null);

        return true;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        switch (keyCode) {

        case KeyEvent.KEYCODE_BACK:
            // The InputMethodService already takes care of the back
            // key for us, to dismiss the input method if it is shown.
            // However, our keyboard could be showing a pop-up window
            // that back should dismiss, so we first allow it to do that.
            if (event.getRepeatCount() == 0 && mInputView != null) {
                if (mInputView.handleBack()) {
                    return true;
                }
            }
            break;

        case KeyEvent.KEYCODE_DEL:
            // Special handling of the delete key: if we currently are
            // composing text for the user, we want to modify that instead
            // of let the application to the delete itself.
            if (mComposing.length() > 0) {
                onKey(Keyboard.KEYCODE_DELETE, null);
                return true;
            }
            break;

        default:
            // For all other keys, if we want to do transformations on
            // text being entered with a hard keyboard, we need to process
            // it and do the appropriate action.
            if (PROCESS_HARD_KEYS) {
                if (keyCode == KeyEvent.KEYCODE_SPACE
                        && (event.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
                    // A silly example: in our input method, Alt+Space
                    // is a shortcut for 'android' in lower case.
                    InputConnection ic = getCurrentInputConnection();
                    if (ic != null) {
                        // First, tell the editor that it is no longer in the
                        // shift state, since we are consuming this.
                        ic.clearMetaKeyStates(KeyEvent.META_ALT_ON);
                        keyDownUp(KeyEvent.KEYCODE_A);
                        keyDownUp(KeyEvent.KEYCODE_N);
                        keyDownUp(KeyEvent.KEYCODE_D);
                        keyDownUp(KeyEvent.KEYCODE_R);
                        keyDownUp(KeyEvent.KEYCODE_O);
                        keyDownUp(KeyEvent.KEYCODE_I);
                        keyDownUp(KeyEvent.KEYCODE_D);
                        // And we consume this event.
                        return true;
                    }
                }
                if (mPredictionOn && translateKeyDown(keyCode, event)) {
                    return true;
                }
            }
        }

        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {

        // If we want to do transformations on text being entered with a hard
        // keyboard, we need to process the up events to update the meta key
        // state we are tracking.
        if (PROCESS_HARD_KEYS) {
            if (mPredictionOn) {
                mMetaState = MetaKeyKeyListener.handleKeyUp(mMetaState,
                        keyCode, event);
            }
        }

        return super.onKeyUp(keyCode, event);
    }

    private void commitTyped(InputConnection inputConnection) {

        if (mComposing.length() > 0) {
            inputConnection.commitText(mComposing, mComposing.length());
            mComposing.setLength(0);
            updateCandidates();
        }
    }

    private void updateShiftKeyState(EditorInfo attr) {
        if (attr != null && mInputView != null
                && mQwertyKeyboard == mInputView.getKeyboard()) {
            int caps = getCurrentInputConnection().getCursorCapsMode(
                    attr.inputType);
            mInputView.setShifted(mCapsLock || caps != 0);
        }
    }

    private boolean isAlphabet(int code) {

        if (Character.isLetter(code)) {
            return true;
        } else {
            return false;
        }
    }

    private void keyDownUp(int keyEventCode) {

        getCurrentInputConnection().sendKeyEvent(
                new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
        getCurrentInputConnection().sendKeyEvent(
                new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
    }

    private void sendKey(int keyCode) {
        ;
        switch (keyCode) {
        case '\n':
            keyDownUp(KeyEvent.KEYCODE_ENTER);
            break;
        default:
            if (keyCode >= '0' && keyCode <= '9') {
                keyDownUp(keyCode - '0' + KeyEvent.KEYCODE_0);
            } else {
                getCurrentInputConnection().commitText(
                        String.valueOf((char) keyCode), 1);
            }
            break;
        }
    }

    public void onKey(int primaryCode, int[] keyCodes) {

        if (primaryCode == 32 || primaryCode == -5 || primaryCode == 10) {
            if (strBldrFrEmoji != null) {
                strBldrFrEmoji = null;
            }
        }

        if (isWordSeparator(primaryCode)) {

            if (mComposing.length() > 0) {
                commitTyped(getCurrentInputConnection());
            }
            sendKey(primaryCode);
            updateShiftKeyState(getCurrentInputEditorInfo());
        } else if (primaryCode == Keyboard.KEYCODE_DELETE) {
            handleBackspace();
        } else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
            handleShift();
        } else if (primaryCode == Keyboard.KEYCODE_CANCEL) {
            handleClose();
            return;
        } else if (primaryCode == LatinKeyboardView.KEYCODE_OPTIONS) {
            // Show a menu or somethin'
        } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE
                && mInputView != null) {
            Keyboard current = mInputView.getKeyboard();
            if (current == mSymbolsKeyboard
                    || current == mSymbolsShiftedKeyboard) {
                current = mQwertyKeyboard;
            } else {
                current = mSymbolsKeyboard;
            }
            mInputView.setKeyboard(current);
            if (current == mSymbolsKeyboard) {
                current.setShifted(false);
            }
        } else {
            handleCharacter(primaryCode, keyCodes);
        }
    }

    private void updateCandidates() {

        if (!mCompletionOn) {
            if (mComposing.length() > 0) {
                ArrayList<String> list = new ArrayList<String>();
                list.add(mComposing.toString());
                setSuggestions(list, true, true);
            } else {
                setSuggestions(null, false, false);
            }
        }
    }

    public void setSuggestions(List<String> suggestions, boolean completions,
            boolean typedWordValid) {

        if (mCandidateView != null) {
            mCandidateView.setSuggestions(suggestions, completions,
                    typedWordValid);
            if (suggestions != null && suggestions.size() > 0) {
                setCandidatesViewShown(true);
            } else if (isFullscreenMode()) {
                setCandidatesViewShown(true);
            }
        }
    }

    private void handleBackspace() {
        final int length = mComposing.length();
        if (length > 1) {
            mComposing.delete(length - 1, length);
            getCurrentInputConnection().setComposingText(mComposing,
                    mComposing.length());
            updateCandidates();
        } else if (length > 0) {
            mComposing.setLength(0);
            getCurrentInputConnection().commitText("", 0);
            updateCandidates();
        } else {
            keyDownUp(KeyEvent.KEYCODE_DEL);
        }
        updateShiftKeyState(getCurrentInputEditorInfo());
    }

    private void handleShift() {
        if (mInputView == null) {
            return;
        }

        Keyboard currentKeyboard = mInputView.getKeyboard();
        if (mQwertyKeyboard == currentKeyboard) {
            // Alphabet keyboard
            checkToggleCapsLock();
            mInputView.setShifted(mCapsLock || !mInputView.isShifted());
        } else if (currentKeyboard == mSymbolsKeyboard) {
            mSymbolsKeyboard.setShifted(true);
            mInputView.setKeyboard(mSymbolsShiftedKeyboard);
            mSymbolsShiftedKeyboard.setShifted(true);
        } else if (currentKeyboard == mSymbolsShiftedKeyboard) {
            mSymbolsShiftedKeyboard.setShifted(false);
            mInputView.setKeyboard(mSymbolsKeyboard);
            mSymbolsKeyboard.setShifted(false);
        }
    }

    private void handleCharacter(int primaryCode, int[] keyCodes) {

        if (strBldrFrEmoji == null) {
            strBldrFrEmoji = new StringBuilder();

        }
        strBldrFrEmoji.append(String.valueOf((char) primaryCode));

        checkEnteredText(strBldrFrEmoji.toString());

        if (isInputViewShown()) {
            if (mInputView.isShifted()) {
                primaryCode = Character.toUpperCase(primaryCode);
            }
        }
        if (isAlphabet(primaryCode) && mPredictionOn) {
            mComposing.append((char) primaryCode);
            getCurrentInputConnection().setComposingText(mComposing,
                    mComposing.length());
            updateShiftKeyState(getCurrentInputEditorInfo());
            updateCandidates();
        } else {
            getCurrentInputConnection().commitText(
                    String.valueOf((char) primaryCode), 1);
        }
    }

    void checkEnteredText(String str1) {

        if (str1.length() > 0) {
            if (str1.equalsIgnoreCase("hello")) {


                /**
                ImageGetter imageGetter = new ImageGetter() {
            public Drawable getDrawable(String source) {    
                Drawable d = new BitmapDrawable(getResources(),BitmapFactory.decodeResource(getResources(),
                        R.drawable.ic_smiley));
                d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
                return d;
            }
        };

        Spanned cs = Html.fromHtml("<a href='http://www.google.com'><img src ='"+ str1 +"'/><a>", imageGetter, null);
                */
                ImageGetter imageGetter = new ImageGetter() {
                    @Override
                    public Drawable getDrawable(String source) {
                        byte[] encodeByte = Base64
                                .decode("iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
                                        Base64.DEFAULT);
                        Bitmap bitmap = BitmapFactory.decodeByteArray(
                                encodeByte, 0, encodeByte.length);
                        Drawable d = new BitmapDrawable(bitmap);

                        return d;
                    }
                };

                Spanned cs = Html
                        .fromHtml(
                                "<img src='data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='/>",
                                imageGetter, null);

                getCurrentInputConnection().setComposingText(cs, 1);

            }
        }

    }

    private void handleClose() {

        commitTyped(getCurrentInputConnection());
        // dismissSoftInput(0);
        mInputView.closing();
    }

    private void checkToggleCapsLock() {
        long now = System.currentTimeMillis();
        if (mLastShiftTime + 800 > now) {
            mCapsLock = !mCapsLock;
            mLastShiftTime = 0;
        } else {
            mLastShiftTime = now;
        }
    }

    private String getWordSeparators() {
        return mWordSeparators;
    }

    public boolean isWordSeparator(int code) {

        String separators = getWordSeparators();
        return separators.contains(String.valueOf((char) code));
    }

    public void pickDefaultCandidate() {
        pickSuggestionManually(0);
    }

    public void pickSuggestionManually(int index) {
        if (mCompletionOn && mCompletions != null && index >= 0
                && index < mCompletions.length) {
            CompletionInfo ci = mCompletions[index];
            getCurrentInputConnection().commitCompletion(ci);
            if (mCandidateView != null) {
                mCandidateView.clear();
            }
            updateShiftKeyState(getCurrentInputEditorInfo());
        } else if (mComposing.length() > 0) {
            // If we were generating candidate suggestions for the current
            // text, we would commit one of them here. But for this sample,
            // we will just commit the current text.
            commitTyped(getCurrentInputConnection());
        }
    }

    public void swipeRight() {
        if (mCompletionOn) {
            pickDefaultCandidate();
        }
    }

    public void swipeLeft() {
        handleBackspace();
    }

    public void swipeDown() {
        handleClose();
    }

    public void swipeUp() {
    }

    public void onPress(int primaryCode) {
    }

    public void onRelease(int primaryCode) {
    }

    @Override
    public void onText(CharSequence text) {

    }
}

You're trying to send an image to twitter/whatsapp, not an emoji .您正在尝试将图像发送到 twitter/whatsapp,而不是 emoji There is a difference.它们是有区别的。

Emoji literally means "picture to character" and are dependent on the phone's character set that the receiver is using.表情符号的字面意思是“图片到字符”,取决于接收者使用的手机字符集。 Even if you replaced the images associated with your own phone's emojis, the person you are sending the emoji to would only be able to see their own keysets of emojis.即使您替换了与自己手机的表情符号相关联的图像,您发送表情符号的人也只能看到他们自己的表情符号键集。 This is not what you want.这不是你想要的。

Images have nothing to do with phone keysets and will appear roughly the same no matter which phones they are sent to.图像与手机按键无关,无论它们发送到哪部手机,它们的外观都大致相同。 You need to pipe your image to an intent associated with the application the user is currently focused on.您需要将图像通过管道传输到与用户当前关注的应用程序相关联的意图。

  1. Look at Android's application stack.查看Android的应用程序堆栈。
  2. Find the Application on top.找到顶部的应用程序。
  3. Pipe the bitmap via intent.putExtra(Intent.EXTRA_STREAM, BitmapUriLocation) into the application;通过 intent.putExtra(Intent.EXTRA_STREAM, BitmapUriLocation) 将位图输送到应用程序中;

This is how the app referenced here : https://play.google.com/store/apps/details?id=com.plantpurple.emojidom most likely did things.这是这里引用的应用程序的方式: https : //play.google.com/store/apps/details?id=com.plantpurple.emojidom最有可能做的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM