【OSC手机App技术解析】- 编辑框插入表情图片
众所周知,APP应用中带有表情功能,可以更好的提高用户体验。OSChina.NET Android版客户端v1.6 也加入表情功能,借此机会也给大家分享一下Android的编辑框是如何插入表情图片的,欢迎大家一起交流学习。
首先,把整理好的表情图片以及布局用到的一些图片导入到项目的res/drawable目录中。
然后,编辑res/layout目录下布局.xml文件,这里我把oschina客户端的布局代码贴上来,供大家参考: tweet_pub.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:orientation="vertical" android:background="@color/white"> <FrameLayout android:id="@+id/tweet_pub_form" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1"> <EditText android:id="@+id/tweet_pub_content" android:layout_width="fill_parent" android:layout_height="fill_parent" android:autoLink="web" android:gravity="top" android:hint="请输入动弹内容" android:background="@null"/> <ImageView android:id="@+id/tweet_pub_image" android:layout_width="60.0dip" android:layout_height="45.0dip" android:layout_marginLeft="5.0dip" android:layout_marginBottom="5.0dip" android:layout_gravity="left|bottom" android:clickable="true" android:visibility="gone"/> <LinearLayout android:id="@+id/tweet_pub_clearwords" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="5.0dip" android:layout_marginBottom="5.0dip" android:layout_gravity="right|bottom" android:gravity="center" android:background="@drawable/clearwords_bg" android:clickable="true"> <TextView android:id="@+id/tweet_pub_numberwords" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@color/black" android:text="160"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="3dip" android:src="@drawable/clearwords_icon"/> </LinearLayout> </FrameLayout> <include layout="@layout/tweet_pub_footer"/> </LinearLayout>
tweet_pub_footer.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content"> <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:background="@drawable/widget_bar_bg"> <ImageView android:id="@+id/tweet_pub_footbar_face" style="@style/main_footbar_image" android:src="@drawable/widget_bar_face"/> <ImageView style="@style/main_footbar_cutline" android:src="@drawable/widget_bar_cut_off"/> <ImageView android:id="@+id/tweet_pub_footbar_photo" style="@style/main_footbar_image" android:src="@drawable/widget_bar_photo"/> <ImageView style="@style/main_footbar_cutline" android:src="@drawable/widget_bar_cut_off"/> <ImageView android:id="@+id/tweet_pub_footbar_atme" style="@style/main_footbar_image" android:src="@drawable/widget_bar_atme"/> <ImageView style="@style/main_footbar_cutline" android:src="@drawable/widget_bar_cut_off"/> <ImageView android:id="@+id/tweet_pub_footbar_software" style="@style/main_footbar_image" android:src="@drawable/widget_bar_soft"/> </LinearLayout> <GridView android:id="@+id/tweet_pub_faces" android:layout_width="fill_parent" android:layout_height="220dip" android:numColumns="auto_fit" android:columnWidth="50dip" android:stretchMode="columnWidth" android:gravity="center" android:fadingEdge="none" android:scrollingCache="false" android:background="@color/face_bg" android:visibility="gone"/> </LinearLayout>
通过上面布局代码可以看出,我把整个编辑界面分成了2个.xml文件。主布局文件tweet_pub.xml通过<include>标签把底部工具栏tweet_pub_footer.xml加载进视图。这样做的好处是把一个较复杂的布局细分成几个小布局,让布局更清晰,维护起来更加方便。
接下来,创建一个Activity类,并在AndroidManifest.xml中注册,对该Activity添加一个属性:
android:windowSoftInputMode="stateVisible|adjustResize"
该属性在Activity启动显示的时候,软键盘也自动弹出显示,这样方便用户可直接对EditText编辑框进行输入文字操作。
下面贴出Activity的完整代码:
public class MainActivity extends Activity { private EditText mContent; private ImageView mFace; private LinearLayout mClearwords; private TextView mNumberwords; private GridView mGridView; private GridViewFaceAdapter mGVFaceAdapter; private InputMethodManager imm; private static final int MAX_TEXT_LENGTH = 160;//最大输入字数 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tweet_pub); //软键盘管理类 imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE); //初始化基本视图 this.initView(); //初始化表情视图 this.initGridView(); } //初始化视图控件 private void initView() { mContent = (EditText)findViewById(R.id.tweet_pub_content); mFace = (ImageView)findViewById(R.id.tweet_pub_footbar_face); mClearwords = (LinearLayout)findViewById(R.id.tweet_pub_clearwords); mNumberwords = (TextView)findViewById(R.id.tweet_pub_numberwords); //设置控件点击事件 mFace.setOnClickListener(faceClickListener); mClearwords.setOnClickListener(clearwordsClickListener); //编辑器添加文本监听 mContent.addTextChangedListener(new TextWatcher() { public void onTextChanged(CharSequence s, int start, int before, int count) { //显示剩余可输入的字数 mNumberwords.setText((MAX_TEXT_LENGTH - s.length()) + ""); } public void beforeTextChanged(CharSequence s, int start, int count, int after) {} public void afterTextChanged(Editable s) {} }); //编辑器点击事件 mContent.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { //显示软键盘 showIMM(); } }); //设置最大输入字数 InputFilter[] filters = new InputFilter[1]; filters[0] = new InputFilter.LengthFilter(MAX_TEXT_LENGTH); mContent.setFilters(filters); } //初始化表情控件 private void initGridView() { mGVFaceAdapter = new GridViewFaceAdapter(this); mGridView = (GridView)findViewById(R.id.tweet_pub_faces); mGridView.setAdapter(mGVFaceAdapter); mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener(){ public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //插入的表情 SpannableString ss = new SpannableString(view.getTag().toString()); Drawable d = getResources().getDrawable((int)mGVFaceAdapter.getItemId(position)); d.setBounds(0, 0, 35, 35);//设置表情图片的显示大小 ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BOTTOM); ss.setSpan(span, 0, view.getTag().toString().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); //在光标所在处插入表情 mContent.getText().insert(mContent.getSelectionStart(), ss); } }); } private void showIMM() { mFace.setTag(1); showOrHideIMM(); } private void showFace() { mFace.setImageResource(R.drawable.widget_bar_keyboard); mFace.setTag(1); mGridView.setVisibility(View.VISIBLE); } private void hideFace() { mFace.setImageResource(R.drawable.widget_bar_face); mFace.setTag(null); mGridView.setVisibility(View.GONE); } private void showOrHideIMM() { if(mFace.getTag() == null){ //隐藏软键盘 imm.hideSoftInputFromWindow(mFace.getWindowToken(), 0); //显示表情 showFace(); }else{ //显示软键盘 imm.showSoftInput(mContent, 0); //隐藏表情 hideFace(); } } //表情控件点击事件 private View.OnClickListener faceClickListener = new View.OnClickListener() { public void onClick(View v) { showOrHideIMM(); } }; //清除控件点击事件 private View.OnClickListener clearwordsClickListener = new View.OnClickListener() { public void onClick(View v) { String content = mContent.getText().toString(); if(content != ""){ AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext()); builder.setTitle("清除文字吗?"); builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); //清除文字 mContent.setText(""); mNumberwords.setText(String.valueOf(MAX_TEXT_LENGTH)); } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); builder.show(); } } }; }
这里说明下,GridViewFaceAdapter类是我自定义的适配器类,继承了BaseAdapter。下面我也将该类的完整代码贴出来,给大家参考:
public class GridViewFaceAdapter extends BaseAdapter { // 定义Context private Context mContext; // 定义整型数组 即图片源 private int[] mImageIds; public GridViewFaceAdapter(Context c) { mContext = c; mImageIds = new int[]{ R.drawable.f001,R.drawable.f002,R.drawable.f003,R.drawable.f004,R.drawable.f005,R.drawable.f006, R.drawable.f007,R.drawable.f008,R.drawable.f009,R.drawable.f010,R.drawable.f011,R.drawable.f012, R.drawable.f013,R.drawable.f014,R.drawable.f015,R.drawable.f016,R.drawable.f017,R.drawable.f018, R.drawable.f019,R.drawable.f020,R.drawable.f021,R.drawable.f022,R.drawable.f023,R.drawable.f024, R.drawable.f025,R.drawable.f026,R.drawable.f027,R.drawable.f028,R.drawable.f029,R.drawable.f030, R.drawable.f031,R.drawable.f032,R.drawable.f033,R.drawable.f034,R.drawable.f035,R.drawable.f036, R.drawable.f037,R.drawable.f038,R.drawable.f039,R.drawable.f040,R.drawable.f041,R.drawable.f042, R.drawable.f043,R.drawable.f044,R.drawable.f045,R.drawable.f046,R.drawable.f047,R.drawable.f048, R.drawable.f049,R.drawable.f050,R.drawable.f051,R.drawable.f052,R.drawable.f053,R.drawable.f054, R.drawable.f055,R.drawable.f056,R.drawable.f057,R.drawable.f058,R.drawable.f059,R.drawable.f060, R.drawable.f061,R.drawable.f062,R.drawable.f063,R.drawable.f064,R.drawable.f065,R.drawable.f067, R.drawable.f068,R.drawable.f069,R.drawable.f070,R.drawable.f071,R.drawable.f072,R.drawable.f073, R.drawable.f074,R.drawable.f075,R.drawable.f076,R.drawable.f077,R.drawable.f078,R.drawable.f079, R.drawable.f080,R.drawable.f081,R.drawable.f082,R.drawable.f083,R.drawable.f084,R.drawable.f085, R.drawable.f086,R.drawable.f087,R.drawable.f088,R.drawable.f089,R.drawable.f090,R.drawable.f091, R.drawable.f092,R.drawable.f093,R.drawable.f094,R.drawable.f095,R.drawable.f096,R.drawable.f097, R.drawable.f098,R.drawable.f099,R.drawable.f100,R.drawable.f101,R.drawable.f103,R.drawable.f104, R.drawable.f105 }; } // 获取图片的个数 public int getCount() { return mImageIds.length; } // 获取图片在库中的位置 public Object getItem(int position) { return position; } // 获取图片ID public long getItemId(int position) { return mImageIds[position]; } public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView; if (convertView == null) { imageView = new ImageView(mContext); // 设置图片n×n显示 imageView.setLayoutParams(new GridView.LayoutParams(85, 85)); // 设置显示比例类型 imageView.setScaleType(ImageView.ScaleType.CENTER); } else { imageView = (ImageView) convertView; } imageView.setImageResource(mImageIds[position]); if(position < 65) imageView.setTag("["+position+"]"); else if(position < 100) imageView.setTag("["+(position+1)+"]"); else imageView.setTag("["+(position+2)+"]"); return imageView; } }
对上面GridViewFaceAdapter类的代码做下说明:
1.我将所有的表情图片对应的资源ID,用一个整数数组封装了。
2.getView方法里的下面这段代码:imageView.setImageResource(mImageIds[position]); if(position < 65) imageView.setTag("["+position+"]"); else if(position < 100) imageView.setTag("["+(position+1)+"]"); else imageView.setTag("["+(position+2)+"]");
由于客户端用到表情图片,对应OSC网站里的表情图片不是完整的,才做了上面代码的判断处理。
OK,就这么简单。
下面附上OSC Android项目提取的源码包:
点击此处
如果大家有什么疑问的话,欢迎在下面回帖一起探讨。
PS:
OSC Android客户端下载地址: http://www.oschina.net/uploads/osc.apk
OSCiPhone客户端下载地址:http://www.oschina.net/uploads/osc.ipa
OSC Windows Phone客户端下载地址: http://www.oschina.net/uploads/osc.xap转载:http://www.oschina.net/question/157182_65855