源码Demo下载
http://download.csdn.net/detail/u014620028/9609661
注:在Edittext和Textview中,不要加下面2个属性中的任何一种。否则,当行数大于1行以后会发生表情、图片对不齐的情况
android:lineSpacingExtra=""
android:lineSpacingMultiplier=""
效果图:
输入:
展示效果:
注:为了举例,把“我”当成关键字,“你”当成要凸显出来的字。比如搜索“你”,在结果中,把“你”字变红
把下面的代码,都复制到自己项目下,改改包名什么的,就可以直接用了
代码:
1、布局文件
activity_main_12:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:fitsSystemWindows="true"
android:orientation="vertical">
<EditText
android:id="@+id/et_word_message"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="8dp"
android:layout_marginTop="10dp"
android:background="#dedede"
android:gravity="top|left"
android:hint="输入内容"
android:maxLength="300"
android:textSize="20sp"
/>
<TextView
android:id="@+id/show_result_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/et_word_message"
android:layout_marginTop="5dp"
android:background="#00ff00"
android:gravity="center"
android:padding="5dp"
android:text="显示"
android:textSize="20sp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom"
android:orientation="vertical"
android:paddingBottom="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#dedede">
<ImageView
android:id="@+id/iv_expression"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_alignParentLeft="true"
android:layout_marginLeft="10dp"/>
<TextView
android:id="@+id/tv_show_residue_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:text=""
android:textColor="#999999"
android:textSize="17sp"
/>
</RelativeLayout>
<android.support.v4.view.ViewPager
android:id="@+id/show_emoji_viewPager"
android:layout_width="match_parent"
android:layout_height="180dp"
android:visibility="gone"/>
</LinearLayout>
</RelativeLayout>
emoji_gridview:
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gridView_Expression"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="#ffffff"
android:numColumns="7">
</GridView>
restul_layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp"
>
<com.chen.customviewdemo.view.MyTextView
android:id="@+id/result_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text=""
android:textSize="20sp"/>
</RelativeLayout>
工具等:
Expressions:
package com.chen.customviewdemo.view;
import com.chen.customviewdemo.R;
import java.util.ArrayList;
import java.util.List;
/**
* 处理表情
*/
public class Expressions {
public static List<int[]> emojiIdList = new ArrayList<int[]>();
public static List<String[]> emojiNameList = new ArrayList<String[]>();
public static final int[] emojID = new int[]{R.mipmap.emoji_0, R.mipmap.emoji_1, R.mipmap.emoji_2,
R.mipmap.emoji_3, R.mipmap.emoji_4, R.mipmap.emoji_5, R.mipmap.emoji_6, R.mipmap.emoji_7,
R.mipmap.emoji_8, R.mipmap.emoji_9, R.mipmap.emoji_10, R.mipmap.emoji_11, R.mipmap.emoji_12,
R.mipmap.emoji_13, R.mipmap.emoji_14, R.mipmap.emoji_15, R.mipmap.emoji_16, R.mipmap.emoji_17,
R.mipmap.emoji_18, R.mipmap.emoji_19, R.mipmap.emoji_20, R.mipmap.emoji_21, R.mipmap.emoji_22,
R.mipmap.emoji_23, R.mipmap.emoji_24, R.mipmap.emoji_25, R.mipmap.emoji_26, R.mipmap.emoji_27,
R.mipmap.emoji_28, R.mipmap.emoji_29, R.mipmap.emoji_30, R.mipmap.emoji_31, R.mipmap.emoji_32,
R.mipmap.emoji_33, R.mipmap.emoji_34, R.mipmap.emoji_35, R.mipmap.emoji_36, R.mipmap.emoji_37,
R.mipmap.emoji_38, R.mipmap.emoji_39, R.mipmap.emoji_40, R.mipmap.emoji_41, R.mipmap.emoji_42,
R.mipmap.emoji_43, R.mipmap.emoji_44, R.mipmap.emoji_45, R.mipmap.emoji_46, R.mipmap.emoji_47,
R.mipmap.emoji_48, R.mipmap.emoji_49, R.mipmap.emoji_50, R.mipmap.emoji_51, R.mipmap.emoji_52,
R.mipmap.emoji_53, R.mipmap.emoji_54, R.mipmap.emoji_55, R.mipmap.emoji_56, R.mipmap.emoji_57,
R.mipmap.emoji_58, R.mipmap.emoji_59, R.mipmap.emoji_60, R.mipmap.emoji_61, R.mipmap.emoji_62,
R.mipmap.emoji_63, R.mipmap.emoji_64, R.mipmap.emoji_65, R.mipmap.emoji_66,
R.mipmap.emoji_67, R.mipmap.emoji_68, R.mipmap.emoji_69, R.mipmap.emoji_70, R.mipmap.emoji_71,
R.mipmap.emoji_72, R.mipmap.emoji_73, R.mipmap.emoji_74, R.mipmap.emoji_75, R.mipmap.emoji_76,
R.mipmap.emoji_77, R.mipmap.emoji_78, R.mipmap.emoji_79, R.mipmap.emoji_80, R.mipmap.emoji_81,
R.mipmap.emoji_82, R.mipmap.emoji_83, R.mipmap.emoji_84, R.mipmap.emoji_85, R.mipmap.emoji_86,
R.mipmap.emoji_87, R.mipmap.emoji_88, R.mipmap.emoji_89, R.mipmap.emoji_90, R.mipmap.emoji_91,
R.mipmap.emoji_92, R.mipmap.emoji_93, R.mipmap.emoji_94, R.mipmap.emoji_95, R.mipmap.emoji_96,
R.mipmap.emoji_97, R.mipmap.emoji_98, R.mipmap.emoji_99, R.mipmap.emoji_100, R.mipmap.emoji_101,
R.mipmap.emoji_102, R.mipmap.emoji_103, R.mipmap.emoji_104};
public static final String[] emojName = new String[]{"[/微笑]", "[/撇嘴]", "[/色]", "[/发呆]",
"[/得意]", "[/流泪]", "[/害羞]", "[/闭嘴]", "[/睡]", "[/大哭]", "[/尴尬]", "[/发怒]",
"[/调皮]", "[/呲牙]", "[/惊讶]", "[/难过]", "[/酷]", "[/冷汗]", "[/抓狂]", "[/吐]",
"[/偷笑]", "[/可爱]", "[/白眼]", "[/傲慢]", "[/饥饿]", "[/困]", "[/惊恐]", "[/流汗]",
"[/憨笑]", "[/大兵]", "[/奋斗]", "[/咒骂]", "[/疑问]", "[/嘘...]", "[/晕]", "[/折磨]",
"[/衰]", "[/骷髅]", "[/敲打]", "[/再见]", "[/擦汗]", "[/抠鼻]", "[/鼓掌]", "[/糗大了]",
"[/坏笑]", "[/左哼哼]", "[/右哼哼]", "[/哈欠]", "[/鄙视]", "[/委屈]", "[/快哭了]", "[/阴险]",
"[/亲亲]", "[/吓]", "[/可怜]", "[/菜刀]", "[/西瓜]", "[/啤酒]", "[/篮球]", "[/乒乓]", "[/咖啡]",
"[/饭]", "[/猪头]", "[/玫瑰]", "[/凋谢]", "[/示爱]", "[/爱心]", "[/心碎]", "[/蛋糕]", "[/闪电]",
"[/炸弹]", "[/刀]", "[/足球]", "[/瓢虫]", "[/便便]", "[/月亮]", "[/太阳]", "[/礼物]", "[/拥抱]",
"[/强]", "[/弱]", "[/握手]", "[/胜利]", "[/抱拳]", "[/勾引]", "[/拳头]", "[/差劲]", "[/爱你]",
"[/NO]", "[/OK]", "[/爱情]", "[/飞吻]", "[/跳跳]", "[/发抖]", "[/怄火]", "[/转圈]", "[/磕头]",
"[/回头]", "[/跳绳]", "[/挥手]", "[/激动]", "[/街舞]", "[/献吻]", "[/左太极]", "[/右太极]"};
static {
emojiIdList.add(emojID);
emojiNameList.add(emojName);
}
/**
* 由表情名称获得表情图片id
*
* @param name
* @return
*/
public static int getIdAsName(String name) throws NullPointerException {
for (int i = 0; i < emojName.length; i++) {
if (emojName[i].equals(name)) {
return emojID[i];
}
}
return 0;
}
}
EmotionBean
package com.chen.customviewdemo.bean;
/**
* 表情bean
*/
public class EmotionBean {
private int Id; // 表情对应的Id
private String Name; // 表情对应的名字
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
}
KeyBean
package com.chen.customviewdemo.bean;
/**
* 关键字bean。
* 包含显式内容和隐式内容。
* 如:在界面上显示“百度”,点击了它,就去一个界面打开百度的网页。要打开网页,肯定需要Url。这就需要,在点击的时候,把Url传递过去。
* 这个时候,这个Url,就是隐式内容了。不必让用户看到,只是在触发后,隐含的做一定操作就行
*/
public class KeyBean {
public KeyBean(String content, String hide_content) {
this.content = content;
this.hide_content = hide_content;
}
/**
* 内容
*/
public String content;
/**
* 隐藏的内容,点击内容后,要传递的值或者要做的事
*/
public String hide_content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getHide_content() {
return hide_content;
}
public void setHide_content(String hide_content) {
this.hide_content = hide_content;
}
}
ExpressionUtil
package com.chen.customviewdemo.utils;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ImageSpan;
import com.chen.customviewdemo.view.Expressions;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ExpressionUtil {
public static SpannableString getSpannableString(String str, Context context, int size) {
SpannableString spannableString = new SpannableString(str);
String string = "\\[(.+?)\\]";
Pattern patten = Pattern.compile(string, Pattern.CASE_INSENSITIVE);
try {
dealSpannableString(context, spannableString, patten, 0, size);
} catch (Exception e) {
}
return spannableString;
}
private static void dealSpannableString(Context context, SpannableString spannableString, Pattern patten, int start, int emojiSize) {
Matcher matcher = patten.matcher(spannableString);
while (matcher.find()) {
String key = matcher.group();
if (matcher.start() < start) {
continue;
}
int id = Expressions.getIdAsName(key);
if (id != 0) {
Drawable drawable = context.getResources().getDrawable(id);
drawable.setBounds(0, 0, Utils.sp2px(context, emojiSize), Utils.sp2px(context, emojiSize));
ImageSpan imgSpan = new ImageSpan(drawable);
int end = matcher.start() + key.length();
spannableString.setSpan(imgSpan, matcher.start(), end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (end < spannableString.length()) { //如果整个字符串还未验证完,则继续。。
dealSpannableString(context, spannableString, patten, end, emojiSize);
}
break;
}
}
}
}
Utils
package com.chen.customviewdemo.utils;
import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import android.util.DisplayMetrics;
/**
* 工具类
*/
public class Utils {
/**
* 检查字符串是否是空
*
* @param str
* @return true:字符串为空
*/
public static boolean checkStringIsEmpty(String str) {
if (TextUtils.isEmpty(str) || "null".equals(str)) {
return true;
} else {
return false;
}
}
/**
* 将sp值转换为px值
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
}
CHEN
package com.chen.customviewdemo.utils;
/**
* 用于保存全局变量值
*/
public class CHEN {
/**
* 要被传递的文字
*/
public static String Str = "";
/**
* 要变红的字。一般用于搜索,如:把搜索的字变为红色用于突出显示
*/
public static String toRedWord = "";
/**
* 网址要被替换成的文字
*/
public static String REPLACEMENT_STRING = "*点击链接";
/**
* 匹配网址的正则表达式。以http://为例
*/
public static String urlRegex = "(http://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|([a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)";
/**
* 匹配网址的正则表达式。有http://、https://、ftp://这3中开头的
*/
// public static String urlRegex = "((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|([a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)";
}
EmotionGridViewAdapter
package com.chen.customviewdemo.adapter;
import android.app.Activity;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import com.chen.customviewdemo.R;
import com.chen.customviewdemo.bean.EmotionBean;
import com.chen.customviewdemo.view.Expressions;
import java.util.ArrayList;
import java.util.List;
/**
* 每一个表情页的adapter
*/
public class EmotionGridViewAdapter extends BaseAdapter {
private List<EmotionBean> list;
private Context context;
private String tag;
public EmotionGridViewAdapter(Context context, int page) {
this.context = context;
this.list = new ArrayList<EmotionBean>();
setData1(page);
}
private void setData1(int page) {
switch (page) {
case 0:
case 1:
case 2:
case 3:
case 4:
int len = 20; // 每页显示20个表情,一个删除键
for (int i = 0 + (len * page); i < len + (len * page); i++) {
EmotionBean bean = new EmotionBean();
bean.setId(Expressions.emojID[i]);
bean.setName(Expressions.emojName[i]);
list.add(bean);
}
EmotionBean bean = new EmotionBean();
bean.setId(R.mipmap.emoji_del);
list.add(bean);
break;
case 5:
// 最后一页,显示5个表情和一个删除键
for (int i = 100; i < 105; i++) {
EmotionBean bean1 = new EmotionBean();
bean1.setId(Expressions.emojID[i]);
bean1.setName(Expressions.emojName[i]);
list.add(bean1);
}
EmotionBean bean1 = new EmotionBean();
bean1.setId(R.mipmap.emoji_del);
list.add(bean1);
break;
}
}
public void setData(List<EmotionBean> list) {
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final EmotionBean bean = list.get(position);
int itemWidth = getWindowPX();
int padding = itemWidth / 6;
ImageView iv = new ImageView(context);
LayoutParams params = new LayoutParams(itemWidth, itemWidth);
iv.setLayoutParams(params);
iv.setScaleType(ScaleType.FIT_CENTER);
iv.setPadding(padding, 0, padding, 0);
iv.setImageResource(bean.getId());
return iv;
}
private int getWindowPX() {
Activity activity;
if (context instanceof Activity) {
activity = (Activity) context;
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
return width / 8;
} else {
return 200;
}
}
}
ShowEmojiViewPagerAdapter
package com.chen.customviewdemo.adapter;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;
import java.util.List;
/**
* 展示表情的viewpager的adapter
*/
public class ShowEmojiViewPagerAdapter extends PagerAdapter {
private List<GridView> mGridlist;
public ShowEmojiViewPagerAdapter(List<GridView> mList) {
super();
this.mGridlist = mList;
}
@Override
public int getCount() {
return mGridlist.size();
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0==arg1;
}
@Override
public void destroyItem(View container, int position, Object object) {
((ViewGroup)container).removeView(mGridlist.get(position));
}
@Override
public Object instantiateItem(View container, int position) {
((ViewGroup)container).addView(mGridlist.get(position));
return mGridlist.get(position);
}
}
MyTextView
package com.chen.customviewdemo.view;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.chen.customviewdemo.R;
import com.chen.customviewdemo.bean.KeyBean;
import com.chen.customviewdemo.utils.CHEN;
import com.chen.customviewdemo.utils.ExpressionUtil;
import com.chen.customviewdemo.utils.Utils;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 自定义textview,用于处理url、表情、文字等
*/
public class MyTextView extends TextView {
private Context context = null;
public MyTextView(Context context, AttributeSet attr) {
super(context, attr);
this.context = context;
}
public MyTextView(Context context) {
super(context);
this.context = context;
}
/**
* TextView显示有颜色的字,并添加点击事件,适用于一段文字中有多个内容要处理
*/
public void handleText(String str, final ArrayList<KeyBean> keyBeanList, int size) {
String content = str;
//处理匹配的url
Pattern p = Pattern.compile(CHEN.urlRegex);
Matcher m = p.matcher(content);
ArrayList<String> urlList = new ArrayList<String>();
while (m.find()) {
String urlStr = m.group();
if (urlStr.contains("http://")) {
//如果末尾有英文逗号或者中文逗号等,就去掉
while (urlStr.endsWith(",") || urlStr.endsWith(",") || urlStr.endsWith(".") || urlStr.endsWith("。") || urlStr.endsWith(";") || urlStr.endsWith(";") || urlStr.endsWith("!") || urlStr.endsWith("!") || urlStr.endsWith("?") || urlStr.endsWith("?")) {
urlStr = urlStr.substring(0, urlStr.length() - 1);
}
urlList.add(urlStr);
content = content.replace(urlStr, CHEN.REPLACEMENT_STRING);
}
}
final SpannableString spannableString = ExpressionUtil.getSpannableString(content, context, size);
content = spannableString.toString();
//处理链接
if (urlList.size() > 0) {
int urlStartNew = 0;
int urlStartOld = 0;
String urlTemp = content;
for (int i = 0; i < urlList.size(); i++) {
final String regexUrl = urlList.get(i);
spannableString.setSpan(new ClickableSpan() {
@Override
public void updateDrawState(TextPaint ds) {
// TODO Auto-generated method stub
super.updateDrawState(ds);
ds.setColor(context.getResources().getColor(R.color.linkcolor));
ds.setUnderlineText(false);
}
@Override
public void onClick(View widget) {
Toast.makeText(context, regexUrl, Toast.LENGTH_SHORT).show();
}
}, urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING), urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + CHEN.REPLACEMENT_STRING.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
try {
//“点击链接”前面的回形针图片。大小可自己调整
Drawable drawable = context.getResources().getDrawable(R.mipmap.web_link);
drawable.setBounds(0, 0, Utils.sp2px(context, size), Utils.sp2px(context, size));
ImageSpan imgSpan = new ImageSpan(drawable);
spannableString.setSpan(imgSpan, urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING), urlStartOld + urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception e) {
//异常以后,就不加小图片了
}
setText(spannableString);
setMovementMethod(LinkMovementMethod.getInstance());
urlStartNew = urlTemp.indexOf(CHEN.REPLACEMENT_STRING) + CHEN.REPLACEMENT_STRING.length();
urlStartOld += urlStartNew;
urlTemp = urlTemp.substring(urlStartNew);
}
}
//处理文本中的关键字
if (keyBeanList != null && keyBeanList.size() > 0) {
for (int i = 0; i < keyBeanList.size(); i++) {
final String data = keyBeanList.get(i).getContent();
final String hide_content = keyBeanList.get(i).getHide_content();
String temp = content;
int startNew = 0;
int startOld = 0;
if (temp.contains(data)) {
while (temp.contains(data)) {
spannableString.setSpan(new ClickableSpan() {
@Override
public void updateDrawState(TextPaint ds) {
// TODO Auto-generated method stub
super.updateDrawState(ds);
ds.setColor(context.getResources().getColor(R.color.linkcolor));
ds.setUnderlineText(false);
}
@Override
public void onClick(View widget) {
Toast.makeText(context, hide_content, Toast.LENGTH_SHORT).show();
}
}, startOld + temp.indexOf(data), startOld + temp.indexOf(data) + data.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
setText(spannableString);
setMovementMethod(LinkMovementMethod.getInstance());
startNew = temp.indexOf(data) + data.length();
startOld += startNew;
temp = temp.substring(startNew);
}
} else {
setText(spannableString);
}
}
} else {
setText(spannableString);
}
//处理要变红的字
if (!TextUtils.isEmpty(CHEN.toRedWord)) {
//只要把关键字变红就行了,不需要加点击事件。如果需要,可以自己加
String temp = content;
int startNew = 0;
int startOld = 0;
while (temp.contains(CHEN.toRedWord)) {
spannableString.setSpan(new ClickableSpan() {
@Override
public void updateDrawState(TextPaint ds) {
// TODO Auto-generated method stub
super.updateDrawState(ds);
ds.setColor(0xffff0000);
ds.setUnderlineText(false);
}
@Override
public void onClick(View widget) {
}
}, startOld + temp.indexOf(CHEN.toRedWord), startOld + temp.indexOf(CHEN.toRedWord) + CHEN.toRedWord.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
setText(spannableString);
setMovementMethod(LinkMovementMethod.getInstance());
startNew = temp.indexOf(CHEN.toRedWord) + CHEN.toRedWord.length();
startOld += startNew;
temp = temp.substring(startNew);
}
} else {
setText(spannableString);
}
}
}
Activity中使用
BaseActivity
package com.chen.customviewdemo;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
/**
* 基础Activity
*/
public abstract class BaseActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//不显示title
requestWindowFeature(Window.FEATURE_NO_TITLE);
initview();
}
abstract void initview();
}
MainActivity_12
package com.chen.customviewdemo;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.support.v4.view.ViewPager;
import android.text.Editable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import com.chen.customviewdemo.adapter.EmotionGridViewAdapter;
import com.chen.customviewdemo.adapter.ShowEmojiViewPagerAdapter;
import com.chen.customviewdemo.utils.CHEN;
import com.chen.customviewdemo.utils.ExpressionUtil;
import com.chen.customviewdemo.view.Expressions;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 输入文字和表情的界面
*/
public class MainActivity_12 extends BaseActivity implements View.OnClickListener {
private Context mContext;
// 表情
private ImageView iv_expression;
// 显示表情的ViewPager
private ViewPager show_emoji_viewPager;
// 装载表情的viewPager的Adapter
private ShowEmojiViewPagerAdapter showEmojiViewPagerAdapter;
/**
* 装载表情的viewPager的Adapter的每一页,都是一个gridview,这些gridview,用集合管理起来
*/
private List<GridView> mGridList;
/**
* 文本输入框
*/
private EditText et_word_message;
/**
* 当前是否显示表情键盘
*/
private boolean isShowEmotion = false;
private final String reg = "^[\u4e00-\u9fa5\u0020-\u007E\uFE30-\uFFA0。、……“”‘’《》——¥~]*$";
private Pattern pattern = Pattern.compile(reg);
/**
* 显示动态的剩余字数
*/
private TextView tv_show_residue_num;
/**
* 动态内容剩余字数。以300为例
*/
private int dynamicContentResidueNum = 300;
/**
* 展示输入结果的按钮
*/
private TextView show_result_tv;
/**
* 动态输入框的监听
*/
private TextWatcher watcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String dynicContent = s.toString();
int contentCount = dynicContent.length();
if (TextUtils.isEmpty(dynicContent) || contentCount < 250) {
tv_show_residue_num.setText("");
dynamicContentResidueNum = 300;
} else if (contentCount >= 250 && contentCount <= dynamicContentResidueNum) {
int tempCount = dynamicContentResidueNum - contentCount;
tv_show_residue_num.setText("还能输入" + tempCount + "字");
} else if (contentCount > dynamicContentResidueNum) {
tv_show_residue_num.setText(dynamicContentResidueNum - contentCount + "");
}
}
@Override
public void afterTextChanged(Editable s) {
}
};
@Override
void initview() {
setContentView(R.layout.activity_main_12);
mContext = this;
//默认。以此为例
CHEN.toRedWord = "你";
show_result_tv = (TextView) findViewById(R.id.show_result_tv);
mGridList = new ArrayList<GridView>();
mGridList.clear();
tv_show_residue_num = (TextView) findViewById(R.id.tv_show_residue_num);
iv_expression = (ImageView) findViewById(R.id.iv_expression);
iv_expression.setBackgroundResource(R.mipmap.biao_qing);
show_emoji_viewPager = (ViewPager) findViewById(R.id.show_emoji_viewPager);
initGridViews();
// 填充 表情的ViewPager的adapter
showEmojiViewPagerAdapter = new ShowEmojiViewPagerAdapter(mGridList);
show_emoji_viewPager.setAdapter(showEmojiViewPagerAdapter);
et_word_message = (EditText) findViewById(R.id.et_word_message);
et_word_message.addTextChangedListener(watcher);
iv_expression.setOnClickListener(this);
show_result_tv.setOnClickListener(this);
et_word_message.setOnClickListener(this);
}
/**
* 初始化表情键盘的Grideview
*/
private void initGridViews() {
mGridList = new ArrayList<GridView>();
LayoutInflater inflater = LayoutInflater.from(mContext);
mGridList.clear();
for (int i = 0; i < 6; i++) {
final int j = i;
GridView gridView = (GridView) inflater.inflate(
R.layout.emoji_gridview, null);
gridView.setAdapter(new EmotionGridViewAdapter(mContext, i));
mGridList.add(gridView);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
if (position == 20 || position == 40 || position == 60
|| position == 80 || position == 100
|| (j == 5 && position == 5)) {//处理删除键
int selectionStart = et_word_message.getSelectionStart();
String text = et_word_message.getText().toString();
if (!TextUtils.isEmpty(text)) {
String body = et_word_message.getText().toString();
String tempStr = body.substring(0, selectionStart);
int i = tempStr.lastIndexOf("]");// 获取最后一个表情的位置
if (i == tempStr.length() - 1) {// 说明光标刚好停在这个表情之后
int j = tempStr.lastIndexOf("[");// 将这两个之间的字符删掉
et_word_message.getEditableText().delete(j,
selectionStart);
} else {
Matcher matcher = pattern.matcher(text);
if (!matcher.matches()) {
new Thread(new Runnable() {
@Override
public void run() {
Instrumentation instrumentation = new Instrumentation();
instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DEL);
}
}) {
}.start();
} else {
et_word_message.getEditableText().delete(
tempStr.length() - 1, selectionStart);
}
}
}
} else {
//表情会被转换成字符串。防止最后输入一个表情,超出字数显示
if ((300 - et_word_message.getText().toString().trim().length()) < 5) {
return;
}
String emotionString = Expressions.emojName[position
+ j * 20];
String text = et_word_message.getText() + emotionString;
SpannableString spannableString = ExpressionUtil
.getSpannableString(text, mContext, 23);
et_word_message.setText(spannableString);
Editable b = et_word_message.getText();
et_word_message.setSelection(b.length());
}
}
});
}
showEmojiViewPagerAdapter = new ShowEmojiViewPagerAdapter(mGridList);
show_emoji_viewPager.setAdapter(showEmojiViewPagerAdapter);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.iv_expression:
// 点击表情的按钮,弹出表情
if (isShowEmotion) {
iv_expression.setBackgroundResource(R.mipmap.biao_qing);
show_emoji_viewPager.setVisibility(View.GONE);
showKeyBoard();
} else if (!isShowEmotion) {
iv_expression.setBackgroundResource(R.mipmap.keyboard);
show_emoji_viewPager.setVisibility(View.VISIBLE);
hideKeyBoard(v);
}
isShowEmotion = !isShowEmotion;
break;
case R.id.et_word_message:
if (isShowEmotion) {
iv_expression.setBackgroundResource(R.mipmap.biao_qing);
show_emoji_viewPager.setVisibility(View.GONE);
forceShowKeyBoard(et_word_message);
isShowEmotion = !isShowEmotion;
}
break;
case R.id.show_result_tv:
//去展示结果的界面
CHEN.Str = et_word_message.getText().toString().trim();
startActivity(new Intent(this, ShowWordAndEmojiDataActivity.class));
break;
default:
break;
}
}
/**
* 强制显示软键盘
*/
public void forceShowKeyBoard(View v) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(v, InputMethodManager.SHOW_FORCED);
}
/**
* 隐藏软键盘
* @param v
*/
public void hideKeyBoard(View v) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
/**
* 显示软键盘
*/
public void showKeyBoard() {
InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
}
}
ShowWordAndEmojiDataActivity
package com.chen.customviewdemo;
import com.chen.customviewdemo.bean.KeyBean;
import com.chen.customviewdemo.utils.CHEN;
import com.chen.customviewdemo.view.MyTextView;
import java.util.ArrayList;
/**
* 展示输入结果的Activity
*/
public class ShowWordAndEmojiDataActivity extends BaseActivity {
MyTextView result_tv;
ArrayList<KeyBean> keyBeanList;
@Override
void initview() {
setContentView(R.layout.restul_layout);
keyBeanList = new ArrayList<KeyBean>();
//还有关键字的话,可以继续加
keyBeanList.add(new KeyBean("我", "哈哈"));
result_tv = (MyTextView) findViewById(R.id.result_tv);
result_tv.handleText(CHEN.Str, keyBeanList, 23);
}
@Override
protected void onDestroy() {
super.onDestroy();
CHEN.toRedWord = "";
}
}
清单文件:
<activity
android:name=".MainActivity_12"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name=".ShowWordAndEmojiDataActivity"
android:screenOrientation="portrait"/>