《第一行代码》 编写精美的聊天界面

版权声明:本文为博主原创文章,未经博主允许不得转载,欢迎留言。 https://blog.csdn.net/qq_15046309/article/details/82933369

第一部分:9.png图像的制作

遥想当年Android2.3的时代,手机不Root那基本没得玩,我那个时候荒废了不少时间搞什么系统美化,反编译啊什么的。虽然到头来竹篮打水,但是也算对计算机有了一定的兴趣。当时我最害怕就是9.png图像,因为只要涉及到修改这个的操作,那我是回编译不成功的。现在的我好好的瞥了两眼9.png的制作方法,这有什么难的?

首先《第一行代码中》的方法过时了,这项功能已经被集成到AS当中了,我们把图片放在文件夹里面后,点击图片右键选择create 9-Patch file

随后点击确认,在新生成的文件上操作就行了。具体的注意事项以及重点参见.9.png中四条黑线的意义。【注意:制作完成后,应该将原始文件删除,否则AS会分不清楚而报错。】

第二部分:制作聊天界面

  • 开始之前先把RecyclerView引过来。打开app中的build.gradle,在其中的dependencies中加上
    implementation 'com.android.support:recyclerview-v7:28.0.0'

这个与书上的不同,因为过时了。版本号与com.android.support:appcompat-v7:28.0.0相同即可。

随后同步build.gradle

  • 因为打开就显示界面,所以肯定是在主布局里面写。我们需要一个线性布局,布局应该是垂直的。上面是RecyclerCiew,底部有两个控件,左边是EditView右边是Button,但是因为主布局是垂直的,所以我们在布局中再加入一个水平的布局,底下的两个控件都在水平布局里面。
    <?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>
    

    这里复习一下layout_weight的用法,我印象中这个只有在LinearLayout中才可以使用,使用之后,系统会把所有组件的weight相加,然后把空间按需分配。但是与match_parent不同的是,系统会给其他控件保留最小的尊严。比如上面的的栗子,RecyclerView的weight为1,但它下面还有输入框和按钮,所以系统为下面两个控件留了位置。又比如下面输入框的weight也是1,系统在水平方向上为button保留了位置。【水平状态下,weight抵消width;垂直状态下,weight抵消height】

  • 因为我们使用了RecyclerView,所以我们需要为其编写子项布局。新建一个布局文件,我们意识到这个布局文件里面只会出现TextView,但是发出和接受消息的方向是不同的,比如发出的都会在右面出现,接受的都会在左面出现。所以我们要声明两个TextView。接下来就有意思了,书上面给出的栗子是每个TextView的外层嵌套了一个LinearLayout,而如果你直接写出来两个TextView在一个线性布局里面也是可以通过的,这是两者的区别。

对比图

可以看得出来,没有嵌套的 字几乎是贴着图片的,而嵌套着的显得更加的自然。这就是与郭神的差距啊。

<?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" />

    </LinearLayout>

</LinearLayout>

这里说一下layout_margin的含义,它在线性布局里的意思是view距离父view的距离。正因为如此,TextView才可以与嵌套着的LinearLayout形成缝隙,这是不嵌套没法做出来的。

  • 第三部分:RecyclerView适配器和信息类

信息类Msg,我们的消息分为接收的消息和发送的消息,他们唯一的区别就是在屏幕上的位置不同,因此我们需要定义消息的状态,int就可以。有了信息的状态,当然还需要信息的内容啊,接下来定义几个接收器就可以了。

package com.example.uibestpractice;

public class Msg {
    //普通的记录消息的类
    public static final int TYPE_RECEIVED=0;
    public static final int TYPE_SEND=1;
    private String content;
    private int Type;
    public Msg(String content,int Type)
    {
        this.content=content;
        this.Type=Type;
    }
    public String getContent()
    {
        return content;
    }
    public int getType()
    {
        return Type;
    }

}
  • 制作RecyclerView的适配器

开始制作适配器,都是一样的套路,首先声明一个类MsgAdapter继承RecyclerView.Adapter<MsgAdapter.ViewHolder>,其中ViewHolder是我们定义在类中的一个静态类,继承自RecyclerView.ViewHolder,在这个静态类中我们需要实例化我们RecyclerView布局中的控件,有两个LinearLayout和两个TextView,都在构造函数中进行,但是构造函数必须满足父类的构造函数,因此传一个view进去就可以了。

我们的主类的构造函数很简单,只需要接收一个List即可,将接受的List传给字段。这个List里面存储的都是Msg类的对象。

随后重写 onCreateViewHolder,onBindViewHolder以及getItemCount三种方法。先来说最简单的getItemCount,只需要返回list的size即可。

接着看onCreateVIewHolder,这个方法是动态RecyclerView实例的,所以声明

View view =LayoutInflater.from(paremt.context).inflate(子项id,parent,false);
retuen new ViewHolder(view);

再来onBindViewHolder,这个是要赋给内部类字段属性,首先先建立对应position的Msg的对象,如果这个对象的是接受的消息,那么我们就把对应发送消息的LinearLayout,view.GONE掉。,显示出来接受消息的LinearLayout。注意一定是GONE,不是INVISIBLE,否则后果如下:

上正确的代码

package com.example.uibestpractice;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.List;

public 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;
        public ViewHolder(View view)
        {
            super(view);
            leftLayout=(LinearLayout) view.findViewById(R.id.left_layout);//因为layout是在文件中,所以是R.id
            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> mMsgList){
    this.mMsgList=mMsgList;
    }

    @Override
    public ViewHolder onCreateViewHolder( ViewGroup parent, int i) {
        View view=LayoutInflater.from(parent.getContext()).inflate(i,parent,false);
        return new ViewHolder(view);
    }

    @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_SEND)
        {
            holder.rightLayout.setVisibility(View.VISIBLE);
            holder.leftLayout.setVisibility(View.GONE);
            holder.rightMsg.setText(msg.getContent());
        }
    }

    @Override
    public int getItemCount() {
        return mMsgList.size();
    }
}
  • 最后就是在主类中实例化了​​​​

首先实例化RecyclerView;因为我们要点击button,所以也有它的份;实例化button为了获得EditView的内容啊,所以这个编辑框也得实例化;实例化适配器;最后规定RecyclerView的格式,这次依然是传统格式。

我们还需要为button建立一个监听器,我们需要将编辑框的内容发送到RecyclerView上

①获取内容,将字符串添加到List当中去。

②调用适配器的notifyItemInserted方法,通知有新的数据加入了,赶紧将这个数据加到RecyclerView上面去。

③调用RecyclerView的scrollToPosition方法,以保证一定可以看的到最后发出的一条消息。

最后我没有按照书上的栗子初始化List,修改了一下,现在你可以自己和自己聊天了。

package com.example.uibestpractice;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private List<Msg> msgList=new ArrayList<>();
    private EditText editText;
    private Button button;
    private RecyclerView recyclerView;
    private  MsgAdapter adapter;
    private boolean Send=true;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       editText=(EditText) findViewById(R.id.input_text);
       button=(Button) findViewById(R.id.send);
       recyclerView=(RecyclerView) findViewById(R.id.msg_recycle_view);
       adapter=new MsgAdapter(msgList);
        LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager);
        recyclerView.setAdapter(adapter);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String content=editText.getText().toString();
                if (!"".equals(content))
                {
                    Msg msg;
                    if (Send)
                    { msg=new Msg(content,Msg.TYPE_SEND);}
                    else
                    { msg=new Msg(content,Msg.TYPE_RECEIVED);
                    }
                    Send=!Send;
                    msgList.add(msg);
                    adapter.notifyItemInserted(msgList.size()-1);
                    recyclerView.scrollToPosition(msgList.size()-1);
                    editText.setText("");
                }
            }
        });
}

希望所有认真的同学都能有一个好的结果,祝你晚安。

                                                                                                                                  ------2018.10.3于图书馆

猜你喜欢

转载自blog.csdn.net/qq_15046309/article/details/82933369