使用Listview实现一个简单的聊天页面

如何使用Listview实现一个简单的聊天页面

       Listview是安卓的一个控件,我们可以使用它来显示一些我们希望的一些数据,可以用来制作菜单列表,水果列表,聊天列表等。但它只是一种简单的控件,实现功能比较有限,只能实现上下滑动,不能实现左右滑动。
       如何实现可以左右滑动呢?这就要涉及到另一个组件了,Recycleview。这个组件的编写会比Listview难一点,虽然更自由,功能更强,需要较强的开发经验。所以我们先学简单的,采用Listview实现聊天页面功能。
       在开发学习中,遇到了很多问题。比如说,当你有上百条信息要显示时,但因为屏幕限制只能显示其中的十条,或者更少,那多余的数据该如何进行处理呢?
       首先我们要先知道,什么是Listview? 其实我们可以把它当做是存放数据的一个容器,显示在activity 上的。只有通过Adapter 才可以把列表中的数据映射到ListView 中。
       至于页面之外的信息,可以自己定义一个缓存类ViewHolder存放它们,这样就可以解决信息的缓存问题。具体的实现就不再多说,网上有很多解释各组件相关概念及各种实例教程,可以多去学习,加深理解。

看一下效果演示图:

实现效果图


主布局文件 activity_main.xml

思路:
一个Listview组件,用来显示消息。
子LinearLayout布局组件里包含了一个可输入文本框EditText,一个发送按钮Button。

<?xml version="1.0" encoding="utf-8"?>
<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"
    >

    <!--<TextView android:text="@string/hello_world" 
    android:layout_width="wrap_content"-->
    <!--android:layout_height="wrap_content" />-->

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:dividerHeight="20sp"
        android:divider="#0000">
    </ListView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginEnd="10dp" >

        <EditText
            android:id="@+id/input_text"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_marginLeft="25dp"
            android:hint="请输入内容"
            android:maxLines="2"/>

        <Button
            android:id="@+id/send1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="发送" />
    </LinearLayout>
</LinearLayout>

发送端布局文件 msg_send.xml

思路:
可以当做是一个消息item,通过适配器显示在Listview里。
线性布局选择垂直方式,即 android:orientation=“horizontal”
头像ImageView:找一些图片放在 drawable-v24 里即可。
消息文本TextView: 用于显示消息。
其中消息文本显示的聊天气泡,用了 .9.png 格式的图片,设为背景即可。
关于 .9.png 格式的制作,在这篇博客有详解及视频教学。

在Android Studio如何制作聊天气泡

<?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="horizontal" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="50dp"
        android:gravity="right" >

        <TextView
            android:id="@+id/send_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="5dp"
            android:layout_marginTop="20dp"
            android:layout_marginLeft="40dp"
            android:gravity="left"
            android:background="@drawable/airbubbles7_send2"
            android:text="TextView"
            android:textSize="20dp" />

        <ImageView
            android:id="@+id/send_head_portrait"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:src="@drawable/headportrait4" />
            
    </LinearLayout>
</LinearLayout>
接收端布局文件 msg_receive.xml

接收端同发送端,就不再赘述。

<?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="horizontal" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginRight="50dp"
        android:gravity="left" >
        
        <ImageView
            android:id="@+id/receive_head_portrait"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:src="@drawable/headportrait6" />

        <TextView
            android:id="@+id/receive_msg"
            android:layout_marginLeft="10dp"
            android:background="@drawable/airbubbles6_receive"
            android:textSize="20dp"
            android:gravity="left"
            android:textColor="@color/teal_200"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="TextView" />
            
    </LinearLayout>
</LinearLayout>
消息实体类 msgBean.java

思路:定义一个消息实体类,用于定义数据类型。可以将其当做一个容器,里面可以放什么,调用的时候会返回一个什么参数给你,都可以自己定义。

import android.graphics.Bitmap;

public class msgBean {
    private int type;
    private String text;
    private Bitmap icon;

    //返回类型  用于检测接收数据类型
    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    //接收消息内容
    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    //位图 用于存放头像
    public Bitmap getIcon() {
        return icon;
    }

    public void setIcon(Bitmap icon) {
        this.icon = icon;
    }

    //构造函数
    public ChatItemListViewBean(){    }
}
消息适配器 msgAdapter.java

思路:
新建msgBean类型的链表,存放数据。
编写消息适配器msgAdapter,重写各种方法,以及缓存处理。

import android.content.Context;
        import android.view.LayoutInflater;
        import android.view.View;
        import android.view.ViewGroup;
        import android.widget.BaseAdapter;
        import android.widget.ImageView;
        import android.widget.TextView;
        import java.util.List;

//视图适配器
public  class msgAdapter extends BaseAdapter {

    private List<msgBean> mData;   //数据源
    private LayoutInflater mInflater;   //布局资源

    public msgAdapter(Context context, List<msgBean> data)
    {
        this.mData = data;
        mInflater = LayoutInflater.from(context);
    }

    //构造函数
    public msgAdapter() {

    }

    //重写getCount方法
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return mData.size(); //返回item个数
    }

    //重写getItem方法
    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return mData.get(position); //返回相应位置item,即显示出第几个item
    }

    //重写getItemId方法
    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position; //得到item的具体位置, 挺重要的  后面多次调用
    }

    @Override
    public int getItemViewType(int position)
    {
        msgBean bean = mData.get(position);
        return bean.getType();//返回布局文件的类型  判断是 接收者 还是 发送者
    }

    @Override
    public int getViewTypeCount(){
        return 2;        //类型的数目
    }

    //重写  视图   适配器核心部分
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        ViewHolder holder;  //ViewHolder对象

        //convertView -旧视图重用,如果可能的话。
        // 注意:在使用此视图之前,您应该检查该视图是否为非空且具有适当的类型。
        // 如果无法将此视图转换为显示正确的数据,则此方法可以创建一个新视图。
        // 异构列表可以指定视图类型的数量,因此该视图总是正确的类型
        // (参见getViewTypeCount()和getItemViewType(int))。
        if(convertView == null)
        {
            //Get the type of View that will be created by getView for the specified item.
            if(getItemViewType(position) == 0){
                holder = new ViewHolder();
                //对于 LayoutInflater 的 inflate() 方法,它的作用是把 xml 布局转换为对应的 View 对象
                convertView = mInflater.inflate(R.layout.msg_receive, null);
                holder.icon = (ImageView) convertView.findViewById(R.id.receive_head_portrait);
                holder.text = (TextView) convertView.findViewById(R.id.receive_msg);

            }else {
                holder = new ViewHolder();
                //对于 LayoutInflater 的 inflate() 方法,它的作用是把 xml 布局转换为对应的 View 对象
                convertView = mInflater.inflate(R.layout.msg_send, null);
                holder.icon = (ImageView) convertView.findViewById(R.id.send_head_portrait);
                holder.text = (TextView) convertView.findViewById(R.id.send_msg);
            }

            //View中的    setTag(Object)  表示给View添加一个格外的数据,以后可以用getTag()将这个数据取出来。
            convertView.setTag(holder);
        }else {
            holder = (ViewHolder) convertView.getTag(); //如果有数据就将其getTag出来
        }
        //输出相应的控件  即图片和 消息
        holder.icon.setImageBitmap(mData.get(position).getIcon());
        holder.text.setText(mData.get(position).getText());
        return convertView;
    }

    //缓冲池
    public final class ViewHolder{
        public ImageView icon;
        public TextView text;
    }
}
主类 MainActivity.java

思路:功能实现。
建立msgBean类型的动态列表
实例化适配器
消息初始化的编写
消息输入,点击事件的实现

import android.app.Activity;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity implements View.OnClickListener{

    private ListView mListView;
    private EditText editText;
    private Button button;

    //建立一个msgBean类型的动态列表  data对象
    List<msgBean> data = new ArrayList<msgBean>();
    msgAdapter adapter= new msgAdapter();

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //页面初始化 一般不用管 如果你的初始页面不是这个
        //可以考虑去AndroidManifest注册一个页面,使它合法化 下面对其进行简单的解释

        //一下操作在manifests-->AndroidManifest下操作  俗称 “大管家”
        /*
        <!-- 使MainActivity页面合法化  记得前面的 . 字符      -->
        <activity android:name=".MainActivity">

            <!-- 意图过滤器  所谓意图,即指明计算机跳转到哪一个页面去-->
            <intent-filter>
                <!-- 下面这两句 一个是设为主页面 以及启动方式  具体不大熟悉 有兴趣可以深入一下 -->
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        */

        mListView = (ListView) findViewById(R.id.list_view);
        initMsg();//消息初始化   以后可以优化,即把它去掉 如果有后台数据库,可以保存起来  不过这方面我还研究
        //不然每次打开软件,都对消息进行初始化  那先前的消息都没了  可以自己深挖一下

        //监听按钮
        button =(Button) findViewById(R.id.send1);
        button.setOnClickListener(this);
        mListView.setAdapter(new msgAdapter(this, data));
    }

    public void initMsg(){
        //创建一个msgBean  对象
        msgBean bean1 = new msgBean();
        bean1.setType(0);//左
        bean1.setIcon(BitmapFactory.decodeResource(getResources(), R.drawable.headportrait6));
        bean1.setText("你好呀");

        msgBean bean2 = new msgBean();
        bean2.setType(1);//右
        bean2.setIcon(BitmapFactory.decodeResource(getResources(), R.drawable.headportrait4));
        bean2.setText("你好");

        msgBean bean3 = new msgBean();
        bean3.setType(0);
        bean3.setIcon(BitmapFactory.decodeResource(getResources(), R.drawable.headportrait6));
        bean3.setText("很高兴为你服务,这里是listview测试页面");

        msgBean bean4 = new msgBean();
        bean4.setType(1);
        bean4.setIcon(BitmapFactory.decodeResource(getResources(), R.drawable.headportrait4));
        bean4.setText("好的,我知道了。");

        msgBean bean5 = new msgBean();
        bean5.setType(0);
        bean5.setIcon(BitmapFactory.decodeResource(getResources(), R.drawable.headportrait6));
        bean5.setText("希望能满足你的需求。");

        data.add(bean1);
        data.add(bean2);
        data.add(bean3);
        data.add(bean4);
        data.add(bean5);
    }

    @Override
    public void onClick(View v) {
        editText =(EditText) findViewById(R.id.input_text);
        //定义一个String类型的  input变量  返回input_text里的内容
        String input = editText.getText().toString();
        if(input.equals("")){
            Toast.makeText(MainActivity.this,"发送内容不能为空",Toast.LENGTH_SHORT).show();
        }
        else {
            //定义一个msgBean类型的对象  发送消息的实例
            msgBean bean = new msgBean();
            bean.setType(1);
            bean.setIcon(BitmapFactory.decodeResource(getResources(), R.drawable.headportrait4));
            bean.setText(input);

            data.add(bean);
            //setListViewHeightBasedOnChildren(mListView);//动态显示高度

            mListView.setAdapter(new msgAdapter(this, data));

            //精华部分  卡了两天   当然还有其他实现方式  可能用recycleview来实现聊天页面不大适用
            /* 调用ListView的setSelection()方法将显示的数据定位到最后一行*/
            mListView.setSelection(data.size());

            //清空输入框文本
            editText.setText("");
        }
    }
}

       以上就是本次实验的全部内容了,下面是项目的源码部分,制作不易,请多多支持一下,灰常感谢。 另外还制作了一些简单的气泡文件,有需要的可以看一下,可能不大符合您的个人口味,所以自己动手,丰衣足食。

百度网盘链接:

https://pan.baidu.com/s/1uUGmmOOPEtoVg0_ZxC96iA
提取码:55f6

       Demo及人物图像图片,气泡等的相关资料都在其间。其中气泡,头像等都在 airbubbles.zip 压缩包里,test_demo.zip 是项目的demo,有需要自己可以下载。

希望本文对您有所帮助,感谢支持。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值