Bootstrap

CH3-Android常见界面控件

目标

  1. 掌握简单控件的使用,能够独立搭建一个注册界面
  2. 掌握ListView控件与RecyclerView控件的使用,能独立搭建列表界面
  3. 掌握自定义控件的定义方式,能够自定义一个简单的控件

​ 几乎每一个Android应用都是通过界面控件与用户交互的,Android提供了非常丰富的界面控件,借助这些控件,我们可以很方便地进行用户界面开发。接下来,本章将针对Android常见的界面控件进行讲解。

一、简单控件的使用

1.1 TextView控件

控件是界面组成的主要元素,为了显示界面上的输入框、图片、文字等信息,Android系统提供了一些控件来显示这些信息,每个控件都有对应的属性来设置不同的效果。我们以控件使用的复杂程度将Android中的控件分别简单控件列表控件,简单控件包含以下几种,具体如下图所示。

image-20220225161243658

TextView控件用于显示文本信息,我们可以在XML布局文件中以添加属性的方式来控制TextView控件的样式, TextView控件的属性如下表所示。

属性名称功能描述
android:layout_width设置TextView控件的宽度
android:layout_height设置TextView控件的高度
android:id设置TextView控件的唯一标识
android:background设置TextView控件的背景
android:layout_margin设置当前控件与屏幕边界或周围控件、布局的距离 (外边距)
android:padding设置TextView控件与该控件中内容的距离 (内边距)
android:text设置文本内容
android:textColor设置文字显示的颜色
android:textSize设置文字大小,推荐单位为sp
android:gravity设置文本内容的位置
android:maxLength设置文本最大长度,超出此长度的文本不显示
android:lines设置文本的行数,超出此行数的文本不显示
android:maxLines设置文本的最大行数,超出此行数的文本不显示。
android:ellipsize设置当文本超出TextView规定的范围的显示方式
android:drawableTop在文本的顶部显示图像
android:lineSpacingExtra设置文本的行间距
android:textStyle设置文本样式,如bold(粗体),italic(斜体),normal(正常)

​ 接下来,我们通过一个案例讲解如何将TextView控件中的文本信息居中,并且将文本的字体设置为斜体进行显示,显示斜体文本的界面效果如下图所示。

image-20220225163524460

放置界面控件

在res/layout文件夹的activity_main.xml文件中,放置1个TextView控件,用于显示文本信息。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView控件显示的文本信息"
        android:textColor="#FFF79E38"
        android:textSize="25sp"
        android:gravity="center"
        android:textStyle="italic" />
</RelativeLayout>

1.2 EditText控件

EditText表示编辑框,它是TextView的子类,用户可在此控件中输入信息。除了支持TextView控件的属性外,EditText还支持一些其它的常用属性,这些常用属性如下表所示。

属性名称功能描述
android:hint控件中内容为空时显示的提示文本信息
android:textColorHint控件中内容为空时显示的提示文本信息的颜色
android:password输入文本框中的内容显示为“.”
android:phoneNumber设置输入文本框中的内容只能是数字
android:maxLines设置文本的最大行数
android:scrollHorizontally设置文本信息超出EditText的宽度情况下,是否出现横拉条
android:editable设置是否可编辑

​ 接下来,我们通过一个案例来讲解如何使用EditText控件编辑文本信息,本案例中显示编辑框的界面效果如下图所示。

image-20220225164302021

创建程序

​ 创建一个名为EditText的应用程序,指定包名为cn.itcast.edittext。

放置界面控件

​ 在activity_main.xml文件中,放置1个TextView控件,用于显示标题,1个EditText控件,供用户输入文本信息。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp"
    android:orientation="vertical">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="姓名:"
        android:textSize="28sp"
        android:textColor="#000000" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入姓名"
        android:maxLines="2"
        android:textColor="#000000"
        android:textSize="20sp"
        android:textStyle="italic" />
</LinearLayout>

1.3 Button控件

Button控件表示按钮,它继承TextView控件,既可以显示文本,又可以显示图片,同时也允许用户通过点击来执行操作,当Button控件被点击时,被按下与弹起的背景会有一个动态的切换效果,这个效果就是点击效果 。

Button控件设置点击事件的方式有三种,具体如下所示。

(1)在布局文件中指定onClick属性的方式设置点击事件

<Button
        android:id="@+id/btn_two"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="click"
        android:text="按钮2" />

(2)使用匿名内部类的方式设置点击事件

btn_one.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) { //按钮2的点击事件
        btn_one.setText("按钮1已被点击");
    }
});

(3)Activity实现OnClickListener接口的方式设置点击事件

public void click(View view) {
        btn_two.setText("按钮2已被点击");
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_three:     //按钮3的点击事件
                btn_three.setText("按钮3已被点击");
                break;
        }
    }

注意:实现Button控件的点击事件的三种方式中,前两种方式适合界面上Button控件较少的情况,如果界面上Button控件较多时,建议使用第三种方式实现控件的点击事件。

image-20220225171019185

1.4 ImageView控件

ImageView控件表示图片,它继承自View,可以加载各种图片资源。ImageView控件的常用属性如下表所示。

属性名称功能描述
android:layout_width设置ImageView控件的宽度
android:layout_height设置ImageView控件的高度
android:id设置ImageView控件的唯一标识
android:background设置ImageView控件的背景
android:layout_margin设置当前控件与屏幕边界或周围控件的距离
android:src设置ImageView控件需要显示的图片资源
android:scaleType将图片资源缩放或移动,以适应ImageView控件的宽高
android:tint将图片渲染成指定的颜色

image-20220225171134977

res\layout\activity_main.xml下放置界面控件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/bg" />
    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/icon" />
</RelativeLayout>

1.5 RadioButton控件

RadioButton表示单选按钮,它是Button的子类。每一个单选按钮都有“选中”“未选中”两种状态,这两种状态是通过android:checked属性指定的。当可选值为true时,表示选中状态,否则,表示未选中状态。

​ 在XML布局文件中,RadioGroup和RadioButton配合使用的语法格式如下

<RadioGroup
         android:属性名称 ="属性值"
         ......>
	<RadioButton
	       android:属性名称 ="属性值"
                         ...... />
	......
<RadioGroup/>

​ 接下来,我们通过一个案例来讲解如何使用RadioGroup和RadioButton实现单选按钮的功能。本案例的界面效果如下图所示。

image-20220225204631742

res\layout\activity_main.xml下放置界面控件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <RadioGroup
        android:id="@+id/rdg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <RadioButton
            android:id="@+id/rbtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="25dp"
            android:text="" />
        <RadioButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="25dp"
            android:text="" />
    </RadioGroup>
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30dp" />
</LinearLayout>

radiobutton\MainActivity.java下设置监听事件

package cn.itcast.radiobutton;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.RadioGroup;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private RadioGroup radioGroup;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        radioGroup = findViewById(R.id.rdg);
        textView = findViewById(R.id.tv);
        //利用setOnCheckedChangeListener()为RadioGroup设置监听事件
        radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                //判断被点击的是哪一个RadioButton控件
                if (checkedId == R.id.rbtn) {
                    textView.setText("您的性别是:男");
                } else {
                    textView.setText("您的性别是:女");
                }
            }
        });
    }
}

1.6 CheckBox控件

CheckBox表示复选框,它是Button的子类,用于实现多选功能。每一个复选框都有“选中”和“未选中”两种状态,这两种状态是通过android:checked属性指定的,当该属性的值为true时,表示选中状态,否则,表示未选中状态。

image-20220225214027034

res\layout\activity_main.xml下放置界面控件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="请选择兴趣爱好:"
        android:textColor="#FF8000"
        android:textSize="18sp" />
    <CheckBox
        android:id="@+id/like_shuttlecock"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="羽毛球"
        android:textSize="18sp" />
    <CheckBox
        android:id="@+id/like_basketball"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="篮球"
        android:textSize="18sp" />
    <CheckBox
        android:id="@+id/like_pingpong"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="乒乓球"
        android:textSize="18sp" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="您选择的兴趣爱好为:"
        android:textColor="#FF8000"
        android:textSize="22sp" />
    <TextView
        android:id="@+id/hobby"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp" />
</LinearLayout>

checkbox\MainActivity.java下实现控件的点击事件

package cn.itcast.checkbox;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements
        CompoundButton.OnCheckedChangeListener {
    private TextView hobby;
    private String hobbys;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化CheckBox控件
        CheckBox shuttlecock = findViewById(R.id.like_shuttlecock);
        CheckBox basketball =  findViewById(R.id.like_basketball);
        CheckBox pingpong = findViewById(R.id.like_pingpong);
        shuttlecock.setOnCheckedChangeListener(this);
        basketball.setOnCheckedChangeListener(this);
        pingpong.setOnCheckedChangeListener(this);
        hobby = findViewById(R.id.hobby);
        hobbys = new String();//存放选中的CheckBox的文本信息
    }
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        String motion = buttonView.getText().toString();
        if(isChecked){
            if(!hobbys.contains(motion)){
                hobbys = hobbys + motion;
                hobby.setText(hobbys);
            }
        }else {
            if (hobbys.contains(motion)) {
                hobbys = hobbys.replace(motion, "");
                hobby.setText(hobbys);
            }
        }
    }
}

1.7 Toast类

​ Toast是Android系统提供的轻量级信息提醒机制,用于向用户提示即时消息,它显示在应用程序界面的最上层,显示一段时间后自动消失不会打断当前操作,也不获得焦点。

使用Toast显示提示信息的示例代码如下:

Toast.makeText(Context,Text,Time).show();

关于makeText()方法中参数的相关介绍具体如下:

  • Context:表示应用程序环境的信息,即当前组件的上下文环境。
  • Text:表示提示的字符串信息
  • Time:表示显示信息的时长,其属性值包括Toast.LENGTH_SHORT和Toast.LENGTH_LONG,分别表示显示较短时间和较长时间。

MainActivity.java使用Toast提示用户“WIFI已断开”的信息,示例代码如下:

package cn.itcast.toast;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toast.makeText(MainActivity.this, "WIFI已断开", Toast.LENGTH_SHORT).show();
    }
}

Toast提示用户“WIFI已断开”信息的效果如右图所示。

image-20220225220024401

1.8 实战演练—实现注册界面效果

​ 接下来我们通过实现一个注册界面的功能来演示如何使用Android程序中常用的简单控件,注册界面的效果如下图所示。

image-20220225220730216

image-20220225220748324

res\layout\activity_main.xml下放置界面控件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/register_bg">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tv_title"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#01ceff"
            android:gravity="center"
            android:text="注册"
            android:textColor="@android:color/white"
            android:textSize="20sp" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="130dp"
            android:orientation="horizontal">
            <TextView
                style="@style/tvOne"
                android:drawableTop="@drawable/qq_icon"
                android:text="用QQ注册" />
            <View style="@style/vLine" />
            <TextView
                style="@style/tvOne"
                android:drawableTop="@drawable/weixin_icon"
                android:text="用微信注册" />
        </LinearLayout>
        <View style="@style/hLine" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="horizontal"
            android:padding="15dp">
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:src="@drawable/email_icon" />
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:text="使用电子邮箱注册"
                android:textColor="@android:color/white"
                android:textSize="15sp" />
        </LinearLayout>
        <View style="@style/hLine" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:padding="15dp">
            <TextView
                style="@style/tvTwo"
                android:text="名字" />
            <EditText
                android:id="@+id/et_name"
                style="@style/etOne" />
        </LinearLayout>
        <View style="@style/hLine" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:padding="15dp">
            <TextView
                style="@style/tvTwo"
                android:text="邮箱" />
            <EditText
                android:id="@+id/et_email"
                style="@style/etOne" />
        </LinearLayout>
        <View style="@style/hLine" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:padding="15dp">
            <TextView
                style="@style/tvTwo"
                android:text="密码" />
            <EditText
                android:id="@+id/et_pwd"
                style="@style/etOne"
                android:inputType="textPassword" />
        </LinearLayout>
        <View style="@style/hLine" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:padding="15dp">
            <TextView
                style="@style/tvTwo"
                android:text="性别" />
            <RadioGroup
                android:id="@+id/rg_sex"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="50dp"
                android:orientation="horizontal">
                <RadioButton
                    android:id="@+id/rb_boy"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text=""
                    android:textColor="@android:color/white"
                    android:textSize="15sp" />
                <RadioButton
                    android:id="@+id/rb_girl"
                    style="@style/tvTwo"
                    android:text=""/>
            </RadioGroup>
        </LinearLayout>
        <View style="@style/hLine" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:padding="15dp">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="请选择兴趣爱好:"
                android:textColor="@android:color/white"
                android:textSize="15sp" />
            <CheckBox
                android:id="@+id/cb_sing"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="唱歌"
                android:textColor="@android:color/white"
                android:textSize="15sp" />
            <CheckBox
                android:id="@+id/cb_dance"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="跳舞"
                android:textColor="@android:color/white"
                android:textSize="15sp" />
            <CheckBox
                android:id="@+id/cb_read"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="读书"
                android:textColor="@android:color/white"
                android:textSize="15sp" />
        </LinearLayout>
        <View style="@style/hLine" />
    </LinearLayout>
    <View
        android:id="@+id/v_line"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_above="@id/btn_submit"
        android:background="@android:color/darker_gray" />
    <Button
        android:id="@+id/btn_submit"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:gravity="center"
        android:text="提交"
        android:textColor="@android:color/white"
        android:background="@null"
        android:textSize="18sp" />
</RelativeLayout>

res\values\styles.xml下创建样式

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
    <style name="hLine">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">1dp</item>
        <item name="android:background">@android:color/white</item>
    </style>
    <style name="vLine">
        <item name="android:layout_width">1dp</item>
        <item name="android:layout_height">match_parent</item>
        <item name="android:background">@android:color/white</item>
    </style>
    <style name="tvOne">
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_height">match_parent</item>
        <item name="android:layout_weight">1</item>
        <item name="android:drawablePadding">8dp</item>
        <item name="android:gravity">center_horizontal</item>
        <item name="android:paddingTop">40dp</item>
        <item name="android:textColor">@android:color/white</item>
        <item name="android:textSize">15dp</item>
    </style>
    <style name="tvTwo">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_marginLeft">20dp</item>
        <item name="android:textColor">@android:color/white</item>
        <item name="android:textSize">15dp</item>
    </style>
    <style name="etOne">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_marginLeft">30dp</item>
        <item name="android:background">@null</item>
        <item name="android:textColor">@android:color/white</item>
    </style>

</resources>

src\main\AndroidManifest.xml下去掉默认标题栏

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.itcast.register">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        
        android:theme="@style/Theme.AppCompat.Light.NoActionBar">
        
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

checkbox\MainActivity.java下实现注册功能

package cn.itcast.register;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
    private EditText et_name, et_email, et_pwd;
    private Button btn_submit;
    private String name, email, pwd, sex, hobbys;
    private RadioGroup rg_sex;
    private CheckBox cb_sing,cb_dance,cb_read;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }
    private void init() {
        //获取界面控件
        et_name = findViewById(R.id.et_name);
        et_email = findViewById(R.id.et_email);
        et_pwd = findViewById(R.id.et_pwd);
        rg_sex = findViewById(R.id.rg_sex);
        cb_sing = findViewById(R.id.cb_sing);
        cb_dance = findViewById(R.id.cb_dance);
        cb_read = findViewById(R.id.cb_read);
        btn_submit = findViewById(R.id.btn_submit);
        btn_submit.setOnClickListener(this);//设置提交按钮的点击事件的监听器
        //设置复选框控件的点击事件的监听器
        cb_sing.setOnCheckedChangeListener(this);
        cb_dance.setOnCheckedChangeListener(this);
        cb_read.setOnCheckedChangeListener(this);
        hobbys=new String();
        //设置单选按钮的点击事件
        rg_sex.setOnCheckedChangeListener(new RadioGroup.
                OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                switch (checkedId){ //判断被点击的RadioButton
                    case R.id.rb_boy:
                        sex = "男";
                        break;
                    case R.id.rb_girl:
                        sex = "女";
                        break;
                }
            }
        });
    }
    /**
     * 获取界面输入的信息
     */
    private void getData() {
        name = et_name.getText().toString().trim();
        email = et_email.getText().toString().trim();
        pwd = et_pwd.getText().toString().trim();
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_submit: //提交按钮的点击事件
                getData();
                if (TextUtils.isEmpty(name)) {
                    Toast.makeText(MainActivity.this, "请输入名字",
                            Toast.LENGTH_SHORT).show();
                } else if (TextUtils.isEmpty(email)) {
                    Toast.makeText(MainActivity.this, "请输入邮箱",
                            Toast.LENGTH_SHORT).show();
                } else if (TextUtils.isEmpty(pwd)) {
                    Toast.makeText(MainActivity.this, "请输入密码",
                            Toast.LENGTH_SHORT).show();
                } else if (TextUtils.isEmpty(sex)) {
                    Toast.makeText(MainActivity.this, "请选择性别",
                            Toast.LENGTH_SHORT).show();
                } else if (TextUtils.isEmpty(hobbys)) {
                    Toast.makeText(MainActivity.this, "请选择兴趣爱好",
                            Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, "注册成功",
                            Toast.LENGTH_SHORT).show();
                    Log.i("MainActivity","注册的用户信息:"+"名字:"+name+", 邮箱:"
                            +email+", 性别:"+sex+", 兴趣爱好:"+hobbys);
                }
                break;
        }
    }
    /**
     * 复选框的点击事件
     */
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        String motion = buttonView.getText().toString();//获取复选框中的内容
        if (isChecked) {
            if (!hobbys.contains(motion)) { //判断之前选择的内容是否与此次选择的不一样
                hobbys = hobbys + motion;
            }
        } else {
            if (hobbys.contains(motion)) {
                hobbys = hobbys.replace(motion, "");
            }
        }
    }
}

二、列表控件的使用

目标

  • 掌握ListView控件的使用,能够独立搭建购物商城界面
  • 掌握RecyclerView控件的使用,能够独立搭建仿今日头条推荐列表界面

2.1 ListView控件

​ 在Android开发中,ListView是一个比较常用的控件,它以列表的形式展示数据内容,并且能够根据列表的高度自适应屏幕显示。ListView的样式是由属性决定的,它的常用属性如下表所示。

属性名称功能描述
android:listSelector当条目被点击后,改变条目的背景颜色
android:divider设置分割线的颜色
android:dividerHeight设置分割线的高度
android:scrollbars是否显示滚动条
android:fadingEdge去掉上边和下边的黑色阴影

​ 在XML文件的RelativeLayout布局中添加ListView控件的示例代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
        ......>
       <ListView
            android:id="@+id/lv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:listSelector="#fefefefe"
            android:scrollbars="none">
       </ListView>
</RelativeLayout>

2.2 常用数据适配器(Adapter)

数据适配器是数据与视图之间的桥梁,它类似于一个转换器,将复杂的数据转换成用户可以接受的方式进行呈现。

常用的数据适配器:

  • BaseAdapter:基本的适配器

  • SimpleAdapter:继承自BaseAdapter

  • ArrayAdapter:也是BaseAdapter的子类

BaseAdapter

​ BaseAdapter顾名思义是基本的适配器。它实际上是一个抽象类,通常在自定义适配器时会继承BaseAdapter,该类拥有四个抽象方法,根据这几个抽象方法对ListView控件进行数据适配。BaseAdapter中的4个抽象方法如下表所示。

方法名称功能描述
public int getCount()获取列表条目的总数
public Object getItem(int position)根据position(位置)获取某个条目的对象
public long getItemId(int position)根据position(位置)获取某个条目的id
public View getView(int position, View convertView, ViewGroup parent)获取相应position对应的条目视图,position是当前条目的位置,convertView用于复用旧视图,parent用于加载XML布局。

SimpleAdapter

​ SimpleAdapter继承BaseAdapter,实现了BaseAdapter的四个抽象方法并进行封装。SimpleAdapter的构造方法的具体信息如下:

  public SimpleAdapter(
      Context context, List<? extends Map<String, ?>> data, 
      int resource, String[] from, int[] to
  )

在SimpleAdapter()构造方法中的5个参数的含义如下:

  • context:表示上下文对象
  • data:数据集合,data中的每一项对应ListView控件中的条目的数据
  • resource:条目布局的资源id
  • from:Map集合中的key值
  • to:条目布局中对应的控件

ArrayAdapter

​ ArrayAdapter也是BaseAdapter的子类,用法与SimpleAdapter类似,开发者只需要在构造方法里面传入相应参数即可。ArrayAdapter通常用于适配TextView控件,ArrayAdapter有多个构造方法,构造方法的具体信息如下所示。

   public ArrayAdapter(Context context,int resource)public ArrayAdapter(Context context,int resource, int textViewResourceId)public ArrayAdapter(Context context,int resource,T[] objects)public ArrayAdapter(Context context,int resource,int textViewResourceId,T[] objects);
   public ArrayAdapter(Context context,int resource,List<T> objects)public ArrayAdapter(Context context,int resource,int textViewResourceId, List<T> objects)

在ArrayAdapter()构造方法中的4个参数的含义如下:

  • context:表示上下文对象
  • resource:条目布局的资源id
  • textViewResourceId: 条目布局中对应的TextView控件的id
  • List objects: 需要适配的List类型的数据

2.3 实战演练—购物商城

​ 接下来我们通过一个购物商城的案例来演示如何通过ListView控件与数据适配器显示一个商品信息的列表。本案例的界面效果如下图所示。

image-20220226123306323

res\layout\activity_main.xml下放置界面控件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:text="购物商城"
        android:textSize="18sp"
        android:textColor="#FFFFFF"
        android:background="#FF8F03"
        android:gravity="center"/>
    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

res\layout\list_item.xml创建列表条目

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">
    <ImageView
        android:id="@+id/iv"
        android:layout_width="120dp"
        android:layout_height="90dp"
        android:layout_centerVertical="true"/>
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/iv"
        android:layout_centerVertical="true">
        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="桌子"
            android:textSize="20sp"
            android:textColor="#000000" />
        <TextView
            android:id="@+id/tv_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="价格:"
            android:textSize="20sp"
            android:layout_marginTop="10dp"
            android:layout_below="@+id/title"
            android:textColor="#FF8F03" />
        <TextView
            android:id="@+id/price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="1000"
            android:textSize="20sp"
            android:layout_below="@+id/title"
            android:layout_toRightOf="@+id/tv_price"
            android:textColor="#FF8F03"
            android:layout_marginTop="10dp"/>
    </RelativeLayout>
</RelativeLayout>

ListView\MainActivity.java下实现界面显示功能

package cn.itcast.listview;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends Activity {
    private ListView mListView;
    //商品名称与价格数据集合
    private String[] titles = {"桌子", "苹果", "蛋糕", "线衣", "猕猴桃", "围巾"};
    private String[] prices = {"1800元", "10元/kg", "300元", "350元", "10元/kg",
            "280元"};
    //图片数据集合
    private int[] icons = {R.drawable.table, R.drawable.apple, R.drawable.cake,
            R.drawable.wireclothes, R.drawable.kiwifruit, R.drawable.scarf};

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = findViewById(R.id.lv); //初始化ListView控件
        MyBaseAdapter mAdapter = new MyBaseAdapter(); //创建一个Adapter的实例
        mListView.setAdapter(mAdapter);                  //设置Adapter
    }

    class MyBaseAdapter extends BaseAdapter {
        @Override
        public int getCount() {   //获取条目的总数
            return titles.length; //返回条目的总数
        }

        @Override
        public Object getItem(int position) {
            return titles[position]; //返回条目的数据对象
        }

        @Override
        public long getItemId(int position) {
            return position; //返回条目的Id
        }
        //获取条目的视图
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if (convertView == null) {
                //将list_item.xml文件找出来并转换成View对象
                convertView = View.inflate(MainActivity.this, R.layout.list_item, null);
                //找到list_item.xml中创建的TextView
                holder = new ViewHolder();
                holder.title =  convertView.findViewById(R.id.title);
                holder.price = convertView.findViewById(R.id.price);
                holder.iv = convertView.findViewById(R.id.iv);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            holder.title.setText(titles[position]);
            holder.price.setText(prices[position]);
            holder.iv.setBackgroundResource(icons[position]);
            return convertView;
        }
        class ViewHolder {
            TextView title, price;
            ImageView iv;
        }
    }
}

优化ListView加载数据逻辑

​ 运行前面的购物商城程序后,当ListView控件上加载的条目过多,并快速滑动该列表控件时,界面会出现卡顿的现象,出现这个现象的原因如下:

(1)当滑动屏幕时,不断地创建条目对象

(2)不断执行findViewById()方法初始化界面控件

​ 由于上述两点原因,我们需要对ListView控件进行优化,优化目的是使ListView控件在快速滑动时不再重复创建条目对象,减少内存的消耗和屏幕渲染的处理。优化方式有以下两种:

(1)使用ViewHolder类

(2)复用convertView

​ 为了防止数据量过大造成内存溢出,在使用ListView时通常会进行优化,优化方式中的其中一种是复用convertView

image-20220226123610127

image-20220226123644018

2.4 RecyclerView控件

​ RecyclerView与ListView控件相似,同样是以列表的形式展示数据,并且数据都是通过适配器加载的。RecyclerView的功能更加强大,接下来我们从以下几个方面来分析:

image-20220226123852501

2.5 实战演练—动物列表

​ 接下来,我们通过一个案例来讲解如何通过RecyclerView控件显示一个动物列表界面。本案例的界面效果如下图所示。

image-20220227160801260

image-20220227160812529

res\layout\activity_main.xml下放置界面控件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/id_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>
</RelativeLayout>

res\layout\recycler_item.xml创建列表条目

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:gravity="center"
    android:orientation="horizontal">
    <ImageView
        android:id="@+id/iv"
        android:layout_width="120dp"
        android:layout_height="90dp"
        android:src="@drawable/siberianhusky"/>
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="5dp">
        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:textColor="#FF8F03"
            android:text="哈士奇"/>
        <TextView
            android:id="@+id/introduce"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            android:layout_marginTop="10dp"
            android:layout_below="@+id/name"
            android:textColor="#FF716C6D"
            android:maxLines="2"
            android:ellipsize="end"
            android:text="西伯利亚雪橇犬,常见别名哈士奇,昵称为二哈。"/>
    </RelativeLayout>
</LinearLayout>

Recyclerview\MainActivity.java下实现界面显示功能

package cn.itcast.recyclerview;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
    private RecyclerView mRecyclerView;
    private HomeAdapter mAdapter;
    private String[] names = {"小猫", "哈士奇", "小黄鸭", "小鹿", "老虎"};
    private int[] icons = {R.drawable.cat, R.drawable.siberianhusky,
            R.drawable.yellowduck, R.drawable.fawn, R.drawable.tiger};
    private String[] introduces = {
            "猫,属于猫科动物,分家猫、野猫,是全世界家庭中较为广泛的宠物。",
            "西伯利亚雪橇犬,常见别名哈士奇,昵称为二哈。",
            "鸭的体型相对较小,颈短,一些属的嘴要大些。腿位于身体后方,因而步态蹒跚。",
            "鹿科是哺乳纲偶蹄目下的一科动物。体型大小不等,为有角的反刍类。",
            "虎,大型猫科动物;毛色浅黄或棕黄色,满有黑色横纹;头圆、耳短,耳背面黑色,中央有一白斑甚显著;四肢健壮有力;尾粗长,具黑色环纹,尾端黑色。"
    };
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRecyclerView = findViewById(R.id.id_recyclerview);
        //设置RecyclerView控件的显示方式为线性垂直
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mAdapter = new HomeAdapter();
        //将HomeAdapter对象设置到RecyclerView控件上
        mRecyclerView.setAdapter(mAdapter);
    }
    
    
    class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder> {
        @Override
        //加载item界面的布局文件,并将MyViewHolder类的对象返回
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            MyViewHolder holder = new MyViewHolder(LayoutInflater.from(MainActivity.this).inflate(
                    R.layout.recycler_item, parent, false));
            return holder;
        }

        @Override
        //将获取的数据设置到对应的控件上
        public void onBindViewHolder(MyViewHolder holder, int position) {
            holder.name.setText(names[position]);
            holder.iv.setImageResource(icons[position]);
            holder.introduce.setText(introduces[position]);
        }
        
        @Override
        //获取列表条目的总数
        public int getItemCount() {
            return names.length;
        }
        class MyViewHolder extends RecyclerView.ViewHolder {
            TextView name;
            ImageView iv;
            TextView introduce;
            //获取item界面上的控件
            public MyViewHolder(View view) {
                super(view);
                name = view.findViewById(R.id.name);
                iv = view.findViewById(R.id.iv);
                introduce = view.findViewById(R.id.introduce);
            }
        }
    }
}

2.5 实战演练—仿今日头条

​ 接下来以仿今日头条推荐列表为例,来演示如何使用RecyclerView控件,仿今日头条推荐列表界面的效果如下图所示。

image-20220227161004968

image-20220227160940734

res\values\styles.xml创建样式

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
    <style name="tvStyle" >
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">match_parent</item>
        <item name="android:padding">10dp</item>
        <item name="android:gravity">center</item>
        <item name="android:textSize">15sp</item>
    </style>
    <style name="tvInfo" >
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_marginLeft">8dp</item>
        <item name="android:layout_gravity">center_vertical</item>
        <item name="android:textSize">14sp</item>
        <item name="android:textColor">@color/gray_color</item>
    </style>
    <style name="ivImg" >
        <item name="android:layout_width">0dp</item>
        <item name="android:layout_height">90dp</item>
        <item name="android:layout_weight">1</item>
        <!--ll_info为布局文件list_item_one.xml中的id -->
        <item name="android:layout_toRightOf">@id/ll_info</item>
    </style>
</resources>

res\values\colors.xml添加颜色值

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>
    <color name="light_gray_color">#eeeeee</color>
    <color name="gray_color">#828282</color>
</resources>

res\layout\title_bar.xml创建标题栏

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="#d33d3c"
    android:orientation="horizontal"
    android:paddingLeft="10dp"
    android:paddingRight="10dp">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="仿今日头条"
        android:textColor="@android:color/white"
        android:textSize="22sp" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="35dp"
        android:layout_gravity="center_vertical"
        android:layout_marginStart="15dp"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="15dp"
        android:background="@drawable/search_bg"
        android:gravity="center_vertical"
        android:textColor="@android:color/black"
        android:hint="搜你想搜的"
        android:textColorHint="@color/gray_color"
        android:textSize="14sp"
        android:paddingLeft="30dp" />
</LinearLayout>

res\layout\activity_main.xml搭建推荐列表界面

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/light_gray_color"
    android:orientation="vertical">
    <include layout="@layout/title_bar" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="@android:color/white"
        android:orientation="horizontal">
        <TextView
            style="@style/tvStyle"
            android:text="推荐"
            android:textColor="@android:color/holo_red_dark" />
        <TextView
            style="@style/tvStyle"
            android:text="抗疫"
            android:textColor="@color/gray_color" />
        <TextView
            style="@style/tvStyle"
            android:text="小视频"
            android:textColor="@color/gray_color" />
        <TextView
            style="@style/tvStyle"
            android:text="北京"
            android:textColor="@color/gray_color" />
        <TextView
            style="@style/tvStyle"
            android:text="视频"
            android:textColor="@color/gray_color" />
        <TextView
            style="@style/tvStyle"
            android:text="热点"
            android:textColor="@color/gray_color" />
        <TextView
            style="@style/tvStyle"
            android:text="娱乐"
            android:textColor="@color/gray_color" />
    </LinearLayout>
    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#eeeeee" />
    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

res\layout\list_item_one/two.xml搭建列表条目界面

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="90dp"
    android:layout_marginBottom="8dp"
    android:background="@android:color/white"
    android:padding="8dp">
    <LinearLayout
        android:id="@+id/ll_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tv_title"
            android:layout_width="280dp"
            android:layout_height="wrap_content"
            android:maxLines="2"
            android:textColor="#3c3c3c"
            android:textSize="16sp" />
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <ImageView
                android:id="@+id/iv_top"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:layout_alignParentBottom="true"
                android:src="@drawable/top" />
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_toRightOf="@id/iv_top"
                android:orientation="horizontal">
                <TextView
                    android:id="@+id/tv_name"
                    style="@style/tvInfo" />
                <TextView
                    android:id="@+id/tv_comment"
                    style="@style/tvInfo" />
                <TextView
                    android:id="@+id/tv_time"
                    style="@style/tvInfo" />
            </LinearLayout>
        </RelativeLayout>
    </LinearLayout>
    <ImageView
        android:id="@+id/iv_img"
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:layout_toRightOf="@id/ll_info"
        android:padding="3dp" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="8dp"
    android:background="@android:color/white">
    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:maxLines="2"
        android:padding="8dp"
        android:textColor="#3c3c3c"
        android:textSize="16sp" />
    <LinearLayout
        android:id="@+id/ll_img"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv_title"
        android:orientation="horizontal">
        <ImageView
            android:id="@+id/iv_img1"
            style="@style/ivImg"/>
        <ImageView
            android:id="@+id/iv_img2"
            style="@style/ivImg"/>
        <ImageView
            android:id="@+id/iv_img3"
            style="@style/ivImg"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/ll_img"
        android:orientation="vertical"
        android:padding="8dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <TextView
                android:id="@+id/tv_name"
                style="@style/tvInfo" />
            <TextView
                android:id="@+id/tv_comment"
                style="@style/tvInfo" />
            <TextView
                android:id="@+id/tv_time"
                style="@style/tvInfo" />
        </LinearLayout>
    </LinearLayout>
</RelativeLayout>

headline\MainActivity.java 显示推荐列表的数据

package cn.itcast.headline;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
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 String[] titles = {"各地餐企齐行动,杜绝餐饮浪费",
            "花菜有人焯水,有人直接炒,都错了,看饭店大厨如何做",
            "睡觉时,双脚突然蹬一下,有踩空感,像从高楼坠落,是咋回事?",
            "实拍外卖小哥砸开小吃店的卷帘门救火,灭火后淡定继续送外卖",
            "还没成熟就被迫提前采摘,8毛一斤却没人要,果农无奈:不摘不行",
            "大会、大展、大赛一起来,北京电竞“好嗨哟”"};
    private String[] names = {"央视新闻客户端", "味美食记", "民富康健康", "生活小记",
            "禾木报告", "燕鸣"};
    private String[] comments = {"9884评", "18评", "78评", "678评", "189评",
            "304评"};
    private String[] times = {"6小时前", "刚刚", "1小时前", "2小时前", "3小时前",
            "4个小时前"};
    private int[] icons1 = {R.drawable.food, R.drawable.takeout,
            R.drawable.e_sports};
    private int[] icons2 = {R.drawable.sleep1, R.drawable.sleep2, R.drawable.sleep3,
            R.drawable.fruit1,R.drawable.fruit2, R.drawable.fruit3};
    //新闻类型,1表示置顶新闻或只有1张图片的新闻,2表示包含3张图片的新闻
    private int[] types = {1, 1, 2, 1, 2, 1};
    private RecyclerView mRecyclerView;
    private NewsAdapter mAdapter;
    private List<NewsBean> NewsList;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setData();
        mRecyclerView = findViewById(R.id.rv_list);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mAdapter = new NewsAdapter(MainActivity.this, NewsList);
        mRecyclerView.setAdapter(mAdapter);
    }
//    将定义的数组中的数据添加到新闻数据集合NewList中
    private void setData() {
        NewsList = new ArrayList<NewsBean>();
        NewsBean bean;
        for (int i = 0; i < titles.length; i++) {
            bean = new NewsBean();
            bean.setId(i + 1);
            bean.setTitle(titles[i]);
            bean.setName(names[i]);
            bean.setComment(comments[i]);
            bean.setTime(times[i]);
            bean.setType(types[i]);
            switch (i) {
                case 0: //置顶新闻的图片设置
                    List<Integer> imgList0 = new ArrayList<>();
                    bean.setImgList(imgList0);
                    break;
                case 1://设置第2个条目的图片数据
                    List<Integer> imgList1 = new ArrayList<>();
                    imgList1.add(icons1[i - 1]);
                    bean.setImgList(imgList1);
                    break;
                case 2://设置第3个条目的图片数据
                    List<Integer> imgList2 = new ArrayList<>();
                    imgList2.add(icons2[i - 2]);
                    imgList2.add(icons2[i - 1]);
                    imgList2.add(icons2[i]);
                    bean.setImgList(imgList2);
                    break;
                case 3://设置第4个条目的图片数据
                    List<Integer> imgList3 = new ArrayList<>();
                    imgList3.add(icons1[i - 2]);
                    bean.setImgList(imgList3);
                    break;
                case 4://设置第5个条目的图片数据
                    List<Integer> imgList4 = new ArrayList<>();
                    imgList4.add(icons2[i - 1]);
                    imgList4.add(icons2[i]);
                    imgList4.add(icons2[i + 1]);
                    bean.setImgList(imgList4);
                    break;
                case 5://设置第6个条目的图片数据
                    List<Integer> imgList5 = new ArrayList<>();
                    imgList5.add(icons1[i - 3]);
                    bean.setImgList(imgList5);
                    break;
            }
            NewsList.add(bean);
        }
    }
}

headline\NewsAdapter.java 创建适配器NewsAdapter

package cn.itcast.headline;

import android.content.Context;
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.ImageView;
import android.widget.TextView;

import java.util.List;

public class NewsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private Context mContext;
    private List<NewsBean> NewsList;
    public NewsAdapter(Context context,List<NewsBean> NewsList) {
        this.mContext = context;
        this.NewsList=NewsList;
    }
    //加载条目视图
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
                                                      int viewType) {
        View itemView=null;
        RecyclerView.ViewHolder holder=null;
        if (viewType == 1){
            itemView = LayoutInflater.from(mContext).inflate(R.layout.
                    list_item_one, parent, false);
            holder= new MyViewHolder1(itemView);
        }else if (viewType == 2){
            itemView = LayoutInflater.from(mContext).inflate(R.layout.
                    list_item_two, parent, false);
            holder= new MyViewHolder2(itemView);
        }
        return holder;
    }
//    获取条目类型
    @Override
    public int getItemViewType(int position) {
        return NewsList.get(position).getType();
    }
//    绑定界面数据
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder,
                                 int position) {
        NewsBean bean=NewsList.get(position);
        if (holder instanceof MyViewHolder1){
            if (position==0) {
                ((MyViewHolder1) holder).iv_top.setVisibility(View.VISIBLE);
                ((MyViewHolder1) holder).iv_img.setVisibility(View.GONE);
            } else {
                ((MyViewHolder1) holder).iv_top.setVisibility(View.GONE);
                ((MyViewHolder1) holder).iv_img.setVisibility(View.VISIBLE);
            }
            ((MyViewHolder1) holder).title.setText(bean.getTitle());
            ((MyViewHolder1) holder).name.setText(bean.getName());
            ((MyViewHolder1) holder).comment.setText(bean.getComment());
            ((MyViewHolder1) holder).time.setText(bean.getTime());
            if (bean.getImgList().size()==0)return;
            ((MyViewHolder1) holder).iv_img.setImageResource(bean.getImgList()
                    .get(0));
        }else if (holder instanceof MyViewHolder2){
            ((MyViewHolder2) holder).title.setText(bean.getTitle());
            ((MyViewHolder2) holder).name.setText(bean.getName());
            ((MyViewHolder2) holder).comment.setText(bean.getComment());
            ((MyViewHolder2) holder).time.setText(bean.getTime());
            ((MyViewHolder2) holder).iv_img1.setImageResource(bean.getImgList()
                    .get(0));
            ((MyViewHolder2) holder).iv_img2.setImageResource(bean.getImgList()
                    .get(1));
            ((MyViewHolder2) holder).iv_img3.setImageResource(bean.getImgList()
                    .get(2));
        }
    }
//    获取条目总数
    @Override
    public int getItemCount() {
        return NewsList.size();
    }
    class MyViewHolder1 extends RecyclerView.ViewHolder {
        ImageView iv_top,iv_img;
        TextView title,name,comment,time;
        public MyViewHolder1(View view) {
            super(view);
            iv_top = view.findViewById(R.id.iv_top);
            iv_img = view.findViewById(R.id.iv_img);
            title = view.findViewById(R.id.tv_title);
            name = view.findViewById(R.id.tv_name);
            comment = view.findViewById(R.id.tv_comment);
            time = view.findViewById(R.id.tv_time);
        }
    }
    class MyViewHolder2 extends RecyclerView.ViewHolder {
        ImageView iv_img1,iv_img2,iv_img3;
        TextView title,name,comment,time;
        public MyViewHolder2(View view) {
            super(view);
            iv_img1 = view.findViewById(R.id.iv_img1);
            iv_img2 = view.findViewById(R.id.iv_img2);
            iv_img3 = view.findViewById(R.id.iv_img3);
            title = view.findViewById(R.id.tv_title);
            name = view.findViewById(R.id.tv_name);
            comment = view.findViewById(R.id.tv_comment);
            time = view.findViewById(R.id.tv_time);
        }
    }
}

headline\NewsBean.java 封装新闻信息实体类,在该类中创建新闻信息属性对应的字段

package cn.itcast.headline;
import java.util.List;
public class NewsBean {
    private int id;                   //新闻id
    private String title;            //新闻标题
    private List<Integer> imgList; //新闻图片
    private String name;             //用户名
    private String comment;         //用户评论
    private String time;             //新闻发布时间
    private int type;                 //新闻类型
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getComment() {
        return comment;
    }
    public void setComment(String comment) {
        this.comment = comment;
    }
    public String getTime() {
        return time;
    }
    public void setTime(String time) {
        this.time = time;
    }
    public List<Integer> getImgList() {
        return imgList;
    }
    public void setImgList(List<Integer> imgList) {
        this.imgList = imgList;
    }
    public int getType() {
        return type;
    }
    public void setType(int type) {
        this.type = type;
    }
}

三、自定义控件

目标

  • 掌握自定义View中的3个方法,能够在界面中绘制一个圆形图案

​ 通常开发Android应用的界面时,使用的控件都不直接使用View,而是使用View的子类。虽然Android系统中提供了很多继承View类的控件,但是在实际开发中,还会出现不满足需求的情况。此时我们可以通过自定义View的方式进行实现。

​ 当使用系统控件不满足需求时,需要自定义控件。最简单的自定义View就是创建一个类继承自View类或者其子类,并重写该类的构造方法。示例代码如下:

   public class Customview extends View{
       //在Java代码中创建对象时,使用该构造函数
       public Customview(Context context) {
            super(context);
       }
       //在XML布局中引入自定义控件时,使用该构造函数
       public Customview(Context context, AttributeSet attrs) {
            super(context, attrs);
       }
}

​ 由于系统自带的控件不能满足需求中的某种样式或功能,所以我们需要在自定义View中通过重写指定的方法来添加额外的样式和功能。

image-20220227201946759

3.1 实战演练—显示一个圆形

​ 接下来,我们通过一个案例讲解如何使用自定义View在界面中显示一个圆形,显示圆形界面的效果如下图所示。

image-20220227202020233

customview\CircleView.java自定义CircleView类

package cn.itcast.customview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class CircleView extends View {
    public CircleView(Context context) {
        super(context);
    }
    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int r = getMeasuredWidth() / 2;
        int centerX = getLeft() + r;
        int centerY = getTop()+ r;
        Paint paint = new2 Paint();
        paint.setColor(Color.RED);
        //开始绘制
        canvas.drawCircle(centerX, centerY, r, paint);
    }
}

res\layout\activity_main.xml引用自定义控件CustomView

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginLeft="16dp"
    android:layout_marginTop="16dp">
    <cn.itcast.customview.CircleView
        android:layout_width="100dp"
        android:layout_height="100dp"/>
</RelativeLayout>

AlertDialog对话框

四、编程题

1.整数加法

​ 开发一个整数加法的程序,实现将计算结果显示到界面上的功能

image-20220228122257864

src\main\res\layout\activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <EditText
        android:id="@+id/addend"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:phoneNumber="true"
        android:hint="加数"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="+"/>
    <EditText
        android:id="@+id/augend"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:phoneNumber="true"
        android:hint="被加数"/>
    <Button
        android:id="@+id/equal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="="/>
    <TextView
        android:id="@+id/result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        />
</LinearLayout>

answer_addition\MainActivity.java

package cn.itcast.answer_addition;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
 private EditText ed_addend;
 private EditText ed_augend;
 private TextView tv_result;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ed_addend = findViewById(R.id.addend);
        ed_augend = findViewById(R.id.augend);
        tv_result = findViewById(R.id.result);
        findViewById(R.id.equal).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String addend = ed_addend.getText().toString().trim();
                String augend = ed_augend.getText().toString().trim();
                
                if(addend ==null && addend.isEmpty()){
                    Toast.makeText(MainActivity.this,"请输入加数",Toast.LENGTH_SHORT).show();
                    return;
                }else if (augend ==null && augend.isEmpty()){
                    Toast.makeText(MainActivity.this,"请输入被加数",Toast.LENGTH_SHORT).show();
                    return;
                }
                
                try {
                    int result = Integer.parseInt(addend)+Integer.parseInt(augend);
                    tv_result.setText(""+result);
                }catch (Exception e){
                    Toast.makeText(MainActivity.this,"请输入整数",Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}

2.自定义的对话框

​ 开发一个自定义的对话框,其界面中显示标题、提示内容、确定和取消按钮。当点击返回键时,用于提示用户是否退出程序。

image-20220228123012615

src\main\res\layout\activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

src\main\java\answer_customdialog\MainActivity.java

package cn.itcast.answer_customdialog;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        final CommonDialog dialog = new CommonDialog(MainActivity.this);
        dialog.setTitle("提示");
        dialog.setMessage("您确定要退出本应用吗?");
        dialog.setNegtive("取消");
        dialog.setPositive("确定");
        dialog.setOnClickBottomListener(new CommonDialog.
                OnClickBottomListener() {
            @Override
            public void onPositiveClick() { //确定按钮的点击事件
                dialog.dismiss();
                MainActivity.this.finish();
            }
            @Override
            public void onNegtiveClick() { //取消按钮的点击事件
                dialog.dismiss();
            }
        });
        dialog.show();
        return true;
    }
}

src\main\res\layout\custom_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="16dp"
        android:orientation="vertical">
        <TextView android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:visibility="visible"
            android:textColor="#333333"
            android:textSize="18sp"
            android:layout_marginBottom="16dp"/>
        <TextView android:id="@+id/message"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:textSize="14sp"
            android:textColor="#999999" />
        <View android:layout_width="match_parent"
            android:layout_height="2px"
            android:layout_marginTop="16dp"
            android:background="#E8E8E8" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <Button android:id="@+id/negtive"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:paddingTop="16dp"
                android:paddingBottom="16dp"
                android:layout_weight="1"
                android:background="@null"
                android:gravity="center"
                android:singleLine="true"
                android:textColor="#999999"
                android:textSize="16sp" />
            <View android:id="@+id/column_line"
                android:layout_width="2px"
                android:layout_height="match_parent"
                android:background="#E8E8E8" />
            <Button
                android:id="@+id/positive"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginRight="10dp"
                android:paddingTop="16dp"
                android:paddingBottom="16dp"
                android:background="@null"
                android:gravity="center"
                android:textColor="#38ADFF"
                android:textSize="16sp" />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

src\main\java\answer_customdialog\CommonDialog.java

package cn.itcast.answer_customdialog;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class CommonDialog extends AlertDialog {
    private TextView titleTv ;               //显示的标题
    private TextView messageTv ;             //显示的消息
    private Button negtiveBn ,positiveBn;  //确认和取消按钮
    public CommonDialog(Context context) {
        super(context);
    }
    private String message;
    private String title;
    private String positive,negtive;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.custom_dialog);
        initView();     //初始化界面控件
        initEvent();    //初始化界面控件的点击事件
    }
    //初始化界面控件
    private void initView() {
        negtiveBn = (Button) findViewById(R.id.negtive);
        positiveBn = (Button) findViewById(R.id.positive);
        titleTv = (TextView) findViewById(R.id.title);
        messageTv = (TextView) findViewById(R.id.message);
    }
    //初始化界面控件的显示数据
    private void refreshView() {
        //如果自定义了title和message会 显示自定义的信息,否则不显示title和message的信息
        if (!TextUtils.isEmpty(title)) {
            titleTv.setText(title);                 //设置标题控件的文本为自定义的title
            titleTv.setVisibility(View.VISIBLE); //标题控件设置为显示状态
        }else {
            titleTv.setVisibility(View.GONE);     //标题控件设置为隐藏状态
        }
        if (!TextUtils.isEmpty(message)) {
            messageTv.setText(message); //设置消息控件的文本为自定义的message信息
        }
        //如果自定义了按钮的文本,则按钮显示自定义的文本,否则,按钮显示“确定”或“取消”文本
        if (!TextUtils.isEmpty(positive)) {
            positiveBn.setText(positive); //设置按钮的文本为自定义的文本信息
        }else {
            positiveBn.setText("确定");    //设置按钮文本为“确定”
        }
        if (!TextUtils.isEmpty(negtive)) {
            negtiveBn.setText(negtive);
        }else {
            negtiveBn.setText("取消");
        }
    }
    //初始化界面的确定和取消监听器
    private void initEvent() {
        //设置确定按钮的点击事件的监听器
        positiveBn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onClickBottomListener!= null) {
                    onClickBottomListener.onPositiveClick();
                }
            }
        });
        //设置取消按钮的点击事件的监听器
        negtiveBn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if ( onClickBottomListener!= null) {
                    onClickBottomListener.onNegtiveClick();
                }
            }
        });
    }
    @Override
    public void show() {
        super.show();
        refreshView();
    }
    public interface OnClickBottomListener{
        void onPositiveClick();//实现确定按钮点击事件的方法
        void onNegtiveClick(); //实现取消按钮点击事件的方法
    }
    //设置确定取消按钮的回调
    public OnClickBottomListener onClickBottomListener;
    public CommonDialog setOnClickBottomListener(OnClickBottomListener
                                                         onClickBottomListener){
        this.onClickBottomListener = onClickBottomListener;
        return this;
    }
    public CommonDialog setMessage(String message) {
        this.message = message;
        return this ;
    }
    public CommonDialog setTitle(String title) {
        this.title = title;
        return this ;
    }
    public CommonDialog setPositive(String positive) {
        this.positive = positive;
        return this ;
    }
    public CommonDialog setNegtive(String negtive) {
        this.negtive = negtive;
        return this ;
    }
}
;