03UI开发-RecyclerView及简单聊天界面

基本用法

  1. 想要使用这个控件,首先在项目的build.gradle中添加相应的依赖库才行
    打开app/build.gradle文件,在dependencies闭包中添加
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    // 添加的是这一句
    compile 'com.android.support:recyclerview-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

  1. 添加完成后点击一下Sync Now来进行同步,然后修改activity_main.xml中的代码



    


添加一个控件,宽度和高度都是占满整部布局空间的

  1. 接下来定义一个自定义布局fruit_item.xml,这个时候注意layout_width="wrap_content"的设置,让它自适应大就可以了



    

    



4.定义一个实体类,作为RecyclerView适配器的适配类型,建立Fruit


public class Fruit {
    private String name;
    private int imageId;

    public Fruit(String name,int imageId){
        this.imageId = imageId;
        this.name = name;
    }
    public String getName() {
        return name;
    }

    public int getImageId() {
        return imageId;
    }
}

  1. 为RecyclerView准备一个适配器,新建FruitAdapter类,让这个适配器继承RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder,其中ViewHolder是我们在FruitAdapter中定义的一个内部类

public class FruitAdapter extends RecyclerView.Adapter {

    private List mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder{
        ImageView fruitImage;
        TextView fruitName;

        public ViewHolder(View view){
            super(view);
            fruitImage=(ImageView)view.findViewById(R.id.img);
            fruitName = (TextView)view.findViewById(R.id.text);
        }
    }

    public FruitAdapter(List fruitList){
        mFruitList = fruitList;
    }

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

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

    @Override
    public int getItemCount() {
        return mFruitList.size();
    }


}


  • 首先定义了一个内部类,ViewHolder要继承自RecyclerView.ViewHolder,然后在这个里面的构造函数中传入了一个View参数,这个参数通常就是RecyclerView子项的最外层布局,那么就可以通过这个findViewById()的方法获取到布局中的ImageVIew和TextView的实例了
  • FruitAdapter中也有一个构造函数,这个函数用于将把要展示的数据源传递进来,并赋值给一个全局变量mFruitList,后面操作的就是这个数据源了
  • 由于FruitAdapter继承自RecyclerView.Adapter,那么就必须重写 onCreateViewHolder(),onBindViewHolder(),getItemCount()这三个方法,
  • onCreateViewHolder()方法用于创建一个ViewHolder实例,并把加载出来的布局传入到构造函数中,最后将ViewHolder的实例返回
  • onBindViewHolder()方法用与对RecyclerView子项的数据进行赋值,会在每个子项滚动到屏幕内的时候执行,通过position参数可以获得当前项的Fruit实例,然后再将数据设置到ViewHolder的ImageView和TextVIew中
  • getItemCount()用于告诉RecyclerVIew一共有多少个子项,直接返回数据源的长度
  1. 接下来就可以使用RecyclerView,修改MainActivity中的代码

public class MainActivity extends AppCompatActivity {

    private List fruitList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 用于初始化水果
        initFruits();
        RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view_1);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        FruitAdapter adapter = new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }


    private void initFruits(){
        for (int i= 0;i<2;i++){
            Fruit apple = new Fruit("Apple",R.drawable.apple_pic);
            fruitList.add(apple);
            Fruit banana = new Fruit("Banana",R.drawable.banana_pic);
            fruitList.add(banana);
            Fruit orange = new Fruit("orange",R.drawable.orange_pic);
            fruitList.add(orange);
            Fruit watermelon = new Fruit("watermelon",R.drawable.watermelon_pic);
            fruitList.add(watermelon);
            Fruit pear = new Fruit("pear",R.drawable.pear_pic);
            fruitList.add(pear);
            Fruit grape = new Fruit("grape",R.drawable.grape_pic);
            fruitList.add(grape);
            Fruit pineapple = new Fruit("pineapple",R.drawable.pineapple_pic);
            fruitList.add(pineapple);
            Fruit strawberry = new Fruit("strawberry",R.drawable.strawberry_pic);
            fruitList.add(strawberry);
            Fruit cherry = new Fruit("cherry",R.drawable.cherry_pic);
            fruitList.add(cherry);
            Fruit mango = new Fruit("mango",R.drawable.mango_pic);
            fruitList.add(mango);
        }
    }

}

  • 使用了同样的initFruits()方法,用于初始化水果的数据,接着onCreate()方法获取到RecyclerView的实例,
  • 创建一个LayoutManager用于指定RecycleView的布局方式,这里使用的是LinearLayoutManager是线性布局的意思,可以实现和ListView类似的效果,
  • 接下来创建一个FruitAdapter的实例,将水果的数据传入到FruitAdapter的构造函数中
  • 最后调用RecyclerView的setAdapter()方法来完成适配器的设置,这样RecyclerVIew和数据之间的关联就建立完成了


    03UI开发-RecyclerView及简单聊天界面_第1张图片
    2018-03-11_16-06-47.png

实现横向滚动和瀑布流布局

  1. 首先要对fruit_item布局进行修改,实现横向滚动,得把这里面的元素改成垂直排列的方式



    

    



  • android:orientation="vertical"该成了垂直方向排列,而且宽度色设置为100dp,这样不会因为每种水果名字的长度不一而导致RecycleView的各个子项长短不一
  • android:layout_gravity="center_horizontal" 都设置为水平居中
  1. 修改MainActivity中的代码
package com.example.md.recyclerview;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

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

public class MainActivity extends AppCompatActivity {


    private List fruitList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 用于初始化水果
        initFruits();
        RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view_1);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        // 多添加这一句话
        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        recyclerView.setLayoutManager(layoutManager);
        FruitAdapter adapter = new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }
        
    ..............

}


  • 相比之下,只多了一行代码,调用了layoutManager的setOrientation()方法来设置布局的排列方法,默认是纵向的,传入LinearLayoutManager.HORIZONTAL表示让布局横向排列
    03UI开发-RecyclerView及简单聊天界面_第2张图片
    Snipaste_2018-03-11_16-20-07.png
  1. 除了LinearLayoutManager之外,RecyclerView还提供了GridLayoutManagerStaggeredGridLayoutManager这样中内置的布局排列方式,GridLayoutManager是用于网络布局,StaggeredGridLayoutManager用于瀑布布局
    修改fruit_item.xml中的代码



    

    



只有几处调整,把宽度有100dp改成match_parent,因为瀑布的宽度是根据布局的列数自动匹配的,不是一个固定的值,还用margin属性,让子项之间留点间距,文字的对齐方式改成了左对齐

  1. 修改MainActivity中的代码

public class MainActivity extends AppCompatActivity {


    private List fruitList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 用于初始化水果
        initFruits();
        RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view_1);
//        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        // 多添加这一句话
//        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

//      主要就是这一行代码,瀑布
        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(
                3, StaggeredGridLayoutManager.VERTICAL
        );

        // 网络
        //GridLayoutManager layoutManager = new GridLayoutManager(this,4);

        recyclerView.setLayoutManager(layoutManager);
        FruitAdapter adapter = new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }

    private void initFruits(){
        for (int i= 0;i<2;i++){
            Fruit apple = new Fruit(getRandomLengthName("Apple"),R.drawable.apple_pic);
            fruitList.add(apple);
            Fruit banana = new Fruit(getRandomLengthName("Banana"),R.drawable.banana_pic);
            fruitList.add(banana);
            Fruit orange = new Fruit(getRandomLengthName("orange"),R.drawable.orange_pic);
            fruitList.add(orange);
            Fruit watermelon = new Fruit(getRandomLengthName("watermelon"),R.drawable.watermelon_pic);
            fruitList.add(watermelon);
            Fruit pear = new Fruit(getRandomLengthName("pear"),R.drawable.pear_pic);
            fruitList.add(pear);
            Fruit grape = new Fruit(getRandomLengthName("grape"),R.drawable.grape_pic);
            fruitList.add(grape);
            Fruit pineapple = new Fruit(getRandomLengthName("pineapple"),R.drawable.pineapple_pic);
            fruitList.add(pineapple);
            Fruit strawberry = new Fruit(getRandomLengthName("strawberry"),R.drawable.strawberry_pic);
            fruitList.add(strawberry);
            Fruit cherry = new Fruit(getRandomLengthName("cherry"),R.drawable.cherry_pic);
            fruitList.add(cherry);
            Fruit mango = new Fruit(getRandomLengthName("mango"),R.drawable.mango_pic);
            fruitList.add(mango);
        }
    }

// 不想实现也可以不用写
    private String getRandomLengthName(String name){
        Random random = new Random();
        int length = random.nextInt(20) + 1;
        StringBuilder builder = new StringBuilder();
        for(int i = 0;i
  • 首先在onCreate()方法中,创建了StaggeredGridLayoutManager的实例,它的构造函数接收了两个参数,第一个用于指定布局的列数,传入3表示会把布局分为3列,第二个参数用于指定布局的排列方向, StaggeredGridLayoutManager.VERTICAL表示是纵向排列,最后再把创建好的实例设置到RecyclerView中
  • 这里使用了一个小技巧,在这个getRandomLengthName()方法上,这个方法用了Random对象来创造一个1到20之间的随机数,然后将参数中传入的字符重复随即便,在initFruits()方法中,每个水果的名字都调用这个方法来生成,这样就保证水果的名字的长短差距大,子项的高度就不一样了


    03UI开发-RecyclerView及简单聊天界面_第3张图片
    2018-03-11_16-45-14.png
  • 若不写这个方法也是可以的,在传入的时候直接写入水果的名字也可以


    03UI开发-RecyclerView及简单聊天界面_第4张图片
    2018-03-17_11-13-27.png
  • 当然了,还可以写网络布局,只需要创建一个GridLayoutManager实例就可以了,它的构造函数接收两个参数,第一个是上下文对象,第二个参数是一行显示几列数据


    03UI开发-RecyclerView及简单聊天界面_第5张图片
    2018-03-17_11-19-17.png

RecyclerView的点击事件

这个的注册监听器方法是需要我们自己给子项具体的View去注册点击事件的

  1. 修改FruitAdapter中的代码

public class FruitAdapter extends RecyclerView.Adapter {

    private List mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder{
        // 这里定义一个View变量
        View fruitView;
        ImageView fruitImage;
        TextView fruitName;

        public ViewHolder(View view){
            super(view);
            // 保存最外层的布局实例
            fruitView  = view;
            fruitImage = view.findViewById(R.id.img);
            fruitName = view.findViewById(R.id.text);
        }
    }

    public FruitAdapter(List fruitList){
        mFruitList = fruitList;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
       // 
        final ViewHolder holder = new ViewHolder(view);

        //分别为最外层的布局和Image注册点击事件
        holder.fruitView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 获取到position,然后拿到Fruit实例
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(),"you click view"+fruit.getName(),Toast.LENGTH_SHORT).show();
            }
        });
        holder.fruitImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(),"you clcik image"+fruit.getImageId(),Toast.LENGTH_SHORT).show();
            }
        });
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

    @Override
    public int getItemCount() {
        return mFruitList.size();
    }
}

  • 先修改ViewHolder,在ViewHolder中添加fruitView变量来保存子项最外层布局的实例,然后在onCreateViewHolder()方法中注册点击事件
  • 分别为最外层的布局和Image都注册了事件,先获取到用户点击的position,然后通过position拿到响应的Fruit实例,然后通过Toast显示


    03UI开发-RecyclerView及简单聊天界面_第6张图片
    点击图片.png

    03UI开发-RecyclerView及简单聊天界面_第7张图片
    点击文字.png

了解Nine-Patch

  1. 现在项目中有一个气泡状的图片,将这个图片设置为背景图片




03UI开发-RecyclerView及简单聊天界面_第8张图片
2018-03-17_13-53-54.png

这个时候可以看到图片被均匀的拉伸了,效果极差,这个时候就可以使用Nine-Patch来处理了

  1. 右击要添加的图片,点击Create 9-Patch file


    03UI开发-RecyclerView及简单聊天界面_第9张图片
    2018-03-17_13-55-30.png
  2. 就进入到这个页面


    03UI开发-RecyclerView及简单聊天界面_第10张图片
    2018-03-17_13-57-25.png

    我们可以在四周绘制一个个小黑点,按住Shift键拖动就可以进行擦除,使用这个新的图片替换原来的,再运行程序就可以看到


    03UI开发-RecyclerView及简单聊天界面_第11张图片
    2018-03-17_14-00-18.png
  3. 这样,当图片需要拉伸的时候,就指定拉伸的区域,在外观上就好看了

编写聊天界面

  1. 首先使用到的是RecyclerView,所以首先进行在app/build.gradle中添加依赖库
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    // 添加这行代码
    compile 'com.android.support:recyclerview-v7:26.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

  1. 编写主页面,修改activity_main.xml



    

    

        

        
  • 在主页面中放一个RecycleView用于显示聊天的消息内容,又放置了一个输入框,和一个按钮
  1. 定义消息的实体类,新建一个Msg
public class Msg {
    // 接收到一条消息
    public static final int TYPE_RECEVED = 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 String getContent(){
        return content;
    }
    public int getType(){
        return type;
    }
}

  • Msg中只有两个字段,content表示消息的内容,type表示消息的类型,其中消息的类型有两个值可选,一个是TYPE_RECEVED表示接收到的数据,TYPE_SENT表示发送的数据
  1. 接下来编写RecyclerView的子项布局,新建msg_item.xml



    

        

    


    

        

    




  • 接收到的消息左对齐,发送的消息右对齐,这个时候问题来了,怎么让收到的消息和发出的消息在一个布局中呢在这一个布局中,必定有一个控件是空白的在学基本控件的时候学到了可见属性,稍后在代码中可以根据这个消息的类型来决定隐藏和显示那种消息
  1. 编写自定义的MagAdapter适配器,和在RecyclerView写的适配器代码基本一样

public class MsgAdapter extends RecyclerView.Adapter {

    private List mMsgList;

    static class ViewHolder extends RecyclerView.ViewHolder{

        LinearLayout leftLayout;
        LinearLayout rightLayout;
        TextView lefMsg;
        TextView rightMsg;

        public ViewHolder(View itemView) {
            super(itemView);
            leftLayout = (LinearLayout)itemView.findViewById(R.id.left_layout);
            rightLayout = (LinearLayout)itemView.findViewById(R.id.right_layout);
            lefMsg = (TextView)itemView.findViewById(R.id.left_msg);
            rightMsg = (TextView)itemView.findViewById(R.id.right_msg);
        }

    }

    public MsgAdapter(List msgList){
        mMsgList = msgList;
    }

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

    @Override
    public void onBindViewHolder(MsgAdapter.ViewHolder holder, int position) {
        Msg msg = mMsgList.get(position);
        if (msg.getType() == Msg.TYPE_RECEVED){
            // 如果是接收到的消息,则显示左边的消息布局,将右边的隐藏
            holder.leftLayout.setVisibility(View.VISIBLE);
            holder.rightLayout.setVisibility(View.GONE);
            holder.lefMsg.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();
    }
}


  1. 修改MainActivity中的代码

public class MainActivity extends AppCompatActivity {

    private List msgList = new ArrayList<>();
    private EditText inputText;
    private Button send;
    private RecyclerView msgRecyclerView;
    private MsgAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);
        // 初始化消息数据
        initMsag();
        inputText = (EditText)findViewById(R.id.edit_1);
        send = (Button)findViewById(R.id.send);
        // 获取到这个滚动控件
        msgRecyclerView = (RecyclerView)findViewById(R.id.recyc_1);
        // 指定这个控件的布局方式
        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 view) {
                // 获取到消息框中的内容
                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("");
                }
            }
        });


    }

    public void initMsag(){
        Msg msg = new Msg("hello pony",Msg.TYPE_RECEVED);
        msgList.add(msg);
        Msg msg1 = new Msg("hello Tom",Msg.TYPE_SENT);
        msgList.add(msg1);
        Msg msg2 = new Msg("我们去哪里呀?",Msg.TYPE_RECEVED);
        msgList.add(msg2);
    }
}


  • 在initMsag()中初始化了几条数据,用于在RecyclerView中显示
  • 在点击按钮的时候,如果内容不为空,就创建了一个Msg对象,并且添加到了msgList中去
  • 然后调用适配器的notifyItemInserted()方法,用于通知列表中有新的数据插入,这样新增的一条消息才能够在RecyclerVIew中显示,
  • 接着调用RecyclerView的scrollToPosition()方法,将数据定位到最后一行,
  • 最后调用setTExt()方法将输入的内容清空


    03UI开发-RecyclerView及简单聊天界面_第12张图片
    2018-03-17_16-13-25.png

参考数据:第一行代码
若有错误请指出

你可能感兴趣的:(03UI开发-RecyclerView及简单聊天界面)