//此系列博文是《第一行Android代码》的学习笔记,如有错漏,欢迎指正!
运用简单的布局知识,我们可以来尝试制作一个聊天界面。
一、制作 Nine-Patch 图片 :
Nine-Patch图片是一种被特殊处理过的 png 图片,能够指定哪些区域可以被拉伸而哪些区域不可以。一般用来作为聊天信息的背景。在此我们先准备一张png图片。然后在 Android sdk 目录下有一个 tools 文件夹,在这个文件夹中找到 draw9patch.bat文件。 双击打开之后, 在导航栏点击 File→Open 9-patch将 msg.png加载进来。我们可以在图片的四个边框绘制一个个的小黑点, 在上边框和左边框绘制的部分就表示当图片需要拉伸时就拉伸黑点标记的区域, 在下边框和右边框绘制的部分则表示内容会被放置的区域。右侧是三种不同拉伸情况下的预览图:
最后点击导航栏 File→Save 9-patch 把绘制好的图片进行保存,此时的文件名就是msg_left.9.png。同样,我们制作一张msg_right.9.png。
二、编写布局文件:
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:background="#d8e0e8" 6 android:orientation="vertical" > 7 <ListView 8 android:id="@+id/msg_list_view" 9 android:layout_width="match_parent" 10 android:layout_height="0dp" 11 android:layout_weight="1" 12 android:divider="#0000" > 13 </ListView> 14 <LinearLayout 15 android:layout_width="match_parent" 16 android:layout_height="wrap_content" > 17 <EditText 18 android:id="@+id/input_text" 19 android:layout_width="0dp" 20 android:layout_height="wrap_content" 21 android:layout_weight="1" 22 android:hint="Type somthing here" 23 android:maxLines="2" /> 24 <Button 25 android:id="@+id/send" 26 android:layout_width="wrap_content" 27 android:layout_height="wrap_content" 28 android:text="Send" /> 29 </LinearLayout> 30 </LinearLayout>
我们指定了一个ListView用来显示聊天内容,另外还有一个EditText和button。其中ListView 中用到了一个 android:divider属性,它可以指定 ListView 分隔线的颜色,这里#0000 表示将分隔线设为透明色。
三、编写Msg类:
为了后续的操作,我们定义一个Msg类:
1 public class Msg { 2 public static final int TYPE_RECEIVED = 0; 3 public static final int TYPE_SENT = 1; 4 private String content; 5 private int type; 6 public Msg(String content, int type) { 7 this.content = content; 8 this.type = type; 9 } 10 public String getContent() { 11 return content; 12 } 13 public int getType() { 14 return type; 15 } 16 }
Msg 类中只有两个字段,content表示消息的内容,type 表示消息的类型。其中消息类型有两个值可选,TYPE_RECEIVED 表示这是一条收到的消息,TYPE_SENT 表示这是一条发出的消息。
四、编写ListView的子布局:
新建 msg_item.xml:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical" 5 android:padding="10dp" > 6 <LinearLayout 7 android:id="@+id/left_layout" 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:layout_gravity="left" 11 android:background="@drawable/msg_left" > 12 <TextView 13 android:id="@+id/left_msg" 14 android:layout_width="wrap_content" 15 android:layout_height="wrap_content" 16 android:layout_gravity="center" 17 android:layout_margin="10dp" 18 android:textColor="#fff" /> 19 </LinearLayout> 20 <LinearLayout 21 android:id="@+id/right_layout" 22 android:layout_width="wrap_content" 23 android:layout_height="wrap_content" 24 android:layout_gravity="right" 25 android:background="@drawable/msg_right" > 26 <TextView 27 android:id="@+id/right_msg" 28 android:layout_width="wrap_content" 29 android:layout_height="wrap_content" 30 android:layout_gravity="center" 31 android:layout_margin="10dp" /> 32 </LinearLayout> 33 </LinearLayout>
这里我们让收到的消息居左对齐,发出的消息居右对齐,并且分别使用 message_left.9.png和 message_right.9.png 作为背景图。这里我们让收到的消息和发出的消息都放在同一个布局里,在稍后在代码中我们根据消息的类型来决定隐藏和显示哪种消息。
五、创建 ListView的适配器类:
同样,我们需要新建一个适配器类:
1 public class MsgAdapter extends ArrayAdapter<Msg> { 2 private int resourceId; 3 public MsgAdapter(Context context, int textViewResourceId, List<Msg> 4 objects) { 5 super(context, textViewResourceId, objects); 6 resourceId = textViewResourceId; 7 } 8 @Override 9 public View getView(int position, View convertView, ViewGroup parent) { 10 Msg msg = getItem(position); 11 View view; 12 ViewHolder viewHolder; 13 if (convertView == null) { 14 view = LayoutInflater.from(getContext()).inflate(resourceId, null); 15 viewHolder = new ViewHolder(); 16 viewHolder.leftLayout = (LinearLayout) view.findViewById(R.id.left_layout); 17 viewHolder.rightLayout = (LinearLayout) view.findViewById(R.id.right_layout); 18 viewHolder.leftMsg = (TextView) view.findViewById(R.id.left_msg); 19 viewHolder.rightMsg = (TextView) view.findViewById(R.id.right_msg); 20 view.setTag(viewHolder); 21 } 22 else { 23 view = convertView; 24 viewHolder = (ViewHolder) view.getTag(); 25 } //提高ListView的效率 26 27 if (msg.getType() == Msg.TYPE_RECEIVED) { 28 // 如果是收到的消息,则显示左边的消息布局,将右边的消息布局隐藏 29 viewHolder.leftLayout.setVisibility(View.VISIBLE); 30 viewHolder.rightLayout.setVisibility(View.GONE); 31 viewHolder.leftMsg.setText(msg.getContent()); 32 } else if(msg.getType() == Msg.TYPE_SENT) { 33 // 如果是发出的消息,则显示右边的消息布局,将左边的消息布局隐藏 34 viewHolder.rightLayout.setVisibility(View.VISIBLE); 35 viewHolder.leftLayout.setVisibility(View.GONE); 36 viewHolder.rightMsg.setText(msg.getContent()); 37 } 38 return view; 39 } 40 class ViewHolder { 41 LinearLayout leftLayout; 42 LinearLayout rightLayout; 43 TextView leftMsg; 44 TextView rightMsg; 45 } 46 }
代码中在 getView()方法中增加了对消息类型的判断。如果这条消息是收到的,则显示左边的消息布局,如果这条消息是发出的,则显示右边的消息布局。
六、添加主活动的逻辑:
1 public class MainActivity extends AppCompatActivity { 2 private ListView msgListView; 3 private EditText inputText; 4 private Button send; 5 private MsgAdapter adapter; 6 private List<Msg> msgList = new ArrayList<Msg>(); 7 private int index = 1; 8 @Override 9 protected void onCreate(Bundle savedInstanceState) { 10 super.onCreate(savedInstanceState); 11 //requestWindowFeature(Window.FEATURE_NO_TITLE); 12 setContentView(R.layout.activity_main); 13 initMsgs(); // 初始化消息数据 14 adapter = new MsgAdapter(MainActivity.this, R.layout.msg_item, msgList); 15 inputText = (EditText) findViewById(R.id.input_text); 16 send = (Button) findViewById(R.id.send); 17 msgListView = (ListView) findViewById(R.id.msg_list_view); 18 msgListView.setAdapter(adapter); 19 send.setOnClickListener(new View.OnClickListener() { 20 @Override 21 public void onClick(View v) { 22 String content = inputText.getText().toString(); 23 if (!"".equals(content)) {//content内容不为空时 24 index++; 25 Msg msg = new Msg(content, index%2); 26 msgList.add(msg); 27 adapter.notifyDataSetChanged(); // 当有新消息时,刷新ListView中的显示 28 msgListView.setSelection(msgList.size()); // 将ListView定位到最后一行 29 inputText.setText(""); // 清空输入框中的内容 30 } 31 } 32 }); 33 } 34 35 private void initMsgs() { 36 Msg msg1 = new Msg("娘子?", Msg.TYPE_RECEIVED); 37 msgList.add(msg1); 38 Msg msg2 = new Msg("啊哈?", Msg.TYPE_SENT); 39 msgList.add(msg2); 40 } 41 }
在 initMsgs()方法中我们先初始化了几条数据用于在 ListView 中显示。然后在发送按钮的点击事件里获取了 EditText中的内容, 如果内容不为空则创建出一个新的 Msg 对象, 并把它添加到 msgList列表中去。之后又调用了适配器的 notifyDataSetChanged()方法,用于通知列表的数据发生了变化,这样新增的一条消息才能够在 ListView 中显示。接着调用 ListView的 setSelection()方法将显示的数据定位到最后一行,以保证一定可以看得到最后发出的一条消息。最后调用 EditText的 setText()方法将输入的内容清空。另外,我们加上一个标志index,这样就能轮流发送和接受信息了。
最后我们的程序运行如下:
//End.