【OSC手机App技术解析】- 编辑框插入表情图片

迷途d书童 发布于 2012/08/21 17:57
阅读 3K+
收藏 15

    众所周知,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
OSC iPhone客户端下载地址:
http://www.oschina.net/uploads/osc.ipa
OSC Windows Phone客户端下载地址: http://www.oschina.net/uploads/osc.xap

 

加载中
0
被风遗忘
被风遗忘
不错不错. 学习了.
0
h
hellozmg
删除表情时有BUG
返回顶部
顶部