1.气泡的准备:为了避免图片的失真需要用到如下工具:

在Android sdk 目录下有一个tools 文件夹,在这个文件夹中找到draw9patch.bat 文件,
我们就是使用它来制作Nine-Patch 图片的。双击打开之后,在导航栏点击File→Open 9-patch
将图片加载进来

我们可以在图片的四个边框绘制一个个的小黑点,在上边框和左边框绘制的部分就表示
当图片需要拉伸时就拉伸黑点标记的区域,在下边框和右边框绘制的部分则表示内容会被放
置的区域。

最后点击导航栏 File→Save 9-patch 把绘制好的图片进行保存,此时的文件名就是.9.png。

2.布局文件如下 activity_main.xml

 

<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"
    tools:context="com.example.simple.MainActivity" >

    <ListView 
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="0dp" 
        android:layout_weight="1"
        android:background="#ffffff"
        android:dividerHeight="5dp"
        android:scrollbars="none"
        android:divider="#ffffff"/>
    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal">
        <EditText 
            android:id="@+id/editText"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:hint="请输入..."
            android:layout_height="match_parent"/>
        <Button 
            android:id="@+id/btn_send"
            android:layout_marginLeft="5dp"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="发送"/>
    </LinearLayout>
</LinearLayout>

 

msg_item.xml:消息的布局,ListView的子条目如下:

 

<!--
 将左右两边的消息放在一个布局,通过信息的类型而隐藏掉相对的布局 
        android:gravity="center|left" 让文字居中且过长的时候换行靠左显示
-->
<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"
    tools:context="com.example.simple.MainActivity" >

    <LinearLayout
        android:id="@+id/left_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <ImageView
            android:id="@+id/ivicon"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_gravity="top"
            android:src="@drawable/ic_launcher" />

        <TextView
            android:id="@+id/left_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="100dp"
            android:layout_marginTop="8dp"
            android:background="@drawable/left"
            android:gravity="center|left"
            android:padding="8dp"
            android:text="左边默认的文字" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/right_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/right_text"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="100dp"
            android:layout_marginRight="5dp"
            android:layout_marginTop="8dp"
            android:layout_weight="1"
            android:background="@drawable/right"
            android:gravity="center|left"
            android:padding="8dp"
            android:text="右边默认的文字" />

        <ImageView
            android:id="@+id/right_image"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:src="@drawable/app_lvjian_message_background" />
    </LinearLayout>

</LinearLayout>

 

注意:这里右边的消息如果不设置权重让右边的图片先显示,如果内容过长会把图片挤出外面

 

3.消息的实体类:Message.java

/**消息的实体类:包括content消息内容   type消息类型
 */
public class Message {
    private String content;//内容
    private boolean type;//消息类型,该消息是否是自己发送
    
    public Message() {
        super();
    }
    /**
     * @param content 消息内容
     * @param type      消息类型:true代表是自己发送   false代表是接收到的消息
     */
    public Message(String content, boolean type) {
        super();
        this.content = content;
        this.type = type;
    }

    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public boolean isType() {
        return type;
    }
    public void setType(boolean type) {
        this.type = type;
    }
}

4.适配器 MyAdapter.java:

//自定义的Adapter加载布局文件的时候需要判断是哪方发送的消息来决定用哪个item布局文件
public class MyAdapter extends BaseAdapter {
    private List<Message> list;// Message包括消息类型和消息
    private Context context;

    public MyAdapter(Context context, List<Message> list) {
        super();
        this.list = list;
        this.context = context;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        View view = null;//装所有消息的布局
        if (convertView == null) {
            holder = new ViewHolder();
            view = LayoutInflater.from(context).inflate(R.layout.msg_item, null);
            holder.leftMsg = (TextView) view.findViewById(R.id.left_text);
            holder.leftLayout = (LinearLayout) view.findViewById(R.id.left_layout);
            holder.rightLayout = (LinearLayout) view.findViewById(R.id.right_layout);
            holder.rightMsg = (TextView) view.findViewById(R.id.right_text);
            view.setTag(holder);
        } else {
            view=convertView;//将翻出去的视图对象赋值给view,对象的重复使用
            holder = (ViewHolder) view.getTag();
        }
        Message msg=list.get(position);//获取消息的对象
        if(msg.isType()){//如果是自己发送的
            holder.rightMsg.setText(msg.getContent());//将信息内容设置到TextView中显示
            holder.leftLayout.setVisibility(View.GONE);//设置左边的布局不可见且不占用位置
            holder.rightLayout.setVisibility(View.VISIBLE);//设置右边的布局可见
        }else{//如果是收到的消息
            holder.leftMsg.setText(msg.getContent());
            holder.leftLayout.setVisibility(View.VISIBLE);
            holder.rightLayout.setVisibility(View.GONE);
        }
        return view;
    }
    // 提高效率
    class ViewHolder {
        LinearLayout leftLayout;
        LinearLayout rightLayout;
        TextView leftMsg;
        TextView rightMsg;
    }
}

5.MainActivity.java

public class MainActivity extends Activity {

    private EditText editText;//编辑信息的编辑框
    private Button btn_send;//发送消息的按钮
    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editText = (EditText) findViewById(R.id.editText);
        btn_send = (Button) findViewById(R.id.btn_send);
        listView = (ListView) findViewById(R.id.listView);
        //模拟几条消息
        final List<Message> list=new ArrayList<Message>();
        list.add(new Message("在吗?", false));//false代表是接收到的消息
        list.add(new Message(,不回我",false));
        list.add(new Message("就是不回,你能拿我怎么滴!!!",true));
        list.add(new Message("咖啡骄傲的算法来房间了发打发斯蒂芬打发大师傅阿道夫阿道夫",false));
        final MyAdapter adapter=new MyAdapter(this, list);
        listView.setAdapter(adapter);
        btn_send.setOnClickListener(new View.OnClickListener() {
            @Override//发送信息按钮监听
            public void onClick(View v) {
                String str=editText.getText().toString();//编辑框的内容
                if(!TextUtils.isEmpty(str)){
                    list.add(new Message(str, true));
                    adapter.notifyDataSetChanged(); //刷新ListView
                    listView.setSelection(list.size()); // 将ListView定位到最后一行
                    editText.setText("");//清空输入框
                }
            }
        });
        
    }
}