zoukankan      html  css  js  c++  java
  • 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("");
        
                        }
                    }
                });
            }
        
  • 相关阅读:
    BZOJ 1176: [Balkan2007]Mokia
    BZOJ 4034: [HAOI2015]T2
    BZOJ 4031: [HEOI2015]小Z的房间
    BZOJ 4128: Matrix
    JSP学习-08-JavaBean
    JSP学习-标准标签库
    电影剧本写作基础
    JSP学习-09-自定义标签
    JSP学习-07Cookie 与Session
    JSP学习-06过滤器
  • 原文地址:https://www.cnblogs.com/SsoZhNO-1/p/14012552.html
Copyright © 2011-2022 走看看