【实战】聊天窗口的实现
今天参考书上的例子,自己也实现了一个相对美观的聊天界面,过程如下:
一、第一步制作用于显示聊天内容的图片,使用SDK目录下的Tools下的draw9patch.bat来制作Nine-Patch图片,以便适用于各种分辨率的终端;
需要注意的是在制作好之后保存的时候不能把保存的名称改掉,一定要带上保存时自动加上的.9,并且在引用的时候名称不用写.9即可,切记!在这浪费了一个多小时
二、编写主界面activity_main.xml,放一个ListView和LinearLayout,LinearLayout下面再放一个EditText(用于编写消息)和Button(用于发送消息)
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:id="@+id/LinearLayout1" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:orientation="vertical" 7 android:background="#d8e0e8" > 8 9 <ListView 10 android:id="@+id/msg_list_view" 11 android:layout_width="match_parent" 12 android:layout_height="0dp" 13 android:layout_weight="1" 14 android:divider="#0000" > 15 </ListView> 16 17 18 <LinearLayout 19 android:layout_width="match_parent" 20 android:layout_height="wrap_content"> 21 <EditText android:id="@+id/input_text" 22 android:layout_width="0dp" 23 android:layout_height="wrap_content" 24 android:layout_weight="1" 25 android:hint="Type something here" 26 android:maxLines="2"/> 27 28 <Button 29 android:id="@+id/send" 30 android:layout_width="wrap_content" 31 android:layout_height="wrap_content" 32 android:text="Send"/> 33 34 </LinearLayout> 35 </LinearLayout>
三、新建消息实体类Msg
1 package com.example.Entity; 2 3 public class Msg { 4 public static final int TYPE_RECEIVED = 0; 5 public static final int TYPE_SEND = 1; 6 private String content;//消息内容 7 private int type;//消息类型,分为发送(TYPE_SEND)和接收的(TYPE_RECEIVED) 8 9 public Msg(String content, int type) { 10 // TODO Auto-generated constructor stub 11 this.content = content; 12 this.type = type; 13 } 14 15 public String getContent() { 16 return content; 17 } 18 19 public int getType() { 20 return type; 21 } 22 23 }
四、由于消息是用ListView显示的,那么接下来要编写ListView子项的布局,新建一个xml文件,msg_item.xml,发送和接收的消息在一个布局中,只要待会在代码中对布局的可见性进行控制即可;
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 android:padding="10dp"> 7 8 9-26行是在左侧出现的主要是在left_layout布局中放入了一个TextView来显示文字,left_layout布局中的背景就是我们做的Nine-Patch图片;;;代表的是接收的消息 9 <LinearLayout 10 android:id="@+id/left_layout" 11 android:layout_width="wrap_content" 12 android:layout_height="wrap_content" 13 android:layout_gravity="start" 14 android:orientation = "vertical" 15 android:background="@drawable/message_left"> 16 17 <TextView 18 android:id="@+id/left_msg" 19 android:layout_width="wrap_content" 20 android:layout_height="wrap_content" 21 android:layout_gravity="center" 22 android:layout_margin="10dp" 23 android:textColor="#fff" 24 android:textSize="15sp"/> 25 26 </LinearLayout> 27 9-26行是在右侧出现的主要是在right_layout布局中放入了一个TextView来显示文字,right_layout布局中的背景就是我们做的Nine-Patch图片;;;代表的是发送的消息 28 <LinearLayout 29 android:id="@+id/right_layout" 30 android:layout_width="wrap_content" 31 android:layout_height="wrap_content" 32 android:layout_gravity="end" 33 android:orientation = "vertical" 34 android:background="@drawable/message_right"> 35 36 <TextView 37 android:id="@+id/right_msg" 38 android:layout_width="wrap_content" 39 android:layout_height="wrap_content" 40 android:layout_gravity="center" 41 android:layout_margin="10dp" 42 android:textColor="#fff" 43 android:textSize="15sp"/> 44 45 </LinearLayout> 46 47 </LinearLayout>
四、由于ListView要显示的数据需要通过适配器Adapter来实现,下面我们来建立一个适配器类MsgAdapter,继承于ArrayAdapter,并将泛型指定为Msg类;
1 package com.example.uibestpractice; 2 3 import java.util.List; 4 5 import com.example.Entity.Msg; 6 7 import android.content.Context; 8 import android.view.LayoutInflater; 9 import android.view.View; 10 import android.view.ViewGroup; 11 import android.widget.ArrayAdapter; 12 import android.widget.LinearLayout; 13 import android.widget.TextView; 14 15 public class MsgAdapter extends ArrayAdapter<Msg> { 16 17 //设置私有变量 18 private int resourceId; 19 20 //建立三个参数的构造方法 21 public MsgAdapter(Context context, int textViewResourceId , List<Msg> objects) { 22 23 //实现了ArrayAdapter类的构造方法 24 super(context, textViewResourceId,objects); 25 resourceId = textViewResourceId; 26 } 27 28 //position就是你选择的 item的第几条从0开始 29 //convertView就是item上的布局layout或者组件 30 //ViewGroup parent 就是设置adapter的那个组件里面封装一个viewGroup用来盛放item 31 @Override 32 public View getView(int position, View convertView, ViewGroup parent) { 33 // TODO Auto-generated method stub 34 Msg msg = getItem(position);//获取当前项的Msg实例; 35 View view ;//定义一个View 36 ViewHolder viewHolder ; 37 // 定义一个ViewHolder,ViewHolder是一个内部类, 38 // 就是一个持有者的类,他里面一般没有方法,只有属性, 39 // 作用就是一个临时的储存器,把你getView方法中每次返回的View存起来,可以下次再用。 40 // 这样做的好处就是不必每次都到布局文件中去拿到你的View,提高了效率 41 42 //判断convertView对象是否为空,如果为空就重新加载; 43 if(convertView == null) 44 { 45 //使用Layoutinflater为这个子项加载我们传入的布局(resourceId), 46 view = LayoutInflater.from(getContext()).inflate(resourceId, null); 47 //实例化内部类viewHolder 48 viewHolder = new ViewHolder(); 49 //将控件的实例都存在ViewHolder中 50 viewHolder.leftLayout =(LinearLayout) view.findViewById(R.id.left_layout); 51 viewHolder.rightLayout =(LinearLayout) view.findViewById(R.id.right_layout); 52 viewHolder.leftMag = (TextView) view.findViewById(R.id.left_msg); 53 viewHolder.rightMag = (TextView) view.findViewById(R.id.right_msg); 54 //把viewHolder通过View的setTag方法存放起来; 55 view.setTag(viewHolder); 56 } 57 else//如果不为空,则重新调用convertView;不必重新加载 58 { 59 view = convertView; 60 viewHolder = (ViewHolder) view.getTag();//取出viewHolder,也就取出了已经加载过得数据 61 } 62 63 if(msg.getType() == msg.TYPE_RECEIVED)//如果是接收的消息则左侧显示布局和文字,右侧隐藏 64 { 65 viewHolder.leftLayout.setVisibility(View.VISIBLE); 66 viewHolder.rightLayout.setVisibility(View.GONE); 67 viewHolder.leftMag.setText(msg.getContent()); 68 } 69 else if(msg.getType() == msg.TYPE_SEND)//如果是发送的消息则右侧显示布局和文字,左侧隐藏 70 { 71 viewHolder.rightLayout.setVisibility(View.VISIBLE); 72 viewHolder.rightMag.setText(msg.getContent()); 73 viewHolder.leftLayout.setVisibility(View.GONE); 74 } 75 //返回一个View 76 return view; 77 } 78 class ViewHolder//临时存储器,把getView方法中每次返回的View存起来,可以下次再用 79 { 80 LinearLayout leftLayout ; 81 LinearLayout rightLayout ; 82 TextView leftMag; 83 TextView rightMag; 84 85 } 86 }
五、最后来实现MainActivity.java的代码,为ListView初始化数据,并设置一些响应事件,完成最后的操作
1 package com.example.uibestpractice; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import com.example.Entity.Msg; 7 8 import android.app.Activity; 9 import android.os.Bundle; 10 import android.view.Menu; 11 import android.view.MenuItem; 12 import android.view.View; 13 import android.view.View.OnClickListener; 14 import android.widget.Button; 15 import android.widget.EditText; 16 import android.widget.ListView; 17 18 public class MainActivity extends Activity { 19 20 private ListView msgListView;//定义ListView 21 private EditText inputText;//定义EditText 22 private Button send;//定义Button 23 private MsgAdapter adapter;//定义MsgAdapter 24 private List<Msg> msgList = new ArrayList<Msg>();//实例化一个泛型为Msg的List 25 26 @Override 27 protected void onCreate(Bundle savedInstanceState) { 28 super.onCreate(savedInstanceState); 29 setContentView(R.layout.activity_main); 30 31 //初始化数据 32 initMsgs(); 33 34 //调用MsgAdapter的构造方法实例化adapter, 35 adapter = new MsgAdapter(MainActivity.this, R.layout.msg_item, msgList); 36 37 //分别实例化EditText、Button、ListView 38 inputText = (EditText) findViewById(R.id.input_text); 39 send = (Button) findViewById(R.id.send); 40 msgListView = (ListView) findViewById(R.id.msg_list_view); 41 42 //为ListView加适配器; 43 msgListView.setAdapter(adapter); 44 45 //设置send的点击事件,响应用户操作 46 send.setOnClickListener(new OnClickListener() { 47 48 @Override 49 public void onClick(View v) { 50 51 //定义内容获取用户在EditText中输入的字符串 52 String content = inputText.getText().toString(); 53 54 //如果字符串不为空,把数据添加到 msgList中 55 if(!"".equals(content)) 56 { 57 Msg msg = new Msg(content , Msg.TYPE_SEND); 58 msgList.add(msg); 59 60 //动态更新ListView 61 adapter.notifyDataSetChanged(); 62 63 //将列表移动到刚发的消息处,即msgList的最大位置 64 msgListView.setSelection(msgList.size()); 65 66 //把输入框置空 67 inputText.setText(""); 68 } 69 } 70 }); 71 } 72 73 private void initMsgs() { 74 // TODO Auto-generated method stub 75 Msg msg1 = new Msg("Hello guy!",Msg.TYPE_RECEIVED);//往msgList中添加接收数据 76 msgList.add(msg1); 77 Msg msg2 = new Msg("Hello,Who is that?",Msg.TYPE_SEND);//往msgList中添加发送数据 78 msgList.add(msg2); 79 Msg msg3 = new Msg("This is Tom,Nice talking to you.",Msg.TYPE_RECEIVED);//往msgList中添加接收数据 80 msgList.add(msg3); 81 } 82 83 84 }
至此,此次实战结束,运行效果图: