• 【安卓基础】03 实现简单聊天界面


    编写一个简单的聊天界面,例子来自《第一行代码》。

    1. 编写主界面

    聊天界面使用 RecyclerView,简单来说是谷歌新增的控件,与 ListView 类似,但是功能更加强大。比如 ListView 只能纵向华东,但是 RecyclerView 可以设置横向的滑动效果。要注意的是为了能让其他版本的安卓程序也能够使用,开发者将 RecyclerView 放到 support 包中,所以使用 RecyclerView 需要在 grale.xml 中添加包的引用。详细参考RecyclerView使用详解

    这里写图片描述

    1.1 在 grale.xml 中添加包的引用

    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    compile 'com.android.support:recyclerview-v7:25.3.1'

    1.2 编写主界面

    activity_main.xml 设置布局:一个 RecyclerView 放聊天对话框,一个输入框和一个发送按钮

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#d8e0e8">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/msg_recycle_view"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <EditText
                android:id="@+id/input_text"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Type someThing Here"
                android:maxLines="2" />
    
            <Button
                android:id="@+id/send"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="send"/>
    
        </LinearLayout>
    
    </LinearLayout>

    2. 聊天对话框的布局

    QQ 气泡一样的布局方式,收到的消息左对齐,发出的消息右对齐,气泡图片作为背景图。前面已经定义了 RecyclerView,所以对其子项进行布局:新建布局文件 msg_item.xml。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp">
    
        <LinearLayout
            android:id="@+id/left_layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:background="@drawable/message_left"
            >
    
            <TextView
                android:id="@+id/left_msg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_margin="10dp"
                android:textColor="#fff"/>
    
        </LinearLayout>
    
        <LinearLayout
            android:id="@+id/right_layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:background="@drawable/message_right"
            >
    
            <TextView
                android:id="@+id/right_msg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_margin="10dp"
                android:textColor="#fff"/>
    
        </LinearLayout>
    
    </LinearLayout>

    3. 消息对象

    消息分为发送的消息和收到的消息,所以要有消息类型,还需要有消息的内容。

    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 static int getTypeReceived() {
            return TYPE_RECEIVED;
        }
    
        public static int getTypeSent() {
            return TYPE_SENT;
        }
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    
        public int getType() {
            return type;
        }
    
        public void setType(int type) {
            this.type = type;
        }
    }

    4. RecyclerView适配器

    ublic class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder> {
    
        private List<Msg> mMsgList;
    
        static class ViewHolder extends RecyclerView.ViewHolder {
    
            LinearLayout leftLayout;
    
            LinearLayout rightLayout;
    
            TextView leftMsg;
    
            TextView rightMsg;
    
            // view表示父类的布局,用其获取子项
            public ViewHolder(View view) {
                super(view);
                leftLayout = (LinearLayout) view.findViewById(R.id.left_layout);
                rightLayout = (LinearLayout) view.findViewById(R.id.right_layout);
                leftMsg = (TextView) view.findViewById(R.id.left_msg);
                rightMsg = (TextView) view.findViewById(R.id.right_msg);
            }
        }
    
        public MsgAdapter(List<Msg> msgList) {
            mMsgList = msgList;
        }
    
        /**
         * 创建 ViewHolder 加载 RecycleView 子项的布局
         *
         * @param parent
         * @param viewType
         * @return
         */
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item, parent, false);
            return new ViewHolder(view);
        }
    
        /**
         * 为 RecycleView 子项赋值
         * 赋值通过 position 判断子项位置
         * 当子项进入界面时执行
         *
         * @param holder
         * @param position
         */
        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            Msg msg = mMsgList.get(position);
            if (msg.getType() == Msg.TYPE_RECEIVED) {
                // 如果是收到的消息,则显示左边的消息布局,将右边的消息布局隐藏
                holder.leftLayout.setVisibility(View.VISIBLE);
                holder.rightLayout.setVisibility(View.GONE);
                holder.leftMsg.setText(msg.getContent());
            } else if (msg.getType() == Msg.TYPE_SENT) {
                // 如果是发出的消息,则显示右边的消息布局,将左边的消息布局隐藏
                holder.rightLayout.setVisibility(View.VISIBLE);
                holder.leftLayout.setVisibility(View.GONE);
                holder.rightMsg.setText(msg.getContent());
            }
        }
    
        @Override
        public int getItemCount() {
            return mMsgList.size();
        }
    }

    5. MainActivity

    public class MainActivity extends AppCompatActivity {
        private List<Msg> msgList = new ArrayList<Msg>();
    
        private EditText inputText;
    
        private Button send;
    
        private RecyclerView msgRecyclerView;
    
        private MsgAdapter adapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initMsgs(); // 初始化消息数据
            inputText = (EditText) findViewById(R.id.input_text);
            send = (Button) findViewById(R.id.send);
            msgRecyclerView = (RecyclerView) findViewById(R.id.msg_recycle_view);
            LinearLayoutManager layoutManager = new LinearLayoutManager(this);
            msgRecyclerView.setLayoutManager(layoutManager);
            adapter = new MsgAdapter(msgList);
            msgRecyclerView.setAdapter(adapter);
            send.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String content = inputText.getText().toString();
                    if (!"".equals(content)) {
                        Msg msg = new Msg(content, Msg.TYPE_SENT);
                        msgList.add(msg);
                        // 当有新消息时,刷新ListView中的显示
                        adapter.notifyItemInserted(msgList.size() - 1); 
                        // 将ListView定位到最后一行
                        msgRecyclerView.scrollToPosition(msgList.size() - 1);
                        // 清空输入框中的内容
                        inputText.setText(""); 
                    }
                }
            });
        }
    
        /**
         * 初始化聊天消息
         */
        private void initMsgs() {
            Msg msg1 = new Msg("Hello guy.", Msg.TYPE_RECEIVED);
            msgList.add(msg1);
            Msg msg2 = new Msg("Hello. Who is that?", Msg.TYPE_SENT);
            msgList.add(msg2);
            Msg msg3 = new Msg("This is Tom. Nice talking to you. ", Msg.TYPE_RECEIVED);
            msgList.add(msg3);
        }
    }

    6. 遇到的问题

    代码还是要多动手敲一敲,看着很简单的代码折腾半天。

    期间聊天界面一直加载不出来,想试着调试。安卓程序没有调试过,而且是布局文件加载不出来,更加没头没脑了。接着发现布局加载的时候都会调用初始化的方法,主界面加载出来了,但是RecycleView没有加载,肯定是没有调用MsgAdapter的onCreateViewHolder()方法,一看果然没有。

    网上说有三种可能导致没有调用该方法:

    1.getItemCount()返回值<=0 ;
    2.好像要设置LinearLayoutManager ;
    3.被ScrollView嵌套

    调试之后发现,msg 的列表size为3,正常;LinearLayoutManager 也设置了;更本没有用到 ScrollView。

    一脸懵逼,最后想到可能是布局文件本身写错了。

    果然,主界面的布局文件少定义了 RecyclerView 的位置:android:orientation=”vertical”。

    所以代码要多敲,敲的时候要严谨。接下来继续学习安卓,希望早日能上手项目。

  • 相关阅读:
    安装Docker-Compose
    Docker微容器Alpine Linux
    Linux 常用命令
    如何定制博客园的个人空间
    Elasticsearch入门之从零开始安装ik分词器
    Elasticsearch入门实践
    写在2017年的总结
    开源ETL工具之Kettle介绍
    常用Java数据库连接池
    细说shiro之七:缓存
  • 原文地址:https://www.cnblogs.com/shuiyj/p/13185226.html
Copyright © 2020-2023  润新知