目前手机上很多聊天app都有一个好看的聊天界面,那么如何用android来编写一个这样的聊天界面呢?
首先应该清楚这个聊天界面应该包含什么内容,当然是显示的消息以及发送消息的编辑框等等,一个好的布局才能有一个漂亮的界面。
大概的步骤如下:
(1)先定义一个Msg消息类,用来声明消息的类型(发出的消息和收到的消息)以及存储消息的内容。
(2)然后编写一个主界面的xml文件,在一个线性布局中用ListView来显示消息,然后就是一个编辑框来输入消息内容以及发送按钮。
(3)在ListView子项中编写布局,其作用就是使发送的消息以及接受的消息显示在不同的位置以及添加一些显示效果。
(4)接下来要自定义一个适配器类用来显示ListView。
(5)最后是在主程序中添加这些布局以及ListView以达到最终的显示目的。
然后就是具体的编写。
(1)创建Msg类
定义两个静态常量用来表示收发消息,type为消息类型,content用于存储消息内容。
package com.chat;
public class Msg {
public static final int TYPE_RECEIVED=0;
public static final int TYPE_SENT=1;
private String content;
private int type;
public Msg(String content,int type){
this.content=content;
this.type=type;
}
public String getContent(){
return content;
}
public int getType(){
return type;
}
}
(2)定义主界面布局main.xml
其中在主界面上设置了一个聊天背景,放在drawable资源文件夹下,图片中的黑色框指一个LinearLayout,显示为vertical,包括两个LinearLayout,上面的LinearLayout其中的红色部分指要显示消息的ListView,下面的LinearLayout包含一个EidtText和一个发送按钮。在编辑框有个android:hint属性指提示文字。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/liaotian" >
<ListView
android:id="@+id/msg_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:divider="@color/red">
</ListView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<EditText
android:id="@+id/input_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:hint="写点东西吧。。。"
android:maxLines="2"/>
<Button
android:id="@+id/send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send"/>
</LinearLayout>
</LinearLayout>
(3)然后是编写ListView中的布局
下面表示在ListView中设置一个LinearLayout,属性为vertical,包含两个LinearLayout上面一个显示接收消息的TextView,下面的显示发送消息的Textview。其中在两个LinearLayout中添加了一个聊天气泡背景以及TextView的位置设定。android:padding设置控件外边距,android:layout_margin设置控件内边距。
<?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"
android:padding="10dp"
>
<!-- 左边显示的LinearLayout -->
<LinearLayout
android:id="@+id/left_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:background="@drawable/qipao"
>
<!-- 设置左边的TextView消息显示在中间,并设置其内边距为15dp -->
<TextView
android:id="@+id/left_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="15dp"/>
</LinearLayout>
<!-- 右边显示的LinearLayout -->
<LinearLayout
android:id="@+id/right_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@drawable/qipao">
<!-- 设置右边的TextView -->
<TextView
android:id="@+id/right_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="15dp"/>
</LinearLayout>
</LinearLayout>
(4)然后自定义一个适配器
创建类MsgAdapter继承ArrayAdapter,用ArrayAdapter来实现ListView ,并指定泛型为Msg。
首先使该类的构造方法继承其父类构造函数 ,其中的参数content指上下文,textViewResourceId指子布局的id,objects指存在列表中的数据,该构造放法就是将这些参数代表的内容传递进来。
然后重写getView()方法,此方法当每个列表子项滚动到屏幕中时会被调用,其中的参数position指列表当前项的位置,convertView用来缓存传进来的布局,以便可以再次使用,在方法中首先获取当前项的Msg实例,接下来有一个if语句判断这个实例的convertView是否为空,如果为空则使用LayoutInflater.from(getContext()).inflate(resourceId, null)加载传入的子布局,反之则继续使用已有的布局。在这里还定义了一个ViewHolder变量,ViewHolder是指对控件实例进行缓存,也是在前面的if语句中,当Msg实例的convertView为空时创建ViewHolder对象调用findViewById( )方法来获得各UI控件将其缓存起来,让后调用View的setTag()方法把ViewHolder对象存在View中,然后但convertView不为空时再用View调用getag()方法取出ViewHolder,这样可以提高cpu运行效率。
最后创建一个内部类ViewHolder声明布局。
另外还要设置一下消息的显示,在if语句中判断若消息类型为接收类型则显示左边布局,右边隐藏,通过setVisibility()方法设置可见性,并通过setText(msg.getContent())来获取消息内容并显示在ListView上;若为发送类型则相反。
package com.chat;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MsgAdapter extends ArrayAdapter<Msg>{
private int resourceId;
//定义MsgAdapter的构造方法
public MsgAdapter(Context context,int textViewResourceId,List<Msg> objects){
super(context,textViewResourceId,objects);
resourceId=textViewResourceId;
}
//获取布局
public View getView(int position,View convertView,ViewGroup parent){
Msg msg=getItem(position);
View view;
ViewHolder viewHolder;
//当消息没有在屏幕上显示时,加载布局以及获取控件
if(convertView==null){
view=LayoutInflater.from(getContext()).inflate(resourceId, null);//加载已传入的布局
viewHolder=new ViewHolder();
//得到两个LinearLayout布局控件
viewHolder.leftLayout=(LinearLayout)view.findViewById(R.id.left_layout);
viewHolder.rightLayout=(LinearLayout)view.findViewById(R.id.right_layout);
//得到两个TextView控件
viewHolder.leftMsg=(TextView)view.findViewById(R.id.left_msg);
viewHolder.rightMsg=(TextView)view.findViewById(R.id.right_msg);
view.setTag(viewHolder);
}
else{
view=convertView;
viewHolder=(ViewHolder)view.getTag();
}
//判断是传入的消息还是收到的消息,若消息为收到的消息,则应显示左边的布局,右边的布局隐藏
if(msg.getType()==0){
viewHolder.leftLayout.setVisibility(view.VISIBLE);
viewHolder.rightLayout.setVisibility(View.GONE);
viewHolder.leftMsg.setText(msg.getContent());
}
else if(msg.getType()==1){
viewHolder.leftLayout.setVisibility(view.GONE);
viewHolder.rightLayout.setVisibility(View.VISIBLE);
viewHolder.rightMsg.setText(msg.getContent());
}
return view;
}
class ViewHolder{
LinearLayout leftLayout;
LinearLayout rightLayout;
TextView leftMsg;
TextView rightMsg;
}
}
(5)编写主程序
首先创建一个MainActiivty继承Activity,然后定义主布局的UI,创建一个ArrayList对象,泛型为Msg以及获取布局等等。
接下来实例化一个自定义适配器MsgAdapter对象,传入自定义子布局,获取各控件对象以及通过匿名类设置发送按钮的监听事件,然后对于编辑框对象input通过调用getText().toString()来将编辑框内容以字符串形式放入content中,判断content,若不为空则创建一个Msg对象添加到list中,如果有新消息时,adapter调用notifyDataSetChanged()方法可以刷新列表显示新消息,然后通过ListView对象调用setSelection(msgList.size())方法将新消息放置到ListView的最后一行,最后将编辑框内容清空。
最后定义一个初始化消息函数initMsgs()。
package com.chat;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
public class MainActivity extends Activity {
private ListView msgListView;
private EditText input;
private Button send;
private MsgAdapter adapter;
private List<Msg> msgList=new ArrayList<Msg>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initMsgs();
adapter=new MsgAdapter(MainActivity.this,R.layout.msg_item,msgList);//创建一个MsgAdapter适配器实例,传入子布局
//获取各控件
input=(EditText)findViewById(R.id.input_text);
msgListView=(ListView)findViewById(R.id.msg_view);
send=(Button)findViewById(R.id.send);
msgListView.setAdapter(adapter);//将适配器加入ListView中
//设置send监听器
send.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO 自动生成的方法存根
String content=input.getText().toString();//将输入的内容以字符串类型传入content中
//1、若content不为空,创建一个Msg实例,并把它传入到列表中
if(content!=null){
Msg msg=new Msg(content,Msg.TYPE_SENT);
msgList.add(msg);//将该键值对加入到列表中
adapter.notifyDataSetChanged();//2、当有新消息时,刷新ListView中的内容
msgListView.setSelection(msgList.size());//3、将新消息显示到ListView的最后一行
input.setText(null);//4、将输入框内的数据清空
}
}
});
}
private void initMsgs(){
Msg msg1=new Msg("hello",Msg.TYPE_RECEIVED);
msgList.add(msg1);
Msg msg2=new Msg("hi",Msg.TYPE_SENT);
msgList.add(msg2);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
menu.add(0,1,1,R.string.exit);
menu.add(0,1,1,R.string.about);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == 1) {
finish();
}
return super.onOptionsItemSelected(item);
}
}
完成这五步一个简单的聊天界面就完成了,下面可以看一下在android虚拟机上运行的效果