Android03——UI

UI开发

常用控件

TextView

  • 包括对齐方式graivty、颜色color、大小size。
  • 另外可以使用"|"来同时指定多个值.|左右没有空格。
  • 布局大小layout的固定值单位是dp,这是一种屏幕密度无关的尺寸单位,可以保证在不同手机上显示相同的效果。
    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal|center_vertical"
        android:textColor="#00ff00"
        android:textSize="24sp"
        android:text="This is a TextView!" />

Button

  • Button默认是TEXT都是大写,如果需要小写,需要把textAllCaps改为false
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button"
        android:textAllCaps="false"/>

EditText

  • 允许用户在控件中输入和编辑内容,并可以在程序中对这些内容进行处理。
  • hint是用于写入提示用的。
  • 可能存在输入文字过多的问题,可能存在超过一行的情况,这个时候使用maxLines。2表示输入的内容超过两行时,文本就会向上滚动,EditText则不会再继续拉伸。
    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Type something here"/>

ImageView

ImageView是用于在界面上展示图片的一个控件。

  1. 在res目录下创建一个drawable-xxhdpi目录。放入img【注意这个地方是选择的project而不是app】

    1. app只负责显示,不同文件夹下的创建还是在project下【这也是xlm中的路径】

  2. 修改主页布局activity_main.xml

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/apple_pic"/>
    
  3. 动态的修改ImageView中的图片,比如在回调函数中来写(同理java中的代码也是根据app的):

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button showEditTextButton = (Button) findViewById(R.id.showEditTextButton);
            final EditText editText = (EditText) findViewById(R.id.editText);
            ImageView imageView = (ImageView)findViewById(R.id.imageView);
            showEditTextButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this,editText.getText().toString(),Toast.LENGTH_SHORT).show();
                    imageView.setImageResource(R.drawable.banana_pic);
                }
            });
        }
    

ProgressBar

progressBar用于在界面上显示一个进度条,表示我们的程序正在加载一些数据。它的用法也非常简单,修改activity_main.xml中的代码,如下所示:

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

通过Android:visibility指定控件的可见属性:

  • visible:可见
  • invisible:不可见
  • gone:不可见+不占用资源

切换轮播图效果:

@Override
public void onClick(View v) {
    Toast.makeText(MainActivity.this,editText.getText().toString(),Toast.LENGTH_SHORT).show();
    if(progressBar.getVisibility() == View.VISIBLE){
        imageView.setImageResource(R.drawable.banana_pic);
        progressBar.setVisibility(View.GONE);
    }else {
        imageView.setImageResource(R.drawable.apple_pic);
        progressBar.setVisibility(View.VISIBLE);
    }

}

设置横线进度条:

  1. 通过style="@style/Widget.AppCompat.ProgressBar.Horizontal"把圆的变成横线。
  2. 通过android:max="100"。设置最大值为100
  3. 修改java中的代码progressBar.progress = progressBar.progress +10

TODO:【做成一个每日任务完成进度条 =task_name + expected_comsume_time】

AlertDialog

alertDialog可以子啊当前界面弹出一个对话框,这个对话框置顶了。

直接在activity代码中使用即可。

@Override
public void onClick(View v) {
    int progress = progressBar.getProgress();
    if( progress ==progressBar.getMax()){
        AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
        dialog.setTitle("progressBar is filled, will you want to restart?");
        dialog.setCancelable(false);
        /**
                     * 如果Yes就从头开始计算progressBar
                     */
        dialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                progressBar.setProgress(0);
            }
        });
        dialog.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });
        dialog.show();
    }else {
        progress = progress + 10;
        progressBar.setProgress(progress);
    }

}
});

三种基本布局

LinearLayout

线性布局,线性方向上依次排列控件。方向的选择取决于android:orientation。而控件的对齐方式则是根据android:layout_gravity

RelativeLayout

relativelayout相对布局,

  • 相对父layout布局:[上对齐+左对齐] [中心对齐]

    • android:layout_alignParentRight
    • android:layout_alignParentTop
    • android:layout_centerInParent
  • 相对于控件对齐:

    • android:layout_above="@id/button3"
    • android:layout_toLeftOf="@id/button3"
    • android:layout_toRightOf="@id/button3"
    • android:layout_below="@id/button3"
    image-20201117235822216
  • 另外一组相对于控件进行定位的属性

    • android:layout_alignLeft="@id/button3"表示让一个控件和另外一个控件的左边缘对齐

    • 等等

      image-20201118000431940

FrameLayout

所有控件都默认在布局的左上角,

ConstrainLayout

这个布局替代了弃用的百分比布局。不过使用relativelayout + linear也够用了。

img

虽说现在Button已经添加到界面上了,但是由于我们还没有给Button添加任何的约束,因此Button并不知道自己应该出现在什么位置。现在我们在预览界面上看到的Button位置并不是它最终运行后的实际位置,如果一个控件没有添加任何约束的话,它在运行之后会自动位于界面的左上角。

  • 给Button添加约束,每个控件的约束都分为垂直和水平两类,一共可以在四个方向上给控件添加约束:

    • 相对于parent_layout的约束
      img

    • 相对于控件的约束
      img

  • 删除约束的方式一共有三种

    • 选中约束圆圈然后delete
    • 删除某一个控件的所有约束,选中一个控件,反键删除即可
    • 删除整个layout的约束,在蓝图上面有一个删除按钮
      img
  • Inspector:选中控件右边就会有Properties,其中包括控件的所有属性,如文本内容、颜色、点击事件等等。Properties区域的上半部分,这部分也被称为Inspector

    • Inspector中有一个纵向的轴和一个横向的轴,这两个轴也是用于确定控件的位置的
    • 位于Inspector最中间的那个正方形区域,它是用来控制控件大小的。一共有三种模式可选,每种模式都使用了一种不同的符号表示,点击符号即可进行切换。
      • img 表示wrap content,这个我们很熟悉了,不需要进行什么解释。
      • img 表示固定值,也就是给控件指定了一个固定的长度或者宽度值。
      • img 表示any size,它有点类似于match parent,但和match parent并不一样,是属于ConstraintLayout中特有的一种大小控制方式,下面我们来重点讲解一下。
        • 首先需要说明,在ConstraintLayout中是有match parent的,只不过用的比较少,因为ConstraintLayout的一大特点就是为了解决布局嵌套,既然没有了布局嵌套,那么match parent也就没有多大意义了。
        • 而any size就是用于在ConstraintLayout中顶替match parent的,比如你想让整个button的宽度充满整个布局。只需要修改为any_size然后设置为0dp(左侧的间距设置成0)即可。
        • 和match parent最大的区别在于,match parent是用于填充满当前控件的父布局,而any size是用于填充满当前控件的约束规则。
  • Guidelines:上面的方法很容易时间一个控件的居中,如果我们想让两个按钮共同居中对齐需要用到ConstraintLayout中的一个新的功能,Guidelines。

    • TODO:没学会
  • 自动添加约束:TODO

  1. 只有linearLayout支持使用layout_weight属性来控制控件的大小

  2. relativeLayout很难实现两个按钮平分布局宽度的效果。

创建自定义控件

所有的控件都是继承自View的。所有的布局都是继承自ViewGroup的。View是Android中最基本的一种UI组件,它可以在屏幕上绘制一个矩形区域,并能响应这块区域的各种事件。而ViewGroup则是一种特殊的控件,他可以包含很多子view和子viewgroup,是一个用于放置控件和布局的容器。

引入布局

类似于Vue的组件吧,布局也是可以引入的,A布局引入B布局的方法:

<include layout="@layout/bottom_layout"/>

引入布局可以解决重复编写布局代码的问题。但是布局中有一些控件要求能够响应事件,因此还需要自定义控件

创建自定义控件

TOOD

ListView【过时】

ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据会滚动出屏幕。

ListView简单用法

  • 首先创建一个数组把 数据放入 比如String[]。
  • 由于集合中的数据是无法直接传递给ListView的,我们还需要借助适配器来完成。适配器的实现类有很多,常用的是ArrayAdapter。
    • ArraryAdapter的参数有activity实例、listview子项布局的id以及数据源。
    • 而子项布局使用了android.R.layout.simple_list_item_1作为ListView子项布局的id,这个是一个Android内置的布局文件,里面只有一个testview用于简单显示一段文本。
  • 最后调用ListView的setAdapter()方法将构建好的适配器对象传递进去。
	// private String[] data = {"apple","banana"...}
	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /**
         * android.R.layout. 和 R.layout. 的区别?
         */
        ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_expandable_list_item_1, data);
        ListView listView = (ListView)findViewById(R.id.listView);
        listView.setAdapter(adapter);
    }

定制ListView的界面

  • 创建Fruit类,——POJO类
  • 为ListViewd的子项制定一个自定义布局,创建一个fruit_item.xml。主要包括一个imageView和一个TextView
  • 创建adapter类。
    • 重写了父类的一组构造函数,用于将context、listview子项布局的id(就是上面这个布局)和数据都传递出来。
    • 重写了另外一个方法getView。这个方法在每个子项被滚动到屏幕内的时候会被调用。
      • 首先通过getItem()方法得到当前项的Fruit实例
      • 然后使用LayoutInflater来为这个子项加载我们传入的布局。LayoutInflater的inflate方法接受三个参数,第一个是fruit_item这个布局、第二个是被adapter的水果array集合。第三个参数默认是false。
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Fruit fruit = getItem(position);  // 获取当前项的fruit实例
        View view = LayoutInflater.from(getContext())
                .inflate(resourceId, parent, false);
        ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
        TextView fruitText = (TextView) view.findViewById(R.id.fruit_name);
        fruitImage.setImageResource(fruit.getImageId());
        fruitText.setText(fruit.getName());
        return view;
    }
  • 在mainActivity上面展示出来。

    • 通过把Fruit类放入集合中 ->initfruit()。自己创建的包名.R$drawable获取内部类drawable。然后getField()获取图片的field
    private void initFruits(){
        for(int i=0;i<data.length;i++) {
            String imageName = data[i]+"_pic";
            try {
                field = Class.forName("com.ssozh.listviewtest.R$drawable").getField(imageName);
                Fruit apple = new Fruit(data[i],field.getInt(R.drawable.class));
                fruitList.add(apple);
                Log.d("initFruits",fruitList.toString());
            } catch (NoSuchFieldException | ClassNotFoundException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initFruits();
            FruitAdapter fruitAdapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
            ListView listView = (ListView)findViewById(R.id.listView);
            listView.setAdapter(fruitAdapter);
        }
    

提升listview的运行效率

使用getView()方法的参数convertView将之前加载好的布局进行缓存,以便之后可以进行使用。=>不重复加载布局

继续优化=>getView()会调用View的findViewId方法来获取一次控件的实例。=>借助ViewHolder来对这个部分进行优化。

具体就是通过新增一个内部类ViewHolder,用于对控件实例进行缓存。当converView为null的时候,创建一个ViewHolder对象,并将控件的实例都存放在ViewHolder里面,然后调用View的setTag()方法,将ViewHolder对象存储在View中。

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    Fruit fruit = getItem(position);  // 获取当前项的fruit实例
    ViewHolder viewHolder = null;
    View view;
    if (viewHolder == null) {
        view = LayoutInflater.from(getContext())
                .inflate(resourceId,parent,false);
        viewHolder = new ViewHolder();
        viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
        viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
        view.setTag(viewHolder);
    }else {
        view = convertView;  // converView 就是缓存
        viewHolder = (ViewHolder) view.getTag();
    }
    viewHolder.fruitImage.setImageResource(fruit.getImageId());
    viewHolder.fruitName.setText(fruit.getName());
    return view;
}

class ViewHolder{
    ImageView fruitImage;
    TextView fruitName;
    public ViewHolder(){}
}

ListView点击事件

和button的点击事件不同,他的点击事件是itemClick而不是直接的clickj同样的,new的View也是adapter的。

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Fruit fruit = fruitList.get(position);
        Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_SHORT).show();
    }
});

RecyclerView【推荐】

增强版的ListView,不仅可以实现和listView一样的效果,还优化了listview存在的各种不足之处。

image-20201119160340192

recyclerView基本用法

  1. 写一个RecyclerView的子项:注意Layout占满屏幕可能是因为布局属性没改:https://blog.csdn.net/clzh2013/article/details/53897357

  2. 定义一个adapter

    • 首先定义一个内部类ViewHolder,构造函数传入View参数,这个参数据说RecyclerView子项的最外层布局。
    • 重写onCreateViewHolder方法,用来创建ViewHolder实例。
    • 重写onBindViewHolder()方法,用来对RecyclerView子项的数据进行赋值,会在每个子项被滚动到屏幕内的时候执行。
    • getItemCount()就是用于高速RecyclerView一共有多少个子项,
    public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
        private List<Fruit> mFruitList;
    
        /**
         * 构造函数把Fruit集合传入
         */
        public FruitAdapter(List<Fruit> fruitList) {
            mFruitList = fruitList;
        }
    
        /**
         * 创建view 然后创建viewHolder把view放入然后返回。
         * 这个方法就类似于ListView的getView
         */
        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
            return new ViewHolder(view);
        }
    
        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            Fruit fruit = mFruitList.get(position);
            holder.fruitName.setText(fruit.fruitName);
            holder.fruitImage.setImageResource(fruit.fruitImage);
        }
    
    
        @Override
        public int getItemCount() {
            return mFruitList.size();
        }
    
        class ViewHolder extends RecyclerView.ViewHolder {
            ImageView fruitImage;
            TextView fruitName;
    
    
            public ViewHolder(@NonNull View itemView) {
                super(itemView);
                fruitImage = itemView.findViewById(R.id.fruit_image);
                fruitName = itemView.findViewById(R.id.fruit_name);
            }
    
            @Override
            public String toString() {
                return super.toString();
            }
        }
    }
    
  3. 在MainActivity中使用recyclerView

    1. 关于反射:fruitList.add(new Fruit(data[i], field.getInt(null)));Android这个地方可以传入null 暂时还是不是恨透侧。
    public class MainActivity extends AppCompatActivity {
        private List<Fruit> fruitList = new ArrayList<>();
        private String[] data = {"apple","banana","orange","watermelon","pear","grape","pineapple","strawberry","cherry","mango","apple","banana","orange","watermelon","pear","grape","pineapple","strawberry","cherry","mango"};
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initFruits();
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
            RecyclerView recyclerView =(RecyclerView) findViewById(R.id.recyclerView);
            recyclerView.setLayoutManager(linearLayoutManager);
            FruitAdapter fruitAdapter = new FruitAdapter(fruitList);
            recyclerView.setAdapter(fruitAdapter);
    
        }
    
        private void initFruits(){
    
            for(int i=0;i<data.length;i++) {
                String imageName = data[i] + "_pic";
                Class clazz = null;
                try {
                    clazz = Class.forName("com.ssozh.recyclerview.R$drawable");  // 注意必须这么写 如果写为com.ssozh.R.drawable这样提示写法是错的反射!!!
                    Field field = clazz.getField(imageName);
                    // 通过class.getField返回指定的field域,然后通过fiedl.get(obj)返回obj对象中用Field对象表示的值域。
                    fruitList.add(new Fruit(data[i], field.getInt(null)));
                } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits();
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        // 重点在这一句!!!
        linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        RecyclerView recyclerView =(RecyclerView) findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(linearLayoutManager);
        FruitAdapter fruitAdapter = new FruitAdapter(fruitList);
        recyclerView.setAdapter(fruitAdapter);

    }

之所以RecyclerView可以横向滚动,是因为RecyclerView将布局排列的任务交给了LayourManager而ListView则是由自身管理的。

RecyclerView提供了GridLayoutManager、StaggerGridLayoutManager和LinearLayoutManager三种内置的布局排泄方法。

  • GridLayouManager可以用于实现网格布局
  • StaggerGridLayoutManager可以用于实现瀑布流布局。

RecyclerView的点击事件

RecyclerView没有提供类似于setOnItemClickListener)这样的注册监听器的方法,而是需要我们自己给子项具体的view去注册点击事件。

之所以这么做,是因为recyclerview可以给子项中具体的某一个按钮(view)去注册。

=>具体实现是修改fruitAdapter中的代码:

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
        ViewHolder viewHolder = new ViewHolder(view);
        // 点击事件写在这里
        viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = viewHolder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(),"you click view" + fruit.getFruitName(),Toast.LENGTH_SHORT).show();
            }
        });
        viewHolder.fruitImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = viewHolder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(),"you click view" + fruit.getFruitName(),Toast.LENGTH_SHORT).show();
            }
        });
        return viewHolder;
    }

编写界面的最佳实践

制作9-patch图片

9-patch图片是一种经过特殊处理过的png图片,能够制定哪些区域可以被拉伸、哪些区域不可以。在Android studio中,我们可以将任何png类型的图片制作成9-patch图片。

编写精美的聊天界面

  • 在主界面放一个recyclerView用于显示聊天的消息内容,又放置了一个editText用于输入信息,一个button用于发送消息。

    • 其中editText和Button放在同一个LinearLayout中
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/msg_recycler_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>
    
  • 编写RecyclerVew的子项布局msg_item.xml【LinearLayout】(第三版书包括发送消息的子布局msg_left_item.xml和接收消息的子布局msg_right_item.xml)

        <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="#000"/>
        </LinearLayout>
    
  • 定义消息实体类,新建Msg

    • 内容content
    • type标识是发送还是接受
    • 添加final静态变量表示上面两种type:
        public final static int TYPE_RECEIVED = 0;
        public final static int TYPE_SEND = 1;
        private String content;
        private int type;
    
  • 编写上面消息类实体的MsgAdapter:

    • 定义静态内部类ViewHolder包括:左右消息子布局【linearLayout】和两个TextView

      LinearLayout leftLayout;
      LinearLayout rightLayout;
      TextView leftMsg;
      TextView rightMsg;
      public ViewHolder(@NonNull View itemView) {
          super(itemView);
          leftLayout = (LinearLayout) itemView.findViewById(R.id.left_layout);
          leftMsg = (TextView) itemView.findViewById(R.id.left_msg);
      
          rightLayout =(LinearLayout) itemView.findViewById(R.id.right_layout);
          rightMsg =  (TextView)itemView.findViewById(R.id.right_msg);
      }
      
      
    • 构造函数应该把msg集合内容传入MsgAdapter:

         private List<Msg> mMsgList;
      
          public MsgAdapter(List<Msg> msgList) {
              this.mMsgList = msgList;
          }
      
    • 继承RecyclerView.Adapter<MsgAdapter.ViewHolder>类必须重写的两个方法:

      • onCreateViewHolder用来创建ViewHolder实例。

           @NonNull
            @Override
            public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item,parent,false);
                ViewHolder viewHolder = new ViewHolder(view);
                return viewHolder;
            }
        
      • onBindViewHolder:用来对RecyclerView子项的数据进行赋值,会在每个子项被滚动到屏幕内的时候执行

            @Override
            public void onBindViewHolder(@NonNull 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());
                    return;
                }
                if(msg.getType() == Msg.TYPE_SEND){
                    // 表名是接受消息,显示左边,隐藏右边
                    holder.leftLayout.setVisibility(View.GONE);
                    holder.rightLayout.setVisibility(View.VISIBLE);
                    holder.rightMsg.setText(msg.getContent());
                }
            }
        
  • 最后在mainActivity上写相关逻辑:

    • 初始化msg集合,也就是历史聊天记录

          public void initMsgs(){
              Msg msg1 = new Msg("hello ssozh", Msg.TYPE_RECEIVED);
              msgList.add(msg1);
              Msg msg2 = new Msg("hello,Who is that", Msg.TYPE_SEND);
              msgList.add(msg2);
      
              Msg msg3 = new Msg("This is Tom. Nice to meet you ", Msg.TYPE_RECEIVED);
              msgList.add(msg3);
          }
      
    • 获取发送button、输入文本框inputText以及msgRecyclerView这几个view

    • 【重点】创建layoutManager 并绑定msgRecyclerView=>否则报错E/RecyclerView: No layout manager attached; skipping layout

    • 创建adpater并绑定

    • 编写send按钮的回调函数

          @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_recycler_view);
      
              // 重点:E/RecyclerView: No layout manager attached; skipping layout
              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_SEND);
                          msgList.add(msg);
                           // 当有新消息的时候刷新ListView定位到最后一行
                          adapter.notifyItemChanged(msgList.size()-1);
                          // 清空输入框
                          inputText.setText("");
      
                      }
                  }
              });
          }
      
posted @ 2020-11-20 18:52  SsoZh  阅读(109)  评论(0编辑  收藏  举报