android在EditText中插入表情图片
jopen
10年前
EditText通常用于显示文字,但有时候也需要在文字中夹杂一些图片,比如QQ中就可以使用表情图片,又比如需要的文字高亮显示等等,如何在android中也做到这样呢?
记得android中有个android.text包,这里提供了对文本的强大的处理功能。
添加图片主要用SpannableString和ImageSpan类,具体参考sdk文档 SpannableString
这里以人人网客户端发布消息的界面为例
布局文件main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#FFFFFF" android:orientation="vertical" > <LinearLayout android:layout_width="fill_parent" android:layout_height="48dip" android:background="@drawable/v5_0_1_flipper_head_blue_background" android:orientation="horizontal" > <ImageView android:id="@+id/newsfeedpublish_back" android:layout_width="54dip" android:layout_height="fill_parent" android:layout_gravity="center" android:background="@drawable/v5_0_1_flipper_head_title_wrapper_background" android:clickable="true" android:scaleType="centerInside" android:src="@drawable/v5_0_1_flipper_head_back" /> <ImageView android:layout_width="2px" android:layout_height="24dip" android:layout_gravity="center" android:src="@drawable/v5_0_1_flipper_head_separator" /> <TextView android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_weight="1" android:gravity="center_vertical" android:paddingLeft="8dip" android:text="发布新鲜事" android:textColor="#FFFFFF" android:textSize="16sp" android:textStyle="bold" /> <ImageView android:id="@+id/newsfeedpublish_publish" android:layout_width="54dip" android:layout_height="fill_parent" android:background="@drawable/v5_0_1_flipper_head_title_wrapper_background" android:clickable="true" android:scaleType="centerInside" android:src="@drawable/v5_0_1_flipper_head_publish" /> </LinearLayout> <EditText android:id="@+id/newsfeedpublish_content" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dip" android:layout_weight="1" android:background="#00000000" android:gravity="top" android:hint="您正在干嘛?" android:maxLength="140" android:padding="8dip" android:textColor="#000000" android:textSize="16sp" > <requestFocus /> </EditText> <View android:layout_width="fill_parent" android:layout_height="1dip" android:layout_marginLeft="8dip" android:layout_marginRight="8dip" android:background="@drawable/v5_0_1_publisher_split_line" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="40dip" android:orientation="horizontal" > <LinearLayout android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_margin="5dip" android:layout_weight="1" android:background="@drawable/v5_0_1_publisher_poi_text_bg" android:orientation="horizontal" > <Button android:id="@+id/newsfeedpublish_poi_place" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_weight="1" android:background="#00000000" android:drawableLeft="@drawable/v5_0_1_publisher_poi_icon" android:drawablePadding="8dip" android:ellipsize="start" android:gravity="left|center_vertical" android:singleLine="true" android:text="正在定位..." android:textColor="#ff005092" /> <ImageView android:id="@+id/newsfeedpublish_poi_sperator" android:layout_width="1dip" android:layout_height="fill_parent" android:src="@drawable/v5_0_1_publisher_poi_area_sperator" /> <ImageView android:id="@+id/newsfeedpublish_poi_list" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:src="@drawable/v5_0_1_publisher_poi_list_icon" android:visibility="invisible" /> </LinearLayout> <LinearLayout android:layout_width="100dip" android:layout_height="fill_parent" android:layout_weight="1" android:gravity="right" android:padding="8dip" > <TextView android:id="@+id/newsfeedpublish_count" android:layout_width="wrap_content" android:layout_height="fill_parent" android:gravity="center" android:text="0" android:textColor="#80333333" android:textSize="14sp" /> <TextView android:layout_width="wrap_content" android:layout_height="fill_parent" android:gravity="center" android:text="/140" android:textColor="#80333333" android:textSize="14sp" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="40dip" android:background="@drawable/v5_0_1_publisher_buttons_area_bg" android:orientation="horizontal" > <ImageButton android:id="@+id/newsfeedpublish_voice" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_weight="1" android:background="@drawable/v5_0_1_publisher_button_bg" android:src="@drawable/v5_0_1_publisher_voice_button" /> <ImageButton android:id="@+id/newsfeedpublish_poi" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_weight="1" android:background="@drawable/v5_0_1_publisher_button_bg" android:src="@drawable/v5_0_1_publisher_poi_button_on" /> <ImageButton android:id="@+id/newsfeedpublish_image" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_weight="1" android:background="@drawable/v5_0_1_publisher_button_bg" android:src="@drawable/v5_0_1_publisher_image_button" /> <ImageButton android:id="@+id/newsfeedpublish_at" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_weight="1" android:background="@drawable/v5_0_1_publisher_button_bg" android:src="@drawable/v5_0_1_publisher_at_button" /> <ImageButton android:id="@+id/newsfeedpublish_emoticon" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_weight="1" android:background="@drawable/v5_0_1_publisher_button_bg" android:src="@drawable/v5_0_1_publisher_emotion_button" /> </LinearLayout> <GridView android:id="@+id/newsfeedpublish_emoticons" android:layout_width="fill_parent" android:layout_height="200dip" android:background="@drawable/v5_0_1_publisher_emotion_area_bg" android:cacheColorHint="#00000000" android:focusableInTouchMode="true" android:horizontalSpacing="5dip" android:listSelector="#00000000" android:numColumns="8" android:stretchMode="columnWidth" android:verticalSpacing="5dip" android:visibility="gone" > </GridView> </LinearLayout>
MainActivity.java
package com.yulore.emotion; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.GridView; import android.widget.ImageButton; import android.widget.ImageView; public class MainActivity extends Activity implements OnClickListener { private static final String TAG = "MainActivity"; private ImageButton ib_emotion; private GridView gv_emotion; private EmotionAdapter mAdapter; private EditText et_content; private ImageView iv_publish; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); findViewById(); initEmotionIcons(); setListener(); mAdapter = new EmotionAdapter(); gv_emotion.setAdapter(mAdapter); } private void initEmotionIcons() { String json = EmotionUtil.getInstance().readFromFile(EmotionUtil.DOWNLOAD_EMOTION_PATH, "Emoticons.json"); if(json!=null){ Log.e(TAG, json); RenRenData.mEmotionList = new DataProvider().resolve(json); } } private void findViewById() { ib_emotion = (ImageButton) findViewById(R.id.newsfeedpublish_emoticon); gv_emotion = (GridView) findViewById(R.id.newsfeedpublish_emoticons); et_content = (EditText) findViewById(R.id.newsfeedpublish_content); iv_publish = (ImageView) findViewById(R.id.newsfeedpublish_publish); } private void setListener() { ib_emotion.setOnClickListener(this); et_content.setOnClickListener(this); gv_emotion.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String emotion = RenRenData.mEmotionList.get(position).getEmotion(); if(et_content.getText().length()+emotion.length()<=140){ //长度小于140 // et_content.setText(et_content.getText().toString()+emotion); CharSequence ret = EmotionUtil.getInstance().replace(getApplicationContext(), et_content.getText().toString()+emotion); Log.e(TAG, "ret="+ret); et_content.setText(ret); } } }); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.newsfeedpublish_emoticon: if(gv_emotion.isShown()){ gv_emotion.setVisibility(View.GONE); ib_emotion.setImageResource(R.drawable.v5_0_1_publisher_emotion_button); }else{ gv_emotion.setVisibility(View.VISIBLE); ib_emotion.setImageResource(R.drawable.v5_0_1_publisher_pad_button); //隐藏输入法界面 InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } break; case R.id.newsfeedpublish_content: if(gv_emotion.isShown()){ gv_emotion.setVisibility(View.GONE); ib_emotion.setImageResource(R.drawable.v5_0_1_publisher_emotion_button); } break; default: break; } } private class EmotionAdapter extends BaseAdapter{ @Override public int getCount() { return RenRenData.mEmotionList.size(); } @Override public Object getItem(int position) { return RenRenData.mEmotionList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view; ViewHolder holder; if(convertView==null){ holder = new ViewHolder(); view = View.inflate(getApplicationContext(), R.layout.emotion_item, null); holder.iv_emotion = (ImageView) view.findViewById(R.id.emotcons_item_img); view.setTag(holder); }else{ view = convertView; holder = (ViewHolder) view.getTag(); } EmotionIcon em = RenRenData.mEmotionList.get(position); holder.iv_emotion.setImageBitmap(EmotionUtil.getInstance().getLocalEmotionIcon(em.getEmotion())); return view; } } public static class ViewHolder{ public ImageView iv_emotion; } }
EmotionUtil.java
package com.yulore.emotion; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Environment; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ImageSpan; public class EmotionUtil { public static final String DOWNLOAD_EMOTION_PATH = Environment .getExternalStorageDirectory().getAbsolutePath() + "/RenRenForAndroid/Emoticons/"; private static EmotionUtil instance = new EmotionUtil(); private EmotionUtil() { }; public static EmotionUtil getInstance() { return instance; } /** * 将文本中的表情符号替换为表情图片 * * @param text * 需要转换的字符 * @return 带有表情的字符 */ public CharSequence replace(Context context,CharSequence text) { try { SpannableStringBuilder builder = new SpannableStringBuilder(text); Pattern pattern = buildPattern(); Matcher matcher = pattern.matcher(text); while (matcher.find()) { Bitmap bitmap = getLocalEmotionIcon(matcher.group()); ImageSpan span = new ImageSpan(context, bitmap); builder.setSpan(span, matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } return builder; } catch (Exception e) { return text; } } /** * 正则表达式 * * @return */ private Pattern buildPattern() { /** * 查看表情名称数据是否存在,不存在则从本地读取Json,并解析 */ if(RenRenData.mEmotionList==null || RenRenData.mEmotionList.size()==0){ String json = EmotionUtil.getInstance().readFromFile(EmotionUtil.DOWNLOAD_EMOTION_PATH, "Emoticons.json"); if(json!=null){ RenRenData.mEmotionList = new DataProvider().resolve(json); } } StringBuilder patternString = new StringBuilder( RenRenData.mEmotionList.size() * 3); patternString.append('('); for (EmotionIcon result : RenRenData.mEmotionList) { String s = result.getEmotion(); patternString.append(Pattern.quote(s)); patternString.append('|'); } patternString.replace(patternString.length() - 1, patternString.length(), ")"); return Pattern.compile(patternString.toString()); } public String readFromFile(String filePath, String fileName) { if (fileName == null || "".equals(fileName)) { return null; } String ret = ""; if (Environment.MEDIA_MOUNTED.equals(Environment .getExternalStorageState())) { File dir = new File(filePath); if (dir == null || !dir.exists()) { dir.mkdirs(); } File targetFile = new File(filePath + fileName); try { if (targetFile == null || targetFile.exists()) { targetFile.createNewFile(); } InputStream in = new BufferedInputStream(new FileInputStream( targetFile)); BufferedReader br = new BufferedReader(new InputStreamReader( in, "UTF-8")); String tmp; while ((tmp = br.readLine()) != null) { ret += tmp; } br.close(); in.close(); } catch (IOException e) { e.printStackTrace(); } } return ret; } /** * 根据表情名称查找表情图片 * @param imageName * @return */ public Bitmap getEmotionIcon(String imageName) { File cacheDir = new File(DOWNLOAD_EMOTION_PATH); if (!cacheDir.exists()) { cacheDir.mkdirs(); } File[] cacheFiles = cacheDir.listFiles(); int i = 0; if (cacheFiles != null) { for (; i < cacheFiles.length; i++) { if (imageName.equals(cacheFiles[i].getName())) { break; } } } if (i < cacheFiles.length) { return BitmapFactory.decodeFile(DOWNLOAD_EMOTION_PATH + imageName); } return null; } /** * 从SD卡中根据表情符号获取表情图片 * * @param imageName * 表情的名称 * @return 表情的Bitmap */ public Bitmap getLocalEmotionIcon(String imageName) { File dir = new File(DOWNLOAD_EMOTION_PATH); if (!dir.exists()) { dir.mkdirs(); } File[] cacheFiles = dir.listFiles(); int index = 0; for (int i = 0; cacheFiles != null && i < cacheFiles.length; i++) { if (imageName.equals(cacheFiles[i].getName())) { index = i; break; } } Bitmap bitmap = null; if (index < cacheFiles.length) { /** * 因表情图片较小,则这里返回了一个60*60的Bitmap,该数值可根据情况调整 */ bitmap = Bitmap .createScaledBitmap( BitmapFactory.decodeFile(DOWNLOAD_EMOTION_PATH + imageName), 60, 60, true); } return bitmap; } /** * 将文本中的表情符号转换为表情图片 * * @param text * @return */ /*public CharSequence replace02(Context context, CharSequence text, int resId) { // SpannableString连续的字符串,长度不可变,同时可以附加一些object;可变的话使用SpannableStringBuilder,参考sdk文档 SpannableString ss = new SpannableString(text.toString() + "[smile]"); // 得到要显示图片的资源 Drawable d = context.getResources().getDrawable(resId); // 设置高度 d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); // 跨度底部应与周围文本的基线对齐 ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BASELINE); // 附加图片 ss.setSpan(span, text.length(), text.length() + "[smile]".length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); return ss; }*/ }