最近在一个项目中做到了表情这一块,就是一个简单的回复功能。类似于百度贴吧回复楼层的样式:废话不说先上图,有图有真相。
大概就是这么一个发送表情的功能,其实实现起来也不是很麻烦。下面说说具体实现。
一、布局文件:
由于此Demo中用到了百度贴吧的布局,不一一展示,只把最重要的布局展示出来,其佘的在代码中看。
1、activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/pb"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/backgroundcolor" >
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@id/layout_reply"
android:layout_below="@id/title" >
<ListView
android:id="@id/pb_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/backgroundcolor"
android:cacheColorHint="@android:color/transparent"
android:divider="@drawable/list_divider"
android:dividerHeight="2.0dip"
android:fadingEdge="vertical"
android:fadingEdgeLength="3.0dip"
android:listSelector="@drawable/list_selector"
android:smoothScrollbar="true" />
</FrameLayout>
<RelativeLayout
android:id="@id/title"
style="@style/title_margin"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_gravity="top"
android:background="@drawable/title_bg" >
<Button
android:id="@id/button_back"
style="@style/comm_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="@drawable/title_back"
android:text="返回" />
<TextView
android:id="@id/text_title"
style="@style/title_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center_vertical"
android:singleLine="true" />
<Button
android:id="@id/button_mark"
style="@style/comm_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@drawable/title_comm"
android:paddingLeft="5.0dip"
android:paddingRight="5.0dip"
android:text="添加书签" />
</RelativeLayout>
<LinearLayout
android:id="@id/layout_reply"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="@id/face_view"
android:background="@drawable/sub_pb_reply_bg"
android:orientation="horizontal"
android:paddingTop="2.0dip" >
<Button
android:id="@id/button_face"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10.0dip"
android:layout_marginRight="6.0dip"
android:background="@drawable/sub_pb_face" />
<EditText
android:id="@id/reply_content"
android:layout_width="0.0dip"
android:layout_height="32.0dip"
android:layout_gravity="center_vertical"
android:layout_weight="1.0"
android:background="@drawable/sub_pb_input_bg"
android:maxLength="140"
android:paddingLeft="7.0dip"
android:paddingRight="7.0dip"
android:singleLine="true"
android:textColorHint="#ffa5a6a8"
android:textSize="14.0sp" />
<Button
android:id="@id/reply_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="7.0dip"
android:layout_marginRight="7.0dip"
android:background="@drawable/sub_pb_reply"
android:gravity="center"
android:paddingLeft="10.0dip"
android:paddingRight="10.0dip"
android:text="发帖"
android:textColor="#ff383838"
android:textSize="14.0sp" />
</LinearLayout>
<View
android:id="@id/hold"
android:layout_width="1.0dip"
android:layout_height="1.0dip"
android:layout_alignParentBottom="true"
android:layout_marginBottom="-1.0dip" />
<GridView
android:layout_marginTop="2dip"
android:id="@id/face_view"
android:layout_width="fill_parent"
android:layout_height="220.0dip"
android:layout_above="@id/hold"
android:background="@drawable/write_face_bg"
android:columnWidth="50.0dip"
android:fadingEdge="none"
android:focusable="false"
android:gravity="center"
android:horizontalSpacing="5.0dip"
android:numColumns="auto_fit"
android:stretchMode="columnWidth"
android:verticalSpacing="5.0dip"
android:visibility="gone" />
</RelativeLayout>
对应的效果图:
2、表情条目的布局,即每一个表情图片的布局:brow_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="@+id/iv_brow"
android:layout_gravity="center"
android:layout_width="26dip"
android:layout_height="26dip"/>
</LinearLayout>
以上就是两个主要的布局文件,当然表情的Item条目也可以在代码中写,这就看个人习惯了。
二、代码:
1、首先我把整体思路说一下,表情功能分为本地显示和上传到服务器在页面中显示两部分。一般表情是服务器给的一个xml文件(你也可以在本地自己组织一个数据),里面有相应的表情名称和所对就的code码(也许不是这样的,大体就是这样),我们解析这个xml文件来获取其中的图片名称和code码,然后用反射把表情找出来和本地图片一一对应,而上传的时候只上传所对就的code码,服务器就会根据code码找到对应的表情图片。好了说的我都有点不清楚了,还是看代码吧。
2、包的结构:根据mvc思想我把包分成四部分,
我就用本地的一个xml文件来代替服务器返回的xml文件(当然你完全可以在本地自己组织数据)。
表情的xml文件brow.xml,我把它放在assets文件夹下
<?xml version="1.0" encoding="utf-8"?>
<brows>
<brow>
<code><![CDATA[[em:1:]]]></code> -->表情code码
<name>write_face_01</name> -->表情名字
</brow>
...
...
</brows>
(1)、把表情图片放在本地的res/drawable-hdpi目录 下
(2)、根据brow.xml的数据结构,我们定义一个bean Simle.java用来存放code码和名字
package com.demo.domain;
/**
* 用来封装表情的bean
*
*/
public class Smile {
private String code; //表情code码
private String name ; //表情名
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
(3)、解析表情的服务类:ParserBrowXml.java
package com.demo.engine;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import com.demo.domain.Smile;
import android.util.Xml;
/**
*解析表情的xml文件
*/
public class ParserBrowXml {
public static List<Smile> getInfo(InputStream inputStream){
XmlPullParser parser = Xml.newPullParser() ;
int eventType = 0;
List<Smile> smiles = null ;
Smile smile = null ;
try {
parser.setInput(inputStream, "UTF-8") ;
eventType = parser.getEventType();
while(eventType != XmlPullParser.END_DOCUMENT){
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
smiles = new ArrayList<Smile>() ;
break ;
case XmlPullParser.START_TAG:
if("brow".equals(parser.getName())){
smile = new Smile() ;
}else if("code".equals(parser.getName())){
smile.setCode(parser.nextText()) ;
}else if("name".equals(parser.getName())){
smile.setName(parser.nextText()) ;
}
break ;
case XmlPullParser.END_TAG:
if("brow".equals(parser.getName())){
smiles.add(smile) ;
smile = null ;
}
break;
default:
break;
}
eventType = parser.next() ;
}
} catch (Exception e) {
e.printStackTrace();
}
return smiles;
}
}
(4)、通用的工具类:CommUtils.java
package com.demo.untils;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.demo.domain.Smile;
import com.demo.simplereply.R;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.GridView;
import android.widget.SimpleAdapter;
/**
* 通用的工具类
*/
public class CommonUtil {
/**
* 生成表情的方法
* @param context 要传入的上下文
* @param smiles 表情集合
* @param gridView 要显示器的grildView
* @throws Exception 异常
*/
public static void addexpression(Context context,List<Smile> smiles,GridView gridView,InputStream inputStream) throws Exception {
// 通过反射把资源文件中的图片取出来放在GridView上
ArrayList<HashMap<String, Object>> lstImageItem = new ArrayList<HashMap<String, Object>>();
for (int i = 0; i < 50; i++) {
Smile smile = smiles.get(i);
if (smile != null) {
HashMap<String, Object> map = new HashMap<String, Object>();
Field f = (Field) R.drawable.class.getDeclaredField(smile
.getName());
int j = f.getInt(R.drawable.class);
map.put("ItemImage", j);// 添加图像资源的ID
lstImageItem.add(map);
}
}
// 生成适配器的ImageItem <====> 动态数组的元素,两者一一对应
SimpleAdapter saImageItems = new SimpleAdapter(context, lstImageItem,// 数据来源
R.layout.brow_item,
// 动态数组与ImageItem对应的子项
new String[] { "ItemImage" },
// ImageItem的XML文件里面的一个ImageView
new int[] { R.id.iv_brow });
gridView.setSelector(new ColorDrawable(Color.TRANSPARENT));//解决点击GridView背景变黑的情况
gridView.setAdapter(saImageItems);
}
/**
* 隐藏软键盘的方法
*
* @param context
* 要传入的上下文
*/
public static void hiddenSoft(Context context) {
// 取得输入方法的服务类
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
View view = ((Activity) context).getCurrentFocus();
if (view != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);// 隐藏软键盘
}
}
//快速显示键盘的方法
public static void ShowSoftFast(Context context) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
View view = ((Activity) context).getCurrentFocus();
if (view != null) {
imm.showSoftInput(view, 0); // 显示软键盘
}
}
}
(5)、主Activity界面调用
package com.demo.simplereply;
import java.io.InputStream;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.GridView;
import com.demo.domain.Smile;
import com.demo.engine.ParserBrowXml;
import com.demo.untils.CommonUtil;
public class MainActivity extends Activity implements OnClickListener{
private Button button_face = null ; //表情按钮
private GridView face_view = null ; //存放表情的GridView
private List<Smile> smiles = null ; //表情集合
private boolean isKeyBoard = false ; //标记是否是键盘
private EditText reply_content = null ; //文本输入框
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button_face = (Button) this.findViewById(R.id.button_face) ; //取得表情按钮组件
face_view = (GridView) this.findViewById(R.id.face_view) ; //取得放置表情的gridView组件
face_view.setOnItemClickListener(new OnItemClickListenerImpl()); //表情添加监听事件
button_face.setOnClickListener(this) ;
reply_content = (EditText) this.findViewById(R.id.reply_content) ;
reply_content.setOnTouchListener(ontouchlistener) ; //给文本输入框添加触摸事件
try {
InputStream inputStream = this.getResources().getAssets()
.open("brow.xml"); //取得assets中的borw.xml文件
smiles = ParserBrowXml.getInfo(inputStream); //解析borw.xml
CommonUtil.addexpression(this, smiles, face_view, inputStream);// 调用生情表情的方法
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button_face: //点击表情按钮
if(isKeyBoard){
button_face.setBackgroundResource(R.drawable.sub_pb_face) ; //键盘按钮变成表情按钮
face_view.setVisibility(View.GONE) ; //表情隐藏
CommonUtil.ShowSoftFast(this) ; //键盘显示出来
}else{
button_face.setBackgroundResource(R.drawable.sub_pb_keyboard) ; //表情按钮变成键盘按钮
face_view.setVisibility(View.VISIBLE) ; //表情显示出来
CommonUtil.hiddenSoft(this) ; //键盘隐藏
}
isKeyBoard = !isKeyBoard ;
break;
default:
break;
}
}
/**
* 触摸事件
*/
private OnTouchListener ontouchlistener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
face_view.setVisibility(View.GONE) ;//表情隐藏
button_face.setBackgroundResource(R.drawable.sub_pb_face) ;//不管是键盘还是表情都要变成表情按钮
isKeyBoard = false ; //让调用始终让调用表情按钮的else方法
return false;
}
};
// 点击表情的实现 类
private class OnItemClickListenerImpl implements OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
Smile smile = smiles.get(position);
int cursor = reply_content.getSelectionStart();
reply_content.getText().insert(cursor, smile.getCode() + " ");
}
}
}
以上就完成了一个含表情发送的回复功能。由于本人属于菜鸟级别的人,所以有什么地方不好的请自己研究再改善。