如题,这是公司项目的一个功能模块,先上个效果图:

其次大致说说原理:

1,首先判断输入的字符,是否包含表情的文字,比如   这个表情对应的文件名为 emoji_1.png,它对应的文字描述 : [可爱],如果我们在输出的是输出这么一句话:老婆,我想你了。  那么我们对应的根本文字就是:老婆,我想你了[可爱]。

2,具体的转换过程就是用正则表达式比配文字中是否含有[xxx]这类的文字,如果有,那么我们就根据拿到的[xxx]找到它对应的资源文件id,当然这其中有一个关系表,看你怎么处理这个关系了。最后将其用SpannableString替换成文字,表面上显示有图片,其实TextView里的text依然是:老婆,我想你了[可爱]。这个过程明白么?

下面贴上DEMO工程的结构:

再贴上几个重要的类:

package com.example.facedemo;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.ImageSpan;
import android.util.Log;/*** ****************************************** * @author 廖乃波* @文件名称 : FaceConversionUtil.java* @创建时间 : 2013-1-27 下午02:34:09* @文件描述 : 表情轉換工具****************************************** */
public class FaceConversionUtil {/** 每一页表情的个数 */private int pageSize = 20;private static FaceConversionUtil mFaceConversionUtil;/** 保存于内存中的表情HashMap */private HashMap<String, String> emojiMap = new HashMap<String, String>();/** 保存于内存中的表情集合 */private List<ChatEmoji> emojis = new ArrayList<ChatEmoji>();/** 表情分页的结果集合 */public List<List<ChatEmoji>> emojiLists = new ArrayList<List<ChatEmoji>>();private FaceConversionUtil() {}public static FaceConversionUtil getInstace() {if (mFaceConversionUtil == null) {mFaceConversionUtil = new FaceConversionUtil();}return mFaceConversionUtil;}/*** 得到一个SpanableString对象,通过传入的字符串,并进行正则判断* * @param context* @param str* @return*/public SpannableString getExpressionString(Context context, String str) {SpannableString spannableString = new SpannableString(str);// 正则表达式比配字符串里是否含有表情,如: 我好[开心]啊String zhengze = "\\[[^\\]]+\\]";// 通过传入的正则表达式来生成一个patternPattern sinaPatten = Pattern.compile(zhengze, Pattern.CASE_INSENSITIVE);try {dealExpression(context, spannableString, sinaPatten, 0);} catch (Exception e) {Log.e("dealExpression", e.getMessage());}return spannableString;}/*** 添加表情* * @param context* @param imgId* @param spannableString* @return*/public SpannableString addFace(Context context, int imgId,String spannableString) {if (TextUtils.isEmpty(spannableString)) {return null;}Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),imgId);bitmap = Bitmap.createScaledBitmap(bitmap, 35, 35, true);ImageSpan imageSpan = new ImageSpan(context, bitmap);SpannableString spannable = new SpannableString(spannableString);spannable.setSpan(imageSpan, 0, spannableString.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);return spannable;}/*** 对spanableString进行正则判断,如果符合要求,则以表情图片代替* * @param context* @param spannableString* @param patten* @param start* @throws Exception*/private void dealExpression(Context context,SpannableString spannableString, Pattern patten, int start)throws Exception {Matcher matcher = patten.matcher(spannableString);while (matcher.find()) {String key = matcher.group();// 返回第一个字符的索引的文本匹配整个正则表达式,ture 则继续递归if (matcher.start() < start) {continue;}String value = emojiMap.get(key);if (TextUtils.isEmpty(value)) {continue;}int resId = context.getResources().getIdentifier(value, "drawable",context.getPackageName());// 通过上面匹配得到的字符串来生成图片资源id,下边的方法可用,但是你工程混淆的时候就有事了,你懂的。不是我介绍的重点// Field field=R.drawable.class.getDeclaredField(value);// int resId=Integer.parseInt(field.get(null).toString());if (resId != 0) {Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId);bitmap = Bitmap.createScaledBitmap(bitmap, 50, 50, true);// 通过图片资源id来得到bitmap,用一个ImageSpan来包装ImageSpan imageSpan = new ImageSpan(bitmap);// 计算该图片名字的长度,也就是要替换的字符串的长度int end = matcher.start() + key.length();// 将该图片替换字符串中规定的位置中spannableString.setSpan(imageSpan, matcher.start(), end,Spannable.SPAN_INCLUSIVE_EXCLUSIVE);if (end < spannableString.length()) {// 如果整个字符串还未验证完,则继续。。dealExpression(context, spannableString, patten, end);}break;}}}public void getFileText(Context context) {ParseData(FileUtils.getEmojiFile(context), context);}/*** 解析字符* * @param data*/private void ParseData(List<String> data, Context context) {if (data == null) {return;}ChatEmoji emojEentry;try {for (String str : data) {String[] text = str.split(",");String fileName = text[0].substring(0, text[0].lastIndexOf("."));emojiMap.put(text[1], fileName);int resID = context.getResources().getIdentifier(fileName,"drawable", context.getPackageName());if (resID != 0) {emojEentry = new ChatEmoji();emojEentry.setId(resID);emojEentry.setCharacter(text[1]);emojEentry.setFaceName(fileName);emojis.add(emojEentry);}}int pageCount = (int) Math.ceil(emojis.size() / 20 + 0.1);for (int i = 0; i < pageCount; i++) {emojiLists.add(getData(i));}} catch (Exception e) {e.printStackTrace();}}/*** 获取分页数据* * @param page* @return*/private List<ChatEmoji> getData(int page) {int startIndex = page * pageSize;int endIndex = startIndex + pageSize;if (endIndex > emojis.size()) {endIndex = emojis.size();}// 不这么写,会在viewpager加载中报集合操作异常,我也不知道为什么List<ChatEmoji> list = new ArrayList<ChatEmoji>();list.addAll(emojis.subList(startIndex, endIndex));if (list.size() < pageSize) {for (int i = list.size(); i < pageSize; i++) {ChatEmoji object = new ChatEmoji();list.add(object);}}if (list.size() == pageSize) {ChatEmoji object = new ChatEmoji();object.setId(R.drawable.face_del_icon);list.add(object);}return list;}
}

下边是表情布局,带输入框的,这样可以多个地方使用,就不不会使用太多多余代码。

package com.example.facedemo;import java.util.ArrayList;
import java.util.List;import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.text.SpannableString;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;/*** ******************************************* @author 廖乃波* @文件名称  :  FaceRelativeLayout.java* @创建时间  : 2013-1-27 下午02:34:17* @文件描述  : 带表情的自定义输入框*******************************************/
public class FaceRelativeLayout extends RelativeLayout implementsOnItemClickListener, OnClickListener {private Context context;/** 表情页的监听事件 */private OnCorpusSelectedListener mListener;/** 显示表情页的viewpager */private ViewPager vp_face;/** 表情页界面集合 */private ArrayList<View> pageViews;/** 游标显示布局 */private LinearLayout layout_point;/** 游标点集合 */private ArrayList<ImageView> pointViews;/** 表情集合 */private List<List<ChatEmoji>> emojis;/** 表情区域 */private View view;/** 输入框 */private EditText et_sendmessage;/** 表情数据填充器 */private List<FaceAdapter> faceAdapters;/** 当前表情页 */private int current = 0;public FaceRelativeLayout(Context context) {super(context);this.context = context;}public FaceRelativeLayout(Context context, AttributeSet attrs) {super(context, attrs);this.context = context;}public FaceRelativeLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);this.context = context;}public void setOnCorpusSelectedListener(OnCorpusSelectedListener listener) {mListener = listener;}/*** 表情选择监听* * @author naibo-liao* @时间: 2013-1-15下午04:32:54*/public interface OnCorpusSelectedListener {void onCorpusSelected(ChatEmoji emoji);void onCorpusDeleted();}@Overrideprotected void onFinishInflate() {super.onFinishInflate();emojis = FaceConversionUtil.getInstace().emojiLists;onCreate();}private void onCreate() {Init_View();Init_viewPager();Init_Point();Init_Data();}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_face:// 隐藏表情选择框if (view.getVisibility() == View.VISIBLE) {view.setVisibility(View.GONE);} else {view.setVisibility(View.VISIBLE);}break;case R.id.et_sendmessage:// 隐藏表情选择框if (view.getVisibility() == View.VISIBLE) {view.setVisibility(View.GONE);}break;}}/*** 隐藏表情选择框*/public boolean hideFaceView() {// 隐藏表情选择框if (view.getVisibility() == View.VISIBLE) {view.setVisibility(View.GONE);return true;}return false;}/*** 初始化控件*/private void Init_View() {vp_face = (ViewPager) findViewById(R.id.vp_contains);et_sendmessage = (EditText) findViewById(R.id.et_sendmessage);layout_point = (LinearLayout) findViewById(R.id.iv_image);et_sendmessage.setOnClickListener(this);findViewById(R.id.btn_face).setOnClickListener(this);view = findViewById(R.id.ll_facechoose);}/*** 初始化显示表情的viewpager*/private void Init_viewPager() {pageViews = new ArrayList<View>();// 左侧添加空页View nullView1 = new View(context);// 设置透明背景nullView1.setBackgroundColor(Color.TRANSPARENT);pageViews.add(nullView1);// 中间添加表情页faceAdapters = new ArrayList<FaceAdapter>();for (int i = 0; i < emojis.size(); i++) {GridView view = new GridView(context);FaceAdapter adapter = new FaceAdapter(context, emojis.get(i));view.setAdapter(adapter);faceAdapters.add(adapter);view.setOnItemClickListener(this);view.setNumColumns(7);view.setBackgroundColor(Color.TRANSPARENT);view.setHorizontalSpacing(1);view.setVerticalSpacing(1);view.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);view.setCacheColorHint(0);view.setPadding(5, 0, 5, 0);view.setSelector(new ColorDrawable(Color.TRANSPARENT));view.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));view.setGravity(Gravity.CENTER);pageViews.add(view);}// 右侧添加空页面View nullView2 = new View(context);// 设置透明背景nullView2.setBackgroundColor(Color.TRANSPARENT);pageViews.add(nullView2);}/*** 初始化游标*/private void Init_Point() {pointViews = new ArrayList<ImageView>();ImageView imageView;for (int i = 0; i < pageViews.size(); i++) {imageView = new ImageView(context);imageView.setBackgroundResource(R.drawable.d1);LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));layoutParams.leftMargin = 10;layoutParams.rightMargin = 10;layoutParams.width = 8;layoutParams.height = 8;layout_point.addView(imageView, layoutParams);if (i == 0 || i == pageViews.size() - 1) {imageView.setVisibility(View.GONE);}if (i == 1) {imageView.setBackgroundResource(R.drawable.d2);}pointViews.add(imageView);}}/*** 填充数据*/private void Init_Data() {vp_face.setAdapter(new ViewPagerAdapter(pageViews));vp_face.setCurrentItem(1);current = 0;vp_face.setOnPageChangeListener(new OnPageChangeListener() {@Overridepublic void onPageSelected(int arg0) {current = arg0 - 1;// 描绘分页点draw_Point(arg0);// 如果是第一屏或者是最后一屏禁止滑动,其实这里实现的是如果滑动的是第一屏则跳转至第二屏,如果是最后一屏则跳转到倒数第二屏.if (arg0 == pointViews.size() - 1 || arg0 == 0) {if (arg0 == 0) {vp_face.setCurrentItem(arg0 + 1);// 第二屏 会再次实现该回调方法实现跳转.pointViews.get(1).setBackgroundResource(R.drawable.d2);} else {vp_face.setCurrentItem(arg0 - 1);// 倒数第二屏pointViews.get(arg0 - 1).setBackgroundResource(R.drawable.d2);}}}@Overridepublic void onPageScrolled(int arg0, float arg1, int arg2) {}@Overridepublic void onPageScrollStateChanged(int arg0) {}});}/*** 绘制游标背景*/public void draw_Point(int index) {for (int i = 1; i < pointViews.size(); i++) {if (index == i) {pointViews.get(i).setBackgroundResource(R.drawable.d2);} else {pointViews.get(i).setBackgroundResource(R.drawable.d1);}}}@Overridepublic void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {ChatEmoji emoji = (ChatEmoji) faceAdapters.get(current).getItem(arg2);if (emoji.getId() == R.drawable.face_del_icon) {int selection = et_sendmessage.getSelectionStart();String text = et_sendmessage.getText().toString();if (selection > 0) {String text2 = text.substring(selection - 1);if ("]".equals(text2)) {int start = text.lastIndexOf("[");int end = selection;et_sendmessage.getText().delete(start, end);return;}et_sendmessage.getText().delete(selection - 1, selection);}}if (!TextUtils.isEmpty(emoji.getCharacter())) {if (mListener != null)mListener.onCorpusSelected(emoji);SpannableString spannableString = FaceConversionUtil.getInstace().addFace(getContext(), emoji.getId(), emoji.getCharacter());et_sendmessage.append(spannableString);}}
}

接下来是聊天数据填充器的

package com.example.facedemo;import android.content.Context;import android.text.SpannableString;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;import android.widget.BaseAdapter;
import android.widget.TextView;import java.util.List;/*** ******************************************* @author 廖乃波* @文件名称  :  ChatMsgAdapter.java* @创建时间  : 2013-1-27 下午02:33:16* @文件描述  : 消息数据填充起*******************************************/
public class ChatMsgAdapter extends BaseAdapter {public static interface IMsgViewType {int IMVT_COM_MSG = 0;int IMVT_TO_MSG = 1;}private List<ChatMsgEntity> coll;private LayoutInflater mInflater;private Context context;public ChatMsgAdapter(Context context, List<ChatMsgEntity> coll) {this.coll = coll;mInflater = LayoutInflater.from(context);this.context = context;}public int getCount() {return coll.size();}public Object getItem(int position) {return coll.get(position);}public long getItemId(int position) {return position;}public int getItemViewType(int position) {ChatMsgEntity entity = coll.get(position);if (entity.getMsgType()) {return IMsgViewType.IMVT_COM_MSG;} else {return IMsgViewType.IMVT_TO_MSG;}}public int getViewTypeCount() {return 2;}public View getView(int position, View convertView, ViewGroup parent) {ChatMsgEntity entity = coll.get(position);boolean isComMsg = entity.getMsgType();ViewHolder viewHolder = null;if (convertView == null) {if (isComMsg) {convertView = mInflater.inflate(R.layout.chatting_item_msg_text_left, null);} else {convertView = mInflater.inflate(R.layout.chatting_item_msg_text_right, null);}viewHolder = new ViewHolder();viewHolder.tvSendTime = (TextView) convertView.findViewById(R.id.tv_sendtime);viewHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_chatcontent);viewHolder.isComMsg = isComMsg;convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}viewHolder.tvSendTime.setText(entity.getDate());SpannableString spannableString = FaceConversionUtil.getInstace().getExpressionString(context, entity.getText());viewHolder.tvContent.setText(spannableString);return convertView;}class ViewHolder {public TextView tvSendTime;public TextView tvContent;public boolean isComMsg = true;}}

最开始要读取的表情配置文件

package com.example.facedemo;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;import android.content.Context;/*** ******************************************* @author 廖乃波* @文件名称  :  FileUtils.java* @创建时间   : 2013-1-27 下午02:35:09* @文件描述  : 文件工具类*******************************************/
public class FileUtils {/*** 读取表情配置文件* * @param context* @return*/public static List<String> getEmojiFile(Context context) {try {List<String> list = new ArrayList<String>();InputStream in = context.getResources().getAssets().open("emoji");BufferedReader br = new BufferedReader(new InputStreamReader(in,"UTF-8"));String str = null;while ((str = br.readLine()) != null) {list.add(str);}return list;} catch (IOException e) {e.printStackTrace();}return null;}
}

下边这个是表情翻页的数据填充,用的是viewpager,每一页填充的是一个gridview

package com.example.facedemo;import java.util.List;import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
/*** ******************************************* @author 廖乃波* @文件名称   :  ViewPagerAdapter.java* @创建时间    : 2013-1-27 下午02:35:27* @文件描述  : ViewPager 数据填充器,切记做其他操作!!!只填充View!!!!*******************************************/
public class ViewPagerAdapter extends PagerAdapter {private List<View> pageViews;public ViewPagerAdapter(List<View> pageViews) {super();this.pageViews=pageViews;}// 显示数目@Overridepublic int getCount() {return pageViews.size();}@Overridepublic boolean isViewFromObject(View arg0, Object arg1) {return arg0 == arg1;}@Overridepublic int getItemPosition(Object object) {return super.getItemPosition(object);}@Overridepublic void destroyItem(View arg0, int arg1, Object arg2) {((ViewPager)arg0).removeView(pageViews.get(arg1));}/**** 获取每一个item�?类于listview中的getview*/@Overridepublic Object instantiateItem(View arg0, int arg1) {((ViewPager)arg0).addView(pageViews.get(arg1));return pageViews.get(arg1);}
}

最后呢,是表情的配置文件,你想怎么搞都行,我就这么搞的

emoji_1.png,[可爱]
emoji_2.png,[笑脸]
emoji_3.png,[囧]
emoji_4.png,[生气]
emoji_5.png,[鬼脸]
emoji_6.png,[花心]
emoji_7.png,[害怕]
emoji_8.png,[我汗]
emoji_9.png,[尴尬]
emoji_10.png,[哼哼]
emoji_11.png,[忧郁]
emoji_12.png,[呲牙]
emoji_13.png,[媚眼]
emoji_14.png,[累]
emoji_15.png,[苦逼]
emoji_16.png,[瞌睡]
emoji_17.png,[哎呀]
emoji_18.png,[刺瞎]
emoji_19.png,[哭]
emoji_20.png,[激动]
emoji_21.png,[难过]
emoji_22.png,[害羞]
emoji_23.png,[高兴]
emoji_24.png,[愤怒]
emoji_25.png,[亲]
emoji_26.png,[飞吻]
emoji_27.png,[得意]
emoji_28.png,[惊恐]
emoji_29.png,[口罩]
emoji_30.png,[惊讶]
emoji_31.png,[委屈]
emoji_32.png,[生病]
emoji_33.png,[红心]
emoji_34.png,[心碎]
emoji_35.png,[玫瑰]
emoji_36.png,[花]
emoji_37.png,[外星人]
emoji_38.png,[金牛座]
emoji_39.png,[双子座]
emoji_40.png,[巨蟹座]
emoji_41.png,[狮子座]
emoji_42.png,[处女座]
emoji_43.png,[天平座]
emoji_44.png,[天蝎座]
emoji_45.png,[射手座]
emoji_46.png,[摩羯座]
emoji_47.png,[水瓶座]
emoji_48.png,[白羊座]
emoji_49.png,[双鱼座]
emoji_50.png,[星座]
emoji_51.png,[男孩]
emoji_52.png,[女孩]
emoji_53.png,[嘴唇]
emoji_54.png,[爸爸]
emoji_55.png,[妈妈]
emoji_56.png,[衣服]
emoji_57.png,[皮鞋]
emoji_58.png,[照相]
emoji_59.png,[电话]
emoji_60.png,[石头]
emoji_61.png,[胜利]
emoji_62.png,[禁止]
emoji_63.png,[滑雪]
emoji_64.png,[高尔夫]
emoji_65.png,[网球]
emoji_66.png,[棒球]
emoji_67.png,[冲浪]
emoji_68.png,[足球]
emoji_69.png,[小鱼]
emoji_70.png,[问号]
emoji_71.png,[叹号]
emoji_179.png,[顶]
emoji_180.png,[写字]
emoji_181.png,[衬衫]
emoji_182.png,[小花]
emoji_183.png,[郁金香]
emoji_184.png,[向日葵]
emoji_185.png,[鲜花]
emoji_186.png,[椰树]
emoji_187.png,[仙人掌]
emoji_188.png,[气球]
emoji_189.png,[炸弹]
emoji_190.png,[喝彩]
emoji_191.png,[剪子]
emoji_192.png,[蝴蝶结]
emoji_193.png,[机密]
emoji_194.png,[铃声]
emoji_195.png,[女帽]
emoji_196.png,[裙子]
emoji_197.png,[理发店]
emoji_198.png,[和服]
emoji_199.png,[比基尼]
emoji_200.png,[拎包]
emoji_201.png,[拍摄]
emoji_202.png,[铃铛]
emoji_203.png,[音乐]
emoji_204.png,[心星]
emoji_205.png,[粉心]
emoji_206.png,[丘比特]
emoji_207.png,[吹气]
emoji_208.png,[口水]
emoji_209.png,[对]
emoji_210.png,[错]
emoji_211.png,[绿茶]
emoji_212.png,[面包]
emoji_213.png,[面条]
emoji_214.png,[咖喱饭]
emoji_215.png,[饭团]
emoji_216.png,[麻辣烫]
emoji_217.png,[寿司]
emoji_218.png,[苹果]
emoji_219.png,[橙子]
emoji_220.png,[草莓]
emoji_221.png,[西瓜]
emoji_222.png,[柿子]
emoji_223.png,[眼睛]
emoji_224.png,[好的]

忘了布局文件,哇哈哈

<?xml version="1.0" encoding="utf-8"?>
<com.example.facedemo.FaceRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/FaceRelativeLayout"android:layout_width="fill_parent"android:layout_height="wrap_content" ><RelativeLayoutandroid:id="@+id/rl_input"android:layout_width="fill_parent"android:layout_height="wrap_content"android:background="@drawable/chat_footer_bg" ><ImageButtonandroid:id="@+id/btn_face"android:layout_width="40dip"android:layout_height="40dip"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:layout_marginLeft="8dip"android:background="@drawable/chat_send_btn"android:src="@drawable/ib_face" /><Buttonandroid:id="@+id/btn_send"android:layout_width="60dp"android:layout_height="40dp"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginRight="10dp"android:background="@drawable/chat_send_btn"android:text="发送" /><EditTextandroid:id="@+id/et_sendmessage"android:layout_width="fill_parent"android:layout_height="40dp"android:layout_centerVertical="true"android:layout_marginLeft="8dp"android:layout_marginRight="10dp"android:layout_toLeftOf="@id/btn_send"android:layout_toRightOf="@id/btn_face"android:background="@drawable/login_edit_normal"android:singleLine="true"android:textSize="18sp" /></RelativeLayout><RelativeLayoutandroid:id="@+id/ll_facechoose"android:layout_width="fill_parent"android:layout_height="124dip"android:layout_below="@id/rl_input"android:background="#f6f5f5"android:visibility="gone" ><android.support.v4.view.ViewPagerandroid:id="@+id/vp_contains"android:layout_width="match_parent"android:layout_height="match_parent" ></android.support.v4.view.ViewPager><LinearLayoutandroid:id="@+id/iv_image"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_marginBottom="6dip"android:gravity="center"android:orientation="horizontal" ></LinearLayout></RelativeLayout></com.example.facedemo.FaceRelativeLayout>

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  源码 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Android UI【android 仿微信、QQ聊天,带表情,可翻页,带翻页拖动缓冲】相关推荐

  1. android标题栏不被顶上去,Android仿微信QQ聊天顶起输入法不顶起标题栏的问题

    在这记录一下输入法弹出的一系列问题,有的输入法弹出就把整个布局弹上去,有的输入法弹出布局不会有变化,有的输入法弹出遮盖输入框等等问题,网上也有很多说加着加那的,但是看一下都不是很完整,解决不了所有问题 ...

  2. Android之高仿手机QQ聊天

    源代码下载 转载请注明出处,谢谢! 最终版已上传.优化下拉刷新.增加来消息声音提示.主界面改成ViewPager,实现左右滑动.新增群组.最近会话显示条数,开始上班了,不再修改了.谢谢! 国庆这几天, ...

  3. Android仿微信气泡聊天界面设计

    Android仿微信气泡聊天界面设计 微信的气泡聊天是仿iPhone自带短信而设计出来的,不过感觉还不错可以尝试一下仿着微信的气泡聊天做一个Demo,给大家分享一下!效果图如下: 气泡聊天最终要的是素 ...

  4. android 仿微信头像裁剪,Android仿微信QQ设置图形头像裁剪功能

    最近在做毕业设计,想有一个功能和QQ一样可以裁剪头像并设置圆形头像,额,这是设计狮的一种潮流. 而纵观现在主流的APP,只要有用户系统这个功能,这个需求一般都是在(bu)劫(de)难(bu)逃(xue ...

  5. android高仿微信聊天页面,Android 高仿微信语音聊天页面高斯模糊(毛玻璃效果)

    目前的应用市场上,使用毛玻璃效果的APP随处可见,比如用过微信语音聊天的人可以发现,语音聊天页面就使用了高斯模糊效果. 先看下效果图: 仔细观察上图,我们可以发现,背景图以用户头像为模板,对其进行了高 ...

  6. android 仿微信语音聊天

    android 仿微信语音聊天 跟着imooc老师学习 代码地址: https://github.com/tingsky9985/Weixin_Recorder

  7. Android仿微信语音聊天界面设计

    这篇文章主要为大家详细介绍了Android仿微信语音聊天界面设计代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 有段时间没有看视频了,昨天晚上抽了点空时间,又看了下鸿洋大神的视频教程,又抽时间 ...

  8. Android开源之仿微信UI

     Android开源之仿微信UI 这是一个仿Android版本的微信UI开源项目,该项目实现了微信表面的UI编码设计与实现,尚未实现功能部分.实现了部分页面的跳转逻辑,比如初始页进入主界面.git ...

  9. android开发百度地图坐标偏差,利用百度地图Android sdk高仿微信发送位置功能及遇到的问题...

    接触了百度地图开发平台半个月了,这2天试着模仿了微信给好友发送位置功能,对百度地图的操作能力又上了一个台阶 我在实现这个功能的时候,遇到一些困难,可能也是别人将会遇到的困难,特在此列出 1.在微信发送 ...

  10. 使用Android辅助功能AccessibilityService实现微信自动聊天【外挂插件】

    本文是使用Android辅助功能AccessibilityService实现微信自动聊天demo: 只是为了跟深入的了解Android辅助功能, 提高自身的动手能力. 请勿用于商用,或非法用途. 动手 ...

最新文章

  1. statspack系列8
  2. 如何调用华为云api_postman调用华为云接口添加资源
  3. 开放大学计算机应用基础形考答案,国家开放大学计算机应用基础形考作业二答案~.doc...
  4. html纵向文本,html – 垂直对齐CSS圈中多行的文本
  5. Jedis与Redisson选型对比
  6. [转]Mysql Join语法解析与性能分析
  7. 阿里云 centos mysql_在阿里云的CentOS环境中安装配置MySQL的教程
  8. SecureCRT防止自动断开
  9. C语言成为大学必修课!想要不挂科?你只需要这样做
  10. 【英语学习】【Level 07】U04 Rest and Relaxation L2 A rest stop with everything
  11. UI历练素材|成功,少不了的临摹进阶
  12. 使用JDBC编程的问题总结
  13. python译为中文_Python中文手册(汉译)
  14. 移动硬盘显示要格式化怎么办?
  15. ios 凭据验证_苹果内购服务器验证凭证回执Data
  16. MySQL数据库框架
  17. c语言300行代码大作业,C语言300行代码
  18. linux+synaptics+驱动程序,Linux下Synaptics笔记本触摸板的配置
  19. CentOS8 安装epel 使用阿里云镜像、更换国内源(阿里源)[转]
  20. 基于wpa_supplicant库的WIFI连接功能实现--wpa_cli命令解析

热门文章

  1. linux使用iscsi配置共享存储
  2. MySQL导入mdx_一个简单的MDX案例及说明 (转)
  3. Win11无法删除文件夹怎么办?Win11无法删除文件夹的解决方法
  4. 三个月考研英语复习法
  5. There is no getter for property named 'distinct' in 'class cn.ly.item.pojo.SpciGroup'] with root cau
  6. 系统分析师考试论文案例集
  7. kubernetes 用dockerfile 创建jenkins容器
  8. 会计学原理学习笔记——第一章——总论(1.5会计目标)
  9. 数学建模—投资组合问题
  10. 对比欧氏距离与余弦相似度