金职位_Android工程师

01 Android 零基础入门

01 步骤一:java 基础语法

02 步骤二:java 面向对象

03 步骤三:Java 常用工具类

04 步骤四:UI 基础入门

01 Android 入门

02 AS 使用指南

03 Android 线性与相对布局

第 1 章 课程介绍

第 2 章 详解 HelloWorld

.2-1 Activity-慕课网就业班 2019-08-02 22_26(更多 IT 教程 1 )

继承 activity 类(对应一个界面),就能实现窗口。

打开程序就会触发 oncreate,初始化等工作会写在这里



对应 layout 文件夹下的文件。将项目切换为 package 模式






.2-4 布局文件-慕课网就业班 2019-08-02 22_26(更多 IT 教程 1 )

布局的 xml 文件有两种模式,Design 和 Text

可以创建多个 Activity,使用不同的布局 layout 文件

那么如何知道要先加载哪个 activity 呢

2-7 清单文件-慕课网就业班 2019-08-02 22_26(更多 IT 教程 1 )

在 manifest.xml 里定义的 activity>action Main 就是表明这是 main 界面(只能有一个 main 界面)

category 能在应用列表里创建一个图标,可以通过图标进入该界面

第 3 章 布局基础

.3-1 布局的作用-慕课网就业班 2019-08-02 22_26(更多 IT 教程 1 )

.3-2 布局的种类-慕课网就业班 2019-08-02 22_28(更多 IT 教程 1 )




重点。通过选取参照物来决定布局位置



现在用得多,可以用较少的代码实现较为复杂的效果

.3-3 添加布局方式-慕课网就业班 2019-08-02 22_28(更多 IT 教程 1 )

package com.malugy.firstproject;

import android.graphics.Color;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_main);
        // 1.根布局为线性布局
        LinearLayout ll = new LinearLayout(this);
        // 2.设置宽高
        ll.setLayoutParams(new LinearLayout.LayoutParams(
                // 参数1:宽,参数2:高,这里设置为和窗口一样大
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        ));
        // 3.背景设为红色
        ll.setBackgroundColor(Color.RED);
        // 4.指定此activity的视图为该线性布局
        setContentView(ll);
    }
}

这种方式很不方便,界面设计一般用 design

第 4 章 线性布局(重点)

.4-1 线性布局的作用-慕课网就业班 2019-08-02 22_28(更多 IT 教程 1 )

.4-2 线性布局的使用-慕课网就业班 2019-08-02 22_28(更多 IT 教程 1 )

创建布局文件,名称必须全小写



设置布局的宽高,match_parent 就是和父容器一样大,这里布局没有父容器,所以就是和屏幕一样大。wrap_content 就是随着内容的增大而增大。

dp 是像素单位,但是和 px 不一样;sp 是用于字体的;

margin 外边距

padding 内边距



layout weight 可以占据剩余空间

layout weight 可以按比例分配空间

使用 layout weight 和 0dp width 后,width 只和 weight 分配后的大小有关,不会根据内容改变而改变

gravity 会让元素向某个方向靠(相对父容器的偏向)


<?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="horizontal">
    <!--
        android:orientation="vertical"
        vertical: 垂直
        horizontal: 水平
    -->
    <!--
        layout_weight,在其他元素摆好后根据占据剩余空间
    -->
    <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:layout_weight="1"
            android:background="#ff0000"
            android:text="hello"
            android:textSize="28sp"/>


    <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="1"
            android:background="#00ff00"
            android:text="hello"
            android:textSize="28sp"/>

    <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="#0000ff"
            android:text="hello"
            android:textSize="28sp"/>
</LinearLayout>
.4-5 线性布局案例演示-慕课网就业班 2019-08-02 22_28(更多 IT 教程 1 )

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical">

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:background="#333333"
            android:orientation="horizontal"
            android:paddingLeft="15dp">

        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:text="<"
                android:textColor="#ffffff"
                android:textSize="26sp"/>

        <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_weight="1"
                android:text="JOKER"
                android:textColor="#ffffff"
                android:textSize="26sp"/>

        <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/human"/>
    </LinearLayout>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="horizontal">

    </LinearLayout>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:background="#cccccc"
            android:orientation="horizontal">

        <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:src="@mipmap/audio"/>

        <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"/>

        <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:src="@mipmap/emoji"/>

        <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:src="@mipmap/plus"/>
    </LinearLayout>
</LinearLayout>


第 5 章 相对布局(重点)

.5-2 相对布局属性设置-慕课网就业班 2019-08-02 22_28(更多 IT 教程 1 )

<?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="100dp"
            android:layout_height="100dp"
            android:layout_centerInParent="true"
            android:background="#ff0000"
            android:text="屏幕正中"
            android:textSize="30sp"/>
</RelativeLayout>




如果要参照某元素进行相对布局,需要指定该元素 id




<?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:id="@+id/center"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_centerInParent="true"
            android:background="#ff0000"
            android:text="屏幕正中"
            android:textSize="30sp"/>

    <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_above="@id/center"
            android:layout_toLeftOf="@id/center"
            android:background="#00ff00"
            android:text="中偏左上"
            android:textSize="30sp"/>

    <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_above="@id/center"
            android:layout_toRightOf="@id/center"
            android:background="#00ff00"
            android:text="中偏右上"
            android:textSize="30sp"/>

    <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_below="@id/center"
            android:layout_toLeftOf="@id/center"
            android:background="#00ff00"
            android:text="中偏左下"
            android:textSize="30sp"/>

    <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_below="@id/center"
            android:layout_toRightOf="@id/center"
            android:background="#00ff00"
            android:text="中偏右下"
            android:textSize="30sp"/>

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignTop="@id/center"
            android:background="#0000ff"
            android:text="和中间上边线对齐"/>
</RelativeLayout>
.5-3 综合案例-慕课网就业班 2019-08-02 22_28(更多 IT 教程 1 )


<?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="0dp"
            android:layout_weight="2"
            android:background="#ff0000"
            android:orientation="horizontal">

        <RelativeLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="2"
                android:background="#ff00ff">

            <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@mipmap/icon_3d"/>

            <TextView
                    android:layout_width="match_parent"
                    android:layout_height="60dp"
                    android:layout_alignParentBottom="true"
                    android:background="#666666"
                    android:gravity="center"
                    android:text="复仇者联盟"
                    android:textColor="#ffffff"
                    android:textSize="24sp"/>

        </RelativeLayout>

        <LinearLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:orientation="vertical">

            <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="0dp"
                    android:layout_weight="1"
                    android:background="#ffff00">

                <ImageView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:src="@mipmap/icon_imax2d"/>
            </RelativeLayout>

            <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="0dp"
                    android:layout_weight="1"
                    android:background="#ff0000">

                <ImageView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:src="@mipmap/icon_3d"/>

                <TextView
                        android:layout_width="match_parent"
                        android:layout_height="60dp"
                        android:layout_alignParentBottom="true"
                        android:background="#666666"
                        android:gravity="center"
                        android:text="捉妖记"
                        android:textColor="#ffffff"
                        android:textSize="16sp"/>
            </RelativeLayout>
        </LinearLayout>
    </LinearLayout>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#00ff00"
            android:orientation="horizontal">

        <RelativeLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="#0000ff">

            <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@mipmap/icon_4d"/>

            <TextView
                    android:layout_width="match_parent"
                    android:layout_height="60dp"
                    android:layout_alignParentBottom="true"
                    android:background="#666666"
                    android:gravity="center"
                    android:text="像雾像雨又像风"
                    android:textColor="#ffffff"
                    android:textSize="16sp"/>
        </RelativeLayout>

        <RelativeLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="#00ff00">

            <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@mipmap/icon_imax3d"/>
        </RelativeLayout>

        <RelativeLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:background="#00ffff">

            <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@mipmap/icon_4d"/>

        </RelativeLayout>
    </LinearLayout>
</LinearLayout>

04 UI 基础控件

第 1 章 课程介绍

.1-1 课程及案例介绍-慕课网就业班 2019-08-02 22_40(更多 IT 教程 1 )


.1-3 布局及控件组成-慕课网就业班 2019-08-02 22_40(更多 IT 教程 1 )
<?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="wrap_content"
            android:layout_height="wrap_content"
            android:text="Sign Up"/>

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="lorem lorem lorem lorem lorem lorem
         lorem lorem lorem lorem lorem\n lorem lorem lorem
          lorem "/>

    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/add_photo"/>

    <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Register"/>
</LinearLayout>

第 2 章 TextView

2-1 通用属性-慕课网就业班 2019-08-02 22_40(更多 IT 教程 1 )




<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:background="@mipmap/bg"
              android:gravity="center_horizontal"
              android:orientation="vertical"
              tools:context=".MainActivity">

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="70dp"
            android:text="Sign Up"/>

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_margin="20dp"
            android:text="lorem lorem lorem lorem lorem lorem
         lorem lorem lorem lorem lorem\n lorem lorem lorem
          lorem "/>

    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:src="@mipmap/add_photo"/>

    <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="40dp"
            android:layout_marginRight="30dp"/>

    <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="40dp"
            android:layout_marginRight="30dp"/>

    <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="40dp"
            android:layout_marginRight="30dp"/>

    <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="40dp"
            android:layout_marginRight="30dp"/>

    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="40dp"
            android:layout_marginRight="30dp"
            android:background="@mipmap/btn"
            android:text="Register"/>
</LinearLayout>
.2-6 TextView 使用-慕课网就业班 2019-08-02 22_41(更多 IT 教程 1 )



如果有很长的文本,可以在 strings.xml 里定义变量,然后在控件里使用


<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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"
            android:orientation="vertical">

    <!--  scrollview里只能放一个直接子控件  -->
    <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:lineSpacingExtra="15sp"
            android:text="@string/long_txt"
            android:textColor="#00ffff"
            android:textSize="22sp"/>
</ScrollView>


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical">

    <!--
        android:lines="1" 指定行数
        下面设置跑马灯,亲测没用
    -->
    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="marquee"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:marqueeRepeatLimit="marquee_forever"
            android:singleLine="true"
            android:text="@string/long_txt"/>
</LinearLayout>

第 3 章 EditText


<EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="40dp"
        android:layout_marginRight="30dp"
        android:inputType="textPassword"/>

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_marginTop="40dp"
android:layout_marginRight="30dp"
android:inputType="number"/>

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_marginTop="40dp"
android:layout_marginRight="30dp"
android:inputType="phone"/>




<EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="30dp"
        android:layout_marginTop="40dp"
        android:layout_marginRight="30dp"
        android:background="@mipmap/border"
        android:gravity="center"
        android:hint="Name the Surname"
        android:inputType="textPassword"
        android:maxLength="12"
        android:textColorHint="#cccccc"/>

第 4 章 事件监听处理(重点)

4-1 自定义内部类方式-慕课网就业班 2019-08-02 22_41(更多 IT 教程 1 )



<Button
        android:id="@+id/btn1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="通过自定义内部类实现点击事件"/>
package com.malugy.firstproject;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import androidx.appcompat.app.AppCompatActivity;

public class ButtonActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_button);

        // 根据id获取按钮
        Button btn1 = findViewById(R.id.btn1);
        // 点击事件
        MyClickListener mcl = new MyClickListener();
        btn1.setOnClickListener(mcl); // 注册点击事件
    }

    // 1. 自定义实现类
    class MyClickListener implements View.OnClickListener {
        @Override
        public void onClick(View view) {
            // 控制台输出语句
            Log.e("TAG", "自定义内部类实现");
        }
    }
}
.4-4 匿名内部类方式-慕课网就业班 2019-08-02 22_41(更多 IT 教程 1 )


<Button
        android:id="@+id/btn2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="通过匿名内部类实现点击事件"/>

<Button
android:id="@+id/btn3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="通过当前activity实现点击事件接口"/>

package com.malugy.firstproject;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import androidx.appcompat.app.AppCompatActivity;

public class ButtonActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_button);

        Button btn2 = findViewById(R.id.btn2);
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.e("TAG", "匿名内部类");
            }
        });

        Button btn3 = findViewById(R.id.btn3);
        btn3.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        Log.e("TAG", "activity实现接口");
    }

    // 1. 自定义实现类
    class MyClickListener implements View.OnClickListener {
        @Override
        public void onClick(View view) {
            // 控制台输出语句
            Log.e("TAG", "自定义内部类实现");
        }
    }
}
.4-7 通过 xml 绑定点击事件-慕课网就业班 2019-08-02 22_48(更多 IT 教程 1 )

常用


<Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="在xml文件中绑定"
        android:onClick="myClick"/>
package com.malugy.firstproject;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import androidx.appcompat.app.AppCompatActivity;

public class ButtonActivity extends AppCompatActivity implements View.OnClickListener {
    /**
     * 自定义点击事件处理方法
     *
     * @param v 被点击的按钮
     */
    public void myClick(View v) {
        Log.e("TAG", "xml绑定");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_button);
    }
}

如果多个按钮用 xml 绑定,可以指定 id


第 5 章 ImageView

命名规则

放置字符串、常量

mipmap 会自动选择和当前设备最接近、最大的分辨率的图片

src:前景。无论控件如何改变宽高,都会保持自身比例缩放(可以用来准确地显示图片资源)


imagebutton,可以设置图片(前后景都可以),但是没有 text 属性

第 6 章 ProgressBar

package com.malugy.firstproject;

import android.os.Bundle;
import android.widget.ProgressBar;

import androidx.appcompat.app.AppCompatActivity;

public class ProgressBarActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_progress_bar);

        ProgressBar pb = findViewById(R.id.progress);
        pb.setProgress(80);
        // android4.0后不能直接在线程中操作控件,否则会崩溃
        // 进度条是特例
        new Thread() {
            @Override
            public void run() {
                for (int i = 1; i <= 100; i++) {
                    pb.setProgress(i);
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".ProgressBarActivity">

    <!--  默认样式是转圈  -->
    <ProgressBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    <!--
        style: 设置风格
        max: 设置进度条最大值(默认100)
        progress: 设置当前进度
    -->
    <ProgressBar
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:max="200"
            android:progress="30"/>

    <!--
        indeterminate: 设置进度条永恒滚动
    -->
    <ProgressBar
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:indeterminate="true"/>

    <ProgressBar
            android:id="@+id/progress"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
</LinearLayout>

第 7 章 案例完善

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:background="@mipmap/bg"
              android:gravity="center_horizontal"
              android:orientation="vertical"
              tools:context=".MainActivity">

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:text="Sign Up"/>

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_margin="20dp"
            android:text="lorem lorem lorem lorem lorem lorem
         lorem lorem lorem lorem lorem\n lorem lorem lorem
          lorem "/>

    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:src="@mipmap/add_photo"/>

    <ProgressBar
            android:id="@+id/probar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:visibility="invisible"/>


    <EditText
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="68dp"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="10dp"
            android:layout_marginRight="30dp"
            android:background="@mipmap/border"
            android:gravity="center"
            android:hint="Name the Surname"
            android:textColorHint="#cccccc"/>


    <EditText
            android:id="@+id/pwd"
            android:layout_width="match_parent"
            android:layout_height="68dp"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="10dp"
            android:layout_marginRight="30dp"
            android:background="@mipmap/border"
            android:gravity="center"
            android:hint="Password"
            android:inputType="textPassword"
            android:maxLength="12"
            android:textColorHint="#cccccc"/>

    <EditText
            android:layout_width="match_parent"
            android:layout_height="68dp"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="10dp"
            android:layout_marginRight="30dp"
            android:background="@mipmap/border"
            android:gravity="center"
            android:hint="Phone"
            android:inputType="phone"
            android:textColorHint="#cccccc"/>

    <EditText
            android:layout_width="match_parent"
            android:layout_height="68dp"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="10dp"
            android:layout_marginRight="30dp"
            android:background="@mipmap/border"
            android:gravity="center"
            android:hint="Email Address"
            android:inputType="number"
            android:textColorHint="#cccccc"/>


    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="30dp"
            android:layout_marginTop="40dp"
            android:layout_marginRight="30dp"
            android:background="@mipmap/btn"
            android:onClick="register"
            android:text="Register"/>
</LinearLayout>
package com.malugy.firstproject;

import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void register(View v) {
        ProgressBar pb = findViewById(R.id.probar);
        EditText nameEdt = findViewById(R.id.name);
        EditText pwdEdt = findViewById(R.id.pwd);

        String name = nameEdt.getText().toString();
        String pwd = pwdEdt.getText().toString();
        // 1. 判断姓名密码是否为空
        if (name.equals("") || pwd.equals("")) {
            // 2. 为空,提示
            // 无焦点提示
            // 参数1: 环境上下文; 参数2: 提示性文本; 参数3: 提示持续时间
            Toast.makeText(this, "姓名或密码不能为空", Toast.LENGTH_SHORT).show();
        } else {
            // 3. 都不为空,则出现进度条(这里使用进度条只是为了教学演示)
            pb.setVisibility(View.VISIBLE);
            new Thread() {
                @Override
                public void run() {
                    for (int i = 0; i <= 100; i++) {
                        pb.setProgress(i);
                        try {
                            Thread.sleep(30);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
        }
    }
}

05 Android 约束布局

第 1 章 课程介绍


第 2 章 Android 基本布局

.2-1 线性布局、相对布局(重点)-慕课网就业班 2019-08-02 23_58(更多 IT 教程 1x)


.2-3 帧布局-慕课网就业班 2019-08-02 23_58(更多 IT 教程 1x)

package com.malugy.firstproject;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;

import androidx.appcompat.app.AppCompatActivity;

public class MainLayoutActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_zhenlayout);
    }

    public void myclick(View v) {
        switch (v.getId()) {
            case R.id.frame:
                // 显示frameActivity
                startActivity(new Intent(this, FrameActivity.class));
                break;
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".MainLayoutActivity">

    <Button
            android:id="@+id/frame"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="myclick"
            android:text="帧布局"/>

    <Button
            android:id="@+id/table"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="myclick"
            android:text="表格布局"/>

    <Button
            android:id="@+id/grid"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="myclick"
            android:text="网格布局"/>

    <Button
            android:id="@+id/constraint"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="myclick"
            android:text="约束布局"/>
</LinearLayout>

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
             android:foreground="@mipmap/ic_launcher"
             android:foregroundGravity="center"
             tools:context=".FrameActivity">

    <TextView
            android:layout_width="350dp"
            android:layout_height="350dp"
            android:layout_gravity="center"
            android:background="#ff0000"/>

    <TextView
            android:layout_width="300dp"
            android:layout_height="300dp"
            android:layout_gravity="center"
            android:background="#00ff00"/>

    <TextView
            android:layout_width="250dp"
            android:layout_height="250dp"
            android:layout_gravity="center"
            android:background="#0000ff"/>

    <TextView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_gravity="center"
            android:background="#00ffff"/>

    <TextView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:layout_gravity="center"
            android:background="#ff00ff"/>

    <!--  gravity是设置内容在该控件的位置  -->
    <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_gravity="center"
            android:background="#ffff00"
            android:gravity="left"
            android:text="黄色的文本"/>
</FrameLayout>
.2-5 表格布局-慕课网就业班 2019-08-02 23_59(更多 IT 教程 1x)



<?xml version="1.0" encoding="utf-8"?>
<TableLayout 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"
             android:stretchColumns="*"
             tools:context=".Table">
    <!--
          android:stretchColumns="3" -> 对应列的元素可以伸展占据剩余空间
          可以用逗号分割,指定多个
      -->
    <!--
    表格布局不指定宽高,则控件和父容器等宽
    如果想让控件出现在同一行,则要在控件外层加一个tableRow
      -->
    <EditText/>

    <TableRow>
        <!-- tablerow里的控件默认wrap_content -->
        <Button android:text="7"/>

        <Button android:text="8"/>

        <Button android:text="9"/>

        <Button android:text="/"/>
    </TableRow>

    <TableRow>

        <Button android:text="4"/>

        <Button android:text="5"/>

        <Button android:text="6"/>

        <Button android:text="*"/>
    </TableRow>

    <TableRow>

        <Button android:text="1"/>

        <Button android:text="2"/>

        <Button android:text="3"/>

        <Button android:text="-"/>
    </TableRow>

    <TableRow>

        <Button android:text="0"/>

        <Button android:text="."/>

        <Button android:text="+"/>

        <Button android:text="="/>
    </TableRow>

    <Button android:text="clear"/>
</TableLayout>
.2-7 网格布局-慕课网就业班 2019-08-03 00_00(更多 IT 教程 11)

<?xml version="1.0" encoding="utf-8"?>
<GridLayout 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="wrap_content"
            android:layout_height="wrap_content"
            android:columnCount="4"
            android:orientation="horizontal"
            android:rowCount="5"
            tools:context=".GridActivity">

    <Button android:text="1"/>

    <Button android:text="2"/>

    <Button android:text="3"/>

    <Button android:text="/"/>

    <Button android:text="4"/>

    <Button android:text="5"/>

    <Button android:text="6"/>

    <Button android:text="*"/>

    <Button android:text="7"/>

    <Button android:text="8"/>

    <Button android:text="9"/>

    <Button android:text="-"/>

    <!--
      layout_columnSpan: 该元素跨几个列
      layout_gravity: fill -> 跨列填充
      -->
    <Button
            android:layout_columnSpan="2"
            android:layout_gravity="fill"
            android:text="0"/>

    <Button android:text="."/>
    <!--
      -->
    <Button
            android:layout_rowSpan="2"
            android:layout_gravity="fill"
            android:text="+"/>

    <Button
            android:layout_columnSpan="3"
            android:layout_gravity="fill"
            android:text="="/>
</GridLayout>

第 3 章 约束布局(重点)

.3-1 约束布局简介-慕课网就业班 2019-08-03 00_01(更多 IT 教程 1 )



.3-3 约束布局基本使用-慕课网就业班 2019-08-03 00_00(更多 IT 教程 1x)

默认是约束布局,也可以从线性布局切换

我们将 btn 拖到了蓝图,但是 btn 会提示没有进行约束,因为我们指定位置为具体数值,如果不添加约束,会跳到(0,0)
(设计的时候看不出来,一运行就会跳)



中间的圆点可以指定控件的边界,添加上约束


添加完约束,拖动控件到任意位置也不会跳到(0,0)(如果没有指定 y 方向,则会跳到 y=0,没有指定 x 方向,则跳到
x=0,两个都没有就是(0,0))


新版在这里删除约束

约束可以相对于控件




清除约束

.3-5 Inspector 的认识-慕课网就业班 2019-08-03 00_09(更多 IT 教程 1x)




点击中间的线段,切换宽高的模式


3-6 自动添加约束-慕课网就业班 2019-08-03 00_00(更多 IT 教程 1x)

.3-7 Guidelines 的使用-慕课网就业班 2019-08-03 00_00(更多 IT 教程 1x)


点小圆钮可以切换显示左、右到屏幕边的距离,还有两边所占的比例

.3-8 效果实现-慕课网就业班 2019-08-03 00_00(更多 IT 教程 1x)

设置为横板



我使用 textView+背景实现纯色块

技巧:复制黏贴后,被复制的控件位置上出现新的控件,焦点也是在新控件上,因为此时边是 Match
约束模式,所以可以拖动新控件的中间的那个小圆点改变约束到要移动到的位置





06 综合案例:选餐

07 UI 基础入门测试

05 步骤五:UI 常用组件

01 Activity 入门

第 1 章 课程介绍

四大组件

第 2 章 Activity 基础(重点)

2-1 初始 Activity-慕课网就业班 2019-08-03 02_35(更多 IT 教程 11)

activity 是展示视图的,可以与用户交互,接收用户输入,而其他三大组件都是用户不可感知的。

.2-2 Activity 与 Layout 的关系(重点)-慕课网就业班 2019-08-03 02_39(更多 IT 教程 1x)


继承 AppCompatActivity 的类才是 activity

加了 intent-filter,并指定为 main,则作为启动页

如果有两个 activity,里面都有 intent-filter main,则会有两个软件入口




.2-4 Activity 与 View 的关系-慕课网就业班 2019-08-03 02_37(更多 IT 教程 1x)

xml 里写的控件是静态的,我们需要代码来让控件可以进行交互

package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 通过id获取视图
        TextView tv = findViewById(R.id.textView);
        Button btn = findViewById(R.id.button);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                tv.setText(getString(R.string.app_name));
            }
        });
    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.189"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.099"/>

    <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.14"/>

    <ImageView
            android:id="@+id/imageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.3"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button"
            app:layout_constraintVertical_bias="0.112"
            app:srcCompat="@drawable/ic_launcher_background"/>

</androidx.constraintlayout.widget.ConstraintLayout>

其实 xml 里的东西会经过安卓的解析,然后转化为 java 对象,生成视图

.2-6 Activity 间的跳转(重点)-慕课网就业班 2019-08-03 02_37(更多 IT 教程 1x)
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class TestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        Button btn = findViewById(R.id.button2);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 从test跳到main
                Intent intent = new Intent(TestActivity.this, MainActivity.class);
                startActivity(intent);
            }
        });
    }
}
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 通过id获取视图
        TextView tv = findViewById(R.id.textView);
        Button btn = findViewById(R.id.button);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                tv.setText(getString(R.string.app_name));
                // 从main跳到test
                Intent intent=new Intent(MainActivity.this,TestActivity.class);
                startActivity(intent);
            }
        });
    }
}

第 3 章 Activity 的四种启动模式(难点)

.3-1 四种启动模式_理论(重点)-慕课网就业班 2019-08-03 02_35(更多 IT 教程 11)




现在有5种


被多次打开的activity,其后面的activity会被弹出

.3-3 四种启动模式_应用(难点)-慕课网就业班 2019-08-03 02_35(更多 IT 教程 11)

通过 intent 进行跳转,多次点击按钮触发,然后按返回键看是什么效果

standard 模式跳转新建 activity

先开 A(singleTask)然后开 B,再开 A,按返回,直接回桌面

先开 A(singleInstance),再开 B,再开 A,再开 B,然后按返回,回到 A,再按返回,回到 B,再按返回,到桌面

singletop 测试,让 intent 每次都跳转自身 activity,不断跳转,查看 singleTop 和 standard 的不同

代码里可以使用这 Flag 实现这些功能

02 Android 菜单创建和使用

第 1 章 课程介绍


第 2 章 Menu 概述

.2-1 Android3.0 之前的 menu-慕课网就业班 2019-08-03 02_43(更多 IT 教程 11)

.2-2 Android3.0 之后的 menu-慕课网就业班 2019-08-03 02_41(更多 IT 教程 1x)

.2-3 menu 的分类-慕课网就业班 2019-08-03 02_41(更多 IT 教程 1x)




第 3 章 Menu 的使用(重点)

.3-1 OptionMenu-慕课网就业班 2019-08-03 02_43(更多 IT 教程 11)






因为更多操作这个 menuitem 下还有其他 item(子菜单),所以拖动添加菜单到‘更多操作’下

它的可以文字+图标,我的失败了

遇见奇怪的 warning


package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
    }

    // 创建OptionMenu
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // 加载菜单资源
        getMenuInflater().inflate(R.menu.option, menu);
        // 让菜单显示
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        // 获取被点击的item的id
        switch (item.getItemId()) {
            case R.id.save:
                Toast.makeText(this, "保存", Toast.LENGTH_SHORT).show();
                break;
            case R.id.setting:
                Toast.makeText(this, "设置", Toast.LENGTH_SHORT).show();
                break;
            case R.id.exit:
                // 退出程序
                finish();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">

    <!--
        app:showAsAction="always": 显示在最外层顶层(操作栏)
        android:icon 图标
    -->
    <item
            android:id="@+id/save"
            android:icon="@mipmap/ic_launcher"
            android:title="保存"
            app:showAsAction="always"/>
    <item
            android:id="@+id/setting"
            android:title="设置"/>
    <item android:title="更多操作">
        <menu>
            <!-- 子菜单不能超过两层 -->
            <item
                    android:id="@+id/exit"
                    android:title="退出"/>
            <item android:title="子菜单1"/>
            <item android:title="子菜单2"/>
            <item android:title="子菜单3"/>
        </menu>
    </item>
</menu>
.3-3 ContextMenu-慕课网就业班 2019-08-03 02_44(更多 IT 教程 1x)


package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

public class NewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_new);

        // 在长按位置弹出上下文菜单栏
        // 1. 注册
        registerForContextMenu(findViewById(R.id.ctx_btn));
        // 2. 创建 覆盖onCreateContextMenu
        // 3. 菜单项操作 覆盖onContextItemSelected
        // 4. 为按钮设置上下文操作模式

        // other: 在顶部出现上下文菜单
        // 1 实现ActionMode CallBack
        // 2 在view的长按事件中启动上下文操作模式
        findViewById(R.id.ctx_btn).setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                startActionMode(cb);
                return false;
            }
        });
    }

    ActionMode.Callback cb = new ActionMode.Callback() {
        // 创建,在启动上下文操作模式时调用
        @Override
        public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
            Log.e("TAG", "创建");
            getMenuInflater().inflate(R.menu.context, menu);
            return true;
        }

        // 创建方法后调用
        @Override
        public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
            Log.e("TAG", "准备");
            return false;
        }

        // 菜单项被点击
        @Override
        public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
            Log.e("TAG", "selected");
            switch (menuItem.getItemId()) {
                case R.id.delete:
                    Toast.makeText(NewActivity.this, "删除", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.item1:
                    Toast.makeText(NewActivity.this, "item1", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.item2:
                    Toast.makeText(NewActivity.this, "item2", Toast.LENGTH_SHORT).show();
                    break;
            }
            return true;
        }

        // 上下文操作模式结束时调用
        @Override
        public void onDestroyActionMode(ActionMode actionMode) {
            Log.e("TAG", "结束");
            Toast.makeText(NewActivity.this, "over", Toast.LENGTH_SHORT).show();
        }
    };
/*    @Override
    public boolean onContextItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.delete:
                Toast.makeText(this, "删除", Toast.LENGTH_SHORT).show();
                break;
            case R.id.item1:
                Toast.makeText(this, "item1", Toast.LENGTH_SHORT).show();
                break;
            case R.id.item2:
                Toast.makeText(this, "item2", Toast.LENGTH_SHORT).show();
                break;
        }
        return super.onContextItemSelected(item);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        getMenuInflater().inflate(R.menu.context, menu);
    }*/
}
<!-- menu/context.xml -->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
            android:id="@+id/delete"
            android:title="删除"/>
    <item android:title="重命名">
        <menu>
            <item
                    android:id="@+id/item1"
                    android:title="Item1"/>
            <item
                    android:id="@+id/item2"
                    android:title="Item2"/>
        </menu>
    </item>
</menu>
.3-6 PopupMenu-慕课网就业班 2019-08-03 02_41(更多 IT 教程 11)


package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.Toast;

public class NewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_new);

        // popup 弹出式菜单
        Button popBtn = findViewById(R.id.popup_btn);
        popBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 1. 实例化PopupMenu对象(参数2:被锚定的view)
                PopupMenu menu = new PopupMenu(NewActivity.this, popBtn);
                // 2. 加载菜单资源
                menu.getMenuInflater().inflate(R.menu.popup, menu.getMenu());
                // 3. 为PopupMenu设置点击监听器
                menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
                    @Override
                    public boolean onMenuItemClick(MenuItem menuItem) {
                        switch (menuItem.getItemId()) {
                            case R.id.copy:
                                Toast.makeText(
                                        NewActivity.this, "复制", Toast.LENGTH_SHORT
                                ).show();
                                break;
                            case R.id.paste:
                                Toast.makeText(
                                        NewActivity.this, "黏贴", Toast.LENGTH_SHORT
                                ).show();
                                break;
                        }
                        return false;
                    }
                });
                // ! 4. 显示
                menu.show();
            }
        });
    }
}
<!-- menu/popup.xml -->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
            android:id="@+id/copy"
            android:title="复制"/>
    <item
            android:id="@+id/paste"
            android:title="粘贴"/>
</menu>

popup menu 是安卓 11 后才有的

第 4 章 常见问题与经验分享

.4-1 xml 定义 menu 的优势-慕课网就业班 2019-08-03 02_49(更多 IT 教程 1x)

    @Override
    public boolean onContextItemSelected(@NonNull MenuItem item) {
//        switch (item.getItemId()) {
//            case R.id.delete:
//                Toast.makeText(this, "删除", Toast.LENGTH_SHORT).show();
//                break;
//            case R.id.item1:
//                Toast.makeText(this, "item1", Toast.LENGTH_SHORT).show();
//                break;
//            case R.id.item2:
//                Toast.makeText(this, "item2", Toast.LENGTH_SHORT).show();
//                break;
//        }
        switch (item.getItemId()) {
            case 1:
                Toast.makeText(this, "设置", Toast.LENGTH_SHORT).show();
                break;
            case 2:
                Toast.makeText(this, "更多", Toast.LENGTH_SHORT).show();
                break;
            case 3:
                Toast.makeText(this, "添加", Toast.LENGTH_SHORT).show();
                break;
            case 4:
                Toast.makeText(this, "删除", Toast.LENGTH_SHORT).show();
                break;
        }
        return super.onContextItemSelected(item);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        // 加载菜单资源
        // xml资源
//        getMenuInflater().inflate(R.menu.context, menu);

        // 纯java代码设计
        /*
            设置
            更多
                |-> 添加
                |-> 删除
         */
        // 参数1: 组id; 参数2: 菜单项id; 参数3: 序号; 参数4: title
        menu.add(1, 1, 1, "设置");
        SubMenu sub = menu.addSubMenu(1, 2, 2, "更多");
        sub.add(2, 3, 1, "添加");
        sub.add(2, 4, 2, "删除");
    }

.4-2 xml 定义的 menu 不显示-慕课网就业班 2019-08-03 02_47(更多 IT 教程 11)


03 Android 对话框处理

第 1 章 课程介绍

第 2 章 提示对话框 (AlertDialog)


package com.malugy.activitydemo;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;

public class DialogActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog);
    }

    public void showNormalDialog() {
        AlertDialog dialog = new AlertDialog.Builder(this).create();
        dialog.setTitle("提示");
        dialog.setMessage("您确定退出程序吗?");
        dialog.setButton(DialogInterface.BUTTON_POSITIVE, "确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {

            }
        });
        dialog.show();
    }

    public void dialogClick(View v) {
        switch (v.getId()) {
            case R.id.normal_dialog_btn:
                // AlertDialog的构造方法是protected
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("提示");
                builder.setMessage("您确定退出程序吗?");
                builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        finish();
                    }
                });
                builder.setNegativeButton("取消", null);
                builder.show();
//                AlertDialog alertDialog = builder.create();
//                alertDialog.show();
                break;
            case R.id.diy_dialog_btn:
                showNormalDialog();
                break;
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              android:padding="5dp"
              tools:context=".DialogActivity">

    <Button
            android:id="@+id/normal_dialog_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="dialogClick"
            android:text="显示一个普通对话框"/>

    <Button
            android:id="@+id/diy_dialog_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="dialogClick"
            android:text="显示一个自定义对话框"/>
</LinearLayout>

第 3 章 自定义对话框(重点)


package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.KeyboardShortcutGroup;
import android.view.Menu;
import android.view.View;

import java.util.List;

public class MyDialog extends Dialog {

    public MyDialog(@NonNull Context context, int themeResId) {
        super(context, themeResId);
        // 为对话框设置布局
        setContentView(R.layout.dialog_layout);

        findViewById(R.id.yes_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 退出程序
                System.exit(0);
            }
        });
        findViewById(R.id.no_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dismiss();
            }
        });
    }
}
<?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:layout_gravity="center"
              android:background="@mipmap/dialog_bg"
              android:orientation="vertical">

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="255dp"
            android:text="你确定要退出吗?"
            android:textColor="#e61414"
            android:textSize="34sp"
            android:textStyle="bold"/>

    <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_margin="25dp"
            android:orientation="horizontal">

        <Button
                android:id="@+id/no_btn"
                android:layout_width="120dp"
                android:layout_height="50dp"
                android:background="@mipmap/no_btn"/>

        <Button
                android:id="@+id/yes_btn"
                android:layout_width="120dp"
                android:layout_height="50dp"
                android:background="@mipmap/yes_btn"
                android:layout_marginLeft="20dp"/>

    </LinearLayout>

</LinearLayout>
package com.malugy.activitydemo;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;

public class DialogActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog);
    }

    public void dialogClick(View v) {
        switch (v.getId()) {
            case R.id.normal_dialog_btn:
                // ...
                break;
            case R.id.diy_dialog_btn:
                MyDialog md = new MyDialog(this, R.style.mydialog);
                md.show();
                break;
        }
    }
}

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.ActivityDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">
        <!-- ... -->
    </style>
    <!-- 设置对话框风格 -->
    <style name="mydialog" parent="android:style/Theme.Dialog">
        <!-- 窗口没有标题 -->
        <item name="android:windowNoTitle">true</item>
        <!-- 透明背景 -->
        <item name="android:windowBackground">@android:color/transparent</item>
    </style>
</resources>

第 4 章 PopupWindow(重点)

.4-1 基础配置-慕课网就业班 2019-08-03 02_51(更多 IT 教程 11)


package com.malugy.activitydemo;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.PopupWindow;

public class DialogActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog);
    }

    public void dialogClick(View v) {
        switch (v.getId()) {
            // ...
            case R.id.popup_btn:
                showPopupWindow(v);
                break;
        }
    }

    private void showPopupWindow(View anchor) {
        // 准备弹出需要的视图对象
        // 参数2: 父容器
        View v = LayoutInflater.from(this).inflate(R.layout.popup_layout, null);
        // 1. 实例化
        // 参数1:用在弹窗中的view
        // 参数2、3:弹窗的宽高(单位为px)
        // 参数4:能否获取焦点
        PopupWindow window = new PopupWindow(v, 552, 102, true);
        // 2. 设置

        // 3. 显示
        // 参数1(anchor):锚点 -> 显示在该控件的下方
        // 参数2、3:相对于锚点,在相对位置上的偏移量
        window.showAsDropDown(anchor, 100, 0);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:gravity="center_horizontal"
              android:orientation="vertical"
              android:padding="5dp"
              tools:context=".DialogActivity">

    <!-- ... -->

    <Button
            android:id="@+id/popup_btn"
            android:layout_width="160dp"
            android:onClick="dialogClick"
            android:layout_height="wrap_content"
            android:text="显示弹窗"/>

</LinearLayout>
<!-- layout/popup_layout.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:background="#00ffff"
              android:orientation="horizontal"
              android:padding="2dp">

    <TextView
            android:layout_width="60dp"
            android:layout_height="30dp"
            android:background="#000000"
            android:gravity="center"
            android:text="选择"
            android:textColor="#ffffff"/>
    <TextView
            android:layout_width="60dp"
            android:layout_height="30dp"
            android:background="#000000"
            android:gravity="center"
            android:text="全选"
            android:textColor="#ffffff"/>
    <TextView
            android:layout_width="60dp"
            android:layout_height="30dp"
            android:background="#000000"
            android:gravity="center"
            android:text="复制"
            android:textColor="#ffffff"/>
</LinearLayout>
.4-2 设置样式-慕课网就业班 2019-08-03 02_51(更多 IT 教程 11)
    private void showPopupWindow(View anchor) {
        // 准备弹出需要的视图对象
        // 参数2: 父容器
        View v = LayoutInflater.from(this).inflate(R.layout.popup_layout, null);
        // 1. 实例化
        // 参数1:用在弹窗中的view
        // 参数2、3:弹窗的宽高(单位为px)
        // 参数4:能否获取焦点
        PopupWindow window = new PopupWindow(v, 552, 102, true);
        // 2. 设置
        // 设置背景
        // 透明背景 -> 不会影响我们在layout里设置的背景等
        window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        // 设置能响应外部的点击事件
        window.setOutsideTouchable(true);
        // 设置能响应点击事件
        window.setTouchable(true);
        // 3. 显示
        // 参数1(anchor):锚点 -> 显示在该控件的下方
        // 参数2、3:相对于锚点,在相对位置上的偏移量
        window.showAsDropDown(anchor, 100, 0);
    }
.4-3 事件响应-慕课网就业班 2019-08-03 02_51(更多 IT 教程 11)
    private void showPopupWindow(View anchor) {
        /// ...
        window.showAsDropDown(anchor, 100, 0);
        // 弹窗中的文本添加点击事件
        v.findViewById(R.id.choose).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(DialogActivity.this, "选择", Toast.LENGTH_SHORT)
                        .show();
                // 弹窗消失
                window.dismiss();
            }
        });
        v.findViewById(R.id.choose_all).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(DialogActivity.this, "全选", Toast.LENGTH_SHORT)
                        .show();
                window.dismiss();
            }
        });
        v.findViewById(R.id.copy).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(DialogActivity.this, "复制", Toast.LENGTH_SHORT)
                        .show();
                window.dismiss();
            }
        });
    }
.4-4 设置动画(选修)-慕课网就业班 2019-08-03 02_51(更多 IT 教程 11)

    private void showPopupWindow(View anchor) {
        // 准备弹出需要的视图对象
        // 参数2: 父容器
        View v = LayoutInflater.from(this).inflate(R.layout.popup_layout, null);
        // 1. 实例化
        // 参数1:用在弹窗中的view
        // 参数2、3:弹窗的宽高(单位为px)
        // 参数4:能否获取焦点
        PopupWindow window = new PopupWindow(v, 552, 102, true);
        // 2. 设置
        // ...
        // >1 创建动画资源 >2 创建一个style应用动画资源 >3 对当前弹窗的动画风格设置为第二步的资源索引
        window.setAnimationStyle(R.style.translate_anim);
        // 3. 显示
        // ...
    }
<!-- theme.xml -->
<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.ActivityDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">
        ...
    </style>
    <!-- 设置对话框风格 -->
    <style name="mydialog" parent="android:style/Theme.Dialog">
        ...
    </style>
    <!-- 动画资源 -->
    <style name="translate_anim">
        <item name="android:windowEnterAnimation">@anim/translate</item>
    </style>
</resources>
<!-- anim/translate -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- pop win 动画 -->
    <!--
        translate: 移动动画
        fromXDelta: 0 -> toXDelta: 0 表示起始和结束位置不变
    -->
    <translate
            android:duration="2000"
            android:fromXDelta="0"
            android:fromYDelta="300"
            android:toXDelta="0"
            android:toYDelta="0"></translate>
</set>

第 5 章 ArrayAdapter 数据适配器对话框



    private void showArrayDialog() {
        final String[] items = {"java", "mysql", "android", "html", "c", "javascript"};
        // 数组适配器
        // 参数1: 环境; 参数2: 布局资源索引,每一项数据所呈现的样式
        // 参数3: 数据源
        ArrayAdapter adapter = new ArrayAdapter(
                // 3个参数的构造方法需要传入的layout根元素是textView
//                this, android.R.layout.simple_dropdown_item_1line, items);
                // 4个参数的构造方法多了一个指定textView的id的参数.
                // 就不需要根元素是textView
                this, R.layout.array_item_layout, R.id.item_text, items);
        AlertDialog.Builder builder = new AlertDialog.Builder(this)
                .setTitle("请选择")
                // 参数1: 适配器对象(对数据显示样式的规则制定器)
                // 参数2: 监听器
                .setAdapter(adapter, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        Toast.makeText(DialogActivity.this, items[i], Toast.LENGTH_SHORT)
                                .show();
                        dialogInterface.dismiss();
                    }
                });
        builder.show();
    }
<!-- array_item_layout.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:gravity="center_vertical"
              android:orientation="horizontal"
              android:padding="10dp">

    <ImageView
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@mipmap/star"/>

    <TextView
            android:id="@+id/item_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="15dp"
            android:textSize="24sp"
            android:text="text"/>
</LinearLayout>

04 Activity 生命周期详解

第 1 章 课程介绍

第 2 章 生命周期详解(重点)

.2-1 Activity 第一条生命线周期-慕课网就业班 2019-08-03 03_01(更多 IT 教程 1x)



package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

public class LifeCycleActivity extends AppCompatActivity {
    // 快捷: logi
    private static final String TAG = "LifeCycleActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_life_cycle);
        Log.i(TAG, "onCreate: ");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i(TAG, "onStart: ");
    }

    // 此时可以和用户进行交互
    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "onResume: ");
    }

    // 被暂停
    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "onPause: ");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i(TAG, "onStop: ");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy: ");
    }
}

按返回键,会依次执行 onPause onStop onDestroy

.2-4 Activity 生命周期的其他分支-慕课网就业班 2019-08-03 03_01(更多 IT 教程 11)

打开 app,进入 lifecycleActivity

打开其他 activity

回到 lifecycle activity



当应用要被销毁(删除),可以在这个生命周期里来保存一些数据


按返回键时触发

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_life_cycle);
        Log.i(TAG, "onCreate: ");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i(TAG, "onStart: ");
    }

    // 此时可以和用户进行交互
    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "onResume: ");
    }

    // 被暂停
    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "onPause: ");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i(TAG, "onStop: ");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy: ");
    }

    // 按下返回键

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        Log.i(TAG, "onBackPressed: ");
    }

    // 应用被销毁前
    @Override
    public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);
        outState.putString("key","hello");
        Log.i(TAG, "onSaveInstanceState: ");
    }
.2-9 两个 Activity 之间的数据传递(难点)-慕课网就业班 2019-08-03 03_12(更多 IT 教程 11)

package com.malugy.activitydemo;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class TestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        Button btn2 = findViewById(R.id.button_finish);

        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                setResult(RESULT_OK);
                finish();
            }
        });

        if (getIntent() != null) {
            String stringExtra = getIntent().getStringExtra(LifeCycleActivity.BUTTON_TITLE);
            if (stringExtra != null) {
                Log.i("TAG", stringExtra);
            }
            Bundle bundle = getIntent().getBundleExtra(LifeCycleActivity.BUTTON_TITLE);
            if (bundle != null) {
                Log.i("TAG", bundle.getString(LifeCycleActivity.BUTTON_TITLE));
            }
            Toast.makeText(TestActivity.this, "111", Toast.LENGTH_SHORT).show();
        }
    }
}


package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.Log;
import android.view.View;

public class LifeCycleActivity extends AppCompatActivity {
    // 快捷: logi
    private static final String TAG = "LifeCycleActivity";
    public static final String BUTTON_TITLE = "button_title";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_life_cycle);
        Log.i(TAG, "onCreate: ");
        initViews();
    }

    private void initViews() {
        findViewById(R.id.buttonActivity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final Intent intent = new Intent(LifeCycleActivity.this, TestActivity.class);
                //===============第一种=================================
                //intent.putExtra(BUTTON_TITLE,getString(R.string.imooc_title));
                //startActivity(intent);

                //==============第二种=================================
                Bundle bundle = new Bundle();
                bundle.putString(BUTTON_TITLE, "慕课网");
                intent.putExtra(BUTTON_TITLE, bundle);
//                startActivity(intent);
                 startActivityForResult(intent, 999);

                //==============第三种=================================
                //intent.putExtra(BUTTON_TITLE, new User());
                //startActivity(intent);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 999 && resultCode == RESULT_OK) {
            setTitle("返回");
        }
    }

}

第 3 章 总结和扩展


05 Fragment 创建及使用

第 1 章 课程介绍

第 2 章 Fragment 介绍

.2-1 理论基础-慕课网就业班 2019-08-03 12_35(更多 IT 教程 11)

安卓 3.0 提出,为了适配不同分辨率的终端而设计

建议使用 support 包,因为可以兼容老版本


.2-4 初识 Fragment-慕课网就业班 2019-08-03 12_38(更多 IT 教程 1x)

功能或页面复杂时,会分为多个 fragment 来单独(分别)处理


package com.malugy.activitydemo;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FragmentDemoActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_demo);

        // 设置点击事件
        findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 静态加载 fragment

            }
        });
    }

    /**
     * 列表fragment
     */
    public static class ListFragment extends Fragment {
        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
        }

        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }

        // ! 创建视图
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
            return super.onCreateView(inflater, container, savedInstanceState);
        }

        @Override
        public void onStart() {
            super.onStart();
        }

        @Override
        public void onPause() {
            super.onPause();
        }

        @Override
        public void onStop() {
            super.onStop();
        }

        @Override
        public void onDestroyView() {
            super.onDestroyView();
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".FragmentDemoActivity">

    <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:gravity="center"
            android:text="static load fragment"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
</LinearLayout>

第 3 章 Fragment 的加载(重点)

.3-1 静态加载的应用(重点)-慕课网就业班 2019-08-03 12_36(更多 IT 教程 11)

按 f6 可以提取类


package com.malugy.activitydemo;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FragmentDemoActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_demo);

        // 设置点击事件
        findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 静态加载 fragment
                startActivity(new Intent(FragmentDemoActivity.this, StaticLoadFragmentActivity.class));
            }
        });
    }

}
package com.malugy.activitydemo;

import android.os.Bundle;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public class StaticLoadFragmentActivity extends AppCompatActivity {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_static_load_fragment);
    }
}
package com.malugy.activitydemo;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

/**
 * 列表fragment
 */
public class ListFragment extends Fragment {
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    // ! 创建视图
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        // 创建视图
        // 参数1: 布局文件; 参数2: 当前fragment所在的group; 参数3: 是否绑定到当前根布局
        View view = inflater.inflate(R.layout.fragment_list, container, false);

        TextView textView = view.findViewById(R.id.tv);
        textView.setText("imooc");
        return view;
    }

    @Override
    public void onStart() {
        super.onStart();
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    @Override
    public void onStop() {
        super.onStop();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
    }
}
<!-- fragment_list.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="@color/purple_200">

    <TextView
            android:id="@+id/tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="TextView"
            android:textColor="#ffffff"
            android:textSize="20sp"/>
</RelativeLayout>
<!-- activity_static_load_fragment.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
                android:orientation="vertical"
                tools:context=".MainActivity">

    <fragment
            android:id="@+id/listFragment"
            android:name="com.malugy.activitydemo.ListFragment"
            android:layout_width="100dp"
            android:layout_height="100dp"/>

    <fragment
            android:id="@+id/detailFragment"
            android:name="com.malugy.activitydemo.ListFragment"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_centerInParent="true"/>
</RelativeLayout>
.3-4 动态加载(重点)-慕课网就业班 2019-08-03 12_39(更多 IT 教程 11)
package com.malugy.activitydemo;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FragmentDemoActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_demo);

        // 设置点击事件
        findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 静态加载 fragment
                startActivity(new Intent(FragmentDemoActivity.this, StaticLoadFragmentActivity.class));
            }
        });

        // 1. xml里定义container 2. 创建fragment 3. fragment -> container
        ListFragment fragment = new ListFragment();
        getSupportFragmentManager()
                // 开始将fragment放入container
                .beginTransaction()
                // 关联
                .add(R.id.list_container, fragment)
                // 提交
                .commit();
        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.detail_container, new ListFragment())
                .commit();
        // 移除
        getSupportFragmentManager()
                .beginTransaction()
                .remove(fragment)
                .commit();
        // 替换
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.detail_container, new ListFragment())
                .commit();
    }

}
<!-- activity_fragment_demo.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".FragmentDemoActivity">

    <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:gravity="center"
            android:text="static load fragment"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">

        <LinearLayout
                android:id="@+id/list_container"
                android:layout_width="150dp"
                android:layout_height="match_parent"
                android:layout_margin="1dp"
                android:orientation="horizontal"></LinearLayout>

        <LinearLayout
                android:id="@+id/detail_container"
                android:layout_width="200dp"
                android:layout_height="match_parent"
                android:layout_margin="1dp"
                android:orientation="horizontal"></LinearLayout>
    </LinearLayout>
</LinearLayout>

第 4 章 Fragment 传值(难点)

.4-1 Activityt 向 Fragment 传值(难点)-慕课网就业班 2019-08-03 12_39(更多 IT 教程 1x)
package com.malugy.activitydemo;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FragmentDemoActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_demo);

        // ...

        // 1. xml里定义container 2. 创建fragment 3. fragment -> container
        ListFragment fragment = ListFragment.newInstance("list");
        getSupportFragmentManager()
                // 开始将fragment放入container
                .beginTransaction()
                // 关联
                .add(R.id.list_container, fragment)
                // 提交
                .commit();
        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.detail_container,ListFragment.newInstance("list"))
                .commit();
        // 移除
//        getSupportFragmentManager()
//                .beginTransaction()
//                .remove(fragment)
//                .commit();
        // 替换
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.detail_container, ListFragment.newInstance("detail"))
                .commit();
    }

}
// ListFragment.java
package com.malugy.activitydemo;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

/**
 * 列表fragment
 */
public class ListFragment extends Fragment {
    // 提取成常量,方便复用
    public static final String BUNDLE_TITLE = "bundle_title";
    private String title = "imooc";


    public static ListFragment newInstance(String title) {
        ListFragment fragment = new ListFragment();
        Bundle bundle = new Bundle();
        bundle.putString(BUNDLE_TITLE, title);
        // 传值
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            title = getArguments().getString(BUNDLE_TITLE);
        }
    }

    // ...
}
.4-3 Fragment 向 Activityt 传值(难点)-慕课网就业班 2019-08-03 12_39(更多 IT 教程 11)

package com.malugy.activitydemo;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FragmentDemoActivity extends AppCompatActivity
        implements ListFragment.OnTitleClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_demo);

        // ...

        // 1. xml里定义container 2. 创建fragment 3. fragment -> container
        ListFragment fragment = ListFragment.newInstance("list");
        // 设置点击回调
        fragment.setOnTitleClickListener(this);

        getSupportFragmentManager()
                // 开始将fragment放入container
                .beginTransaction()
                // 关联
                .add(R.id.list_container, fragment)
                 // 提交
                .commit();
        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.detail_container, ListFragment.newInstance("list"))
                .commit();
        // 移除
//        getSupportFragmentManager()
//                .beginTransaction()
//                .remove(fragment)
//                .commit();
        // 替换
        ListFragment fragment1 = ListFragment.newInstance("detail");
        fragment1.setOnTitleClickListener(this);

        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.detail_container, fragment1)
                .commit();
    }

    @Override
    public void onClick(String title) {
        setTitle(title);
    }
}
package com.malugy.activitydemo;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

/**
 * 列表fragment
 */
public class ListFragment extends Fragment {
    // 提取成常量,方便复用
    public static final String BUNDLE_TITLE = "bundle_title";
    private String title = "imooc";

    // ...

    // ! 创建视图
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        // 创建视图
        // 参数1: 布局文件; 参数2: 当前fragment所在的group; 参数3: 是否绑定到当前根布局
        View view = inflater.inflate(R.layout.fragment_list, container, false);

        TextView textView = view.findViewById(R.id.tv);
        textView.setText(title);

        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (onTitleClickListener != null) {
                    onTitleClickListener.onClick(title);
                }
            }
        });
        return view;
    }

    // ...

    public OnTitleClickListener getOnTitleClickListener() {
        return onTitleClickListener;
    }

    // 设置接口的方法
    public void setOnTitleClickListener(OnTitleClickListener onTitleClickListener) {
        this.onTitleClickListener = onTitleClickListener;
    }

    // 定义变量
    OnTitleClickListener onTitleClickListener;

    // 定义接口
    public interface OnTitleClickListener {
        void onClick(String title);
    }
}

fragment 和 fragment 互相传值也是用 callback

06 ViewPager 实现导航效果

第 1 章 学习指南

第 2 章 Support 包简介






第 3 章 ViewPager 的实现(重难点)

.3-1 实现最简单 ViewPager-慕课网就业班 2019-08-03 12_44(更多 IT 教程 11)
package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;

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

public class ImageViewPagerAdapter extends AppCompatActivity {

    private ViewPager viewPager;
    private int[] layoutIds = {
            R.layout.view_first,
            R.layout.view_second,
            R.layout.view_third,
    };
    private List<View> views;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_view_pager_adapter);

        viewPager = (ViewPager) findViewById(R.id.view_pager);
        // 初始化数据
        views = new ArrayList<>();
        for (int i = 0; i < layoutIds.length; i++) {
            View view = getLayoutInflater().inflate(layoutIds[i], null);
            views.add(view);
        }
        // 设置adapter
        viewPager.setAdapter(pagerAdapter);
    }

    PagerAdapter pagerAdapter = new PagerAdapter() {
        @Override
        public int getCount() {
            return layoutIds.length;
        }

        @Override
        public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
            return view == object;
        }

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            // 添加视图
            View child = views.get(position);
            container.addView(child);
            return child;
        }

        @Override
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            container.removeView(views.get(position));
        }
    };
}
<!-- view_first.xml second和third和这个一样,只是text不同 -->
<?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="match_parent"
            android:text="first"
            android:textSize="40sp"/>
</RelativeLayout>
.3-4 使用 ViewPager 实现 APP 引导页-慕课网就业班 2019-08-03 12_44(更多 IT 教程 1x)
package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;

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

public class ImageViewPagerAdapter extends AppCompatActivity {

    public static final int INIT_POSITION = 1;
    private ViewPager viewPager;
    private int[] layoutIds = {
            R.layout.view_first,
            R.layout.view_second,
            R.layout.view_third,
    };
    private List<View> views;

    private ViewGroup dotViewGroup;
    // 下方显示的点
    private List<ImageView> dotViews = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_view_pager_adapter);

        viewPager = findViewById(R.id.view_pager);
        dotViewGroup = findViewById(R.id.dot_layout);
        // 初始化数据
        views = new ArrayList<>();
        for (int i = 0; i < layoutIds.length; i++) {
//            View view = getLayoutInflater().inflate(layoutIds[i], null);
//            views.add(view);

            ImageView imageView = new ImageView(this);
            imageView.setImageResource(R.mipmap.ic_launcher);
            views.add(imageView);

            ImageView dot = new ImageView(this);
            dot.setImageResource(R.mipmap.ic_launcher);
            dot.setMaxWidth(100);
            dot.setMaxHeight(100);

            LinearLayout.LayoutParams layoutParams =
                    new LinearLayout.LayoutParams(40, 40);
            layoutParams.leftMargin = 20;
            dot.setLayoutParams(layoutParams);
            dot.setEnabled(false);

            dotViewGroup.addView(dot);
            dotViews.add(dot);
        }
        // 设置adapter
        viewPager.setAdapter(pagerAdapter);
        // 当划到某个view时,其实前后的view也被加载了,
        // 除了前后两个还有自身的其他view都会被销毁,所以不会内存爆炸
        // 设置两侧各保留多少个view不销毁
        viewPager.setOffscreenPageLimit(4);
        viewPager.setCurrentItem(INIT_POSITION);
        // 因为上面设置current不会触发onPageSelected,所以这里手动更新
        setDotViews(INIT_POSITION);

        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                // page滚动时

            }

            @Override
            public void onPageSelected(int position) {
                // position是滑动结果,也就是新页面的的index
                // 必须切到一个跟上一个不一样的页面才会调用(另两个不需要)
                setDotViews(position);
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                // 滚动->不滚动 or 不滚动->滚动
            }
        });
    }

    private void setDotViews(int position) {
        for (int i = 0; i < dotViews.size(); i++) {
            dotViews.get(i).setImageResource(
                    position == i ? R.mipmap.star : R.mipmap.ic_launcher);
        }
    }

    PagerAdapter pagerAdapter = new PagerAdapter() {
        @Override
        public int getCount() {
            return layoutIds.length;
        }

        @Override
        public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
            return view == object;
        }

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            // 添加视图
            View child = views.get(position);
            container.addView(child);
            return child;
        }

        @Override
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            container.removeView(views.get(position));
        }
    };
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".ImageViewPagerAdapter">

    <androidx.viewpager.widget.ViewPager
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    <LinearLayout
            android:id="@+id/dot_layout"
            android:layout_width="120dp"
            android:layout_height="30dp"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:layout_marginBottom="30dp"
            android:gravity="center"
            android:orientation="horizontal">

    </LinearLayout>
</RelativeLayout>
.3-5 Fragment 配合 ViewPager-慕课网就业班 2019-08-03 12_44(更多 IT 教程 11)
package com.malugy.activitydemo;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class TestFragment extends Fragment {

    public static final String POSITION = "position";
    public static final String TITLE = "title";
    private String position;
    private String title;

    // 不建议直接传递参数给fragment的构造函数
    // 建议使用一个方法接收并使用参数
    public static TestFragment newInstance(int position) {
        TestFragment fragment = new TestFragment();
        Bundle bundle = new Bundle();
        bundle.putInt(POSITION, position);
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            position = String.valueOf(getArguments().getInt(POSITION));
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_test, null);

        TextView textView = view.findViewById(R.id.text_view);
        textView.setText(position);

        return view;
    }
}
<!-- fragment_test.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">

    <TextView
            android:id="@+id/text_view"
            android:layout_width="match_parent"

            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:text="test fragment"
            android:textSize="36sp"/>
</RelativeLayout>
package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;

public class TabViewPagerActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tab_view_pager);

        ViewPager viewPager = findViewById(R.id.view_pager);
        viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
            @NonNull
            @Override
            public Fragment getItem(int position) {
                return TestFragment.newInstance(position);
            }

            @Override
            public int getCount() {
                return 4;
            }
        });
    }
}
<!-- activity_tab_view_pager.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical">

    <androidx.viewpager.widget.ViewPager
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
</LinearLayout>
.3-8 实现底部导航-布局-慕课网就业班 2019-08-03 12_47(更多 IT 教程 11)
package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;

public class BottomTabActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_tab);

        Fragment[] fragments = new Fragment[]{
                TestFragment.newInstance("home"),
                TestFragment.newInstance("message"),
                TestFragment.newInstance("me")
        };

        ViewPager viewPager = findViewById(R.id.view_pager1);
        viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
            @NonNull
            @Override
            public Fragment getItem(int position) {
                return fragments[position];
            }

            @Override
            public int getCount() {
                return fragments.length;
            }
        });
    }
}
<!-- activity_bottom_tab.xml -->
<?xml version="1.0" encoding="utf-8"?>
<TabHost 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:id="@+id/tab_host"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:background="#f8f8f8"
         android:orientation="vertical">

    <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <androidx.viewpager.widget.ViewPager
                android:id="@+id/view_pager1"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_above="@id/tab_divider"/>

        <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_above="@id/tab_divider"
                android:visibility="gone">

        </FrameLayout>

        <View
                android:id="@+id/tab_divider"
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:layout_above="@android:id/tabs"
                android:background="#dfdfdf"/>

        <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:layout_alignParentBottom="true"
                android:showDividers="none">

        </TabWidget>
    </RelativeLayout>
</TabHost>
package com.malugy.activitydemo;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class TestFragment extends Fragment {

    public static final String POSITION = "position";
    public static final String TITLE = "title";
    private String position;
    private String title;
    private static String flag;

    // 不建议直接传递参数给fragment的构造函数
    // 建议使用一个方法接收并使用参数
    public static TestFragment newInstance(int position) {
        TestFragment fragment = new TestFragment();
        Bundle bundle = new Bundle();
        bundle.putInt(POSITION, position);
        fragment.setArguments(bundle);
        flag = "position";
        return fragment;
    }

    public static TestFragment newInstance(String title) {
        TestFragment fragment = new TestFragment();
        Bundle bundle = new Bundle();
        bundle.putString(TITLE, title);
        fragment.setArguments(bundle);
        flag = "title";
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            switch (flag) {
                case "title":
                    title = getArguments().getString(TITLE);
                    break;
                case "position":
                    position = String.valueOf(getArguments().getInt(POSITION));
                    break;
            }
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_test, null);
        TextView textView = view.findViewById(R.id.text_view);

        switch (flag) {
            case "title":
                textView.setText(title);
                break;
            case "position":
                textView.setText(position);
                break;
        }

        return view;
    }
}
<!-- mytab_layout.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:id="@+id/tab_bg"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

    <LinearLayout
            android:id="@+id/main_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:orientation="vertical">

        <ImageView
                android:id="@+id/main_tab_icon"
                android:layout_width="30dp"
                android:layout_height="30dp"
                android:layout_marginTop="4dp"
                android:src="@mipmap/ic_launcher"/>

        <TextView
                android:id="@+id/main_tab_txt"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp"
                android:text="faerfaw"
                android:textColor="@color/color_main_tab_txt"/>
    </LinearLayout>

    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="gone"/>
</RelativeLayout>
<!-- res/color/color_main_tab_txt.xml -->
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 选择后的颜色 -->
    <item android:color="#4dd0c8" android:state_selected="true"/>
    <!-- 按下时的颜色 -->
    <item android:color="#4dd0c8" android:state_pressed="true"/>
    <!-- 默认(触发状态从上到下找,所以默认的放最下面,防止默认的被应用到状态) -->
    <item android:color="#cccccc"/>
</selector>
<!-- res/drawable/main_tab_icon_me.xml -->
<!-- 总共3个这样的,只是图片不同 -->
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 选中的图 -->
    <item android:drawable="@drawable/tabbar_my_pressed" android:state_selected="true"/>
    <!-- 按下的图 -->
    <item android:drawable="@drawable/tabbar_my_pressed" android:state_pressed="true"/>
    <!-- 默认图(触发状态从上到下找,所以默认的放最下面,防止默认的被应用到状态) -->
    <item android:drawable="@drawable/tabbar_my"/>
</selector>
.3-11 实现导航事件联动-慕课网就业班 2019-08-03 12_48(更多 IT 教程 1x)

有很多状态,当触发状态(状态之间也有排序),会从上到下找,如果最上层有一个 item 是默认的,就会使用默认的,而不是我们希望的那个对应状态的
item

-
1. tabhost
-
2. 创建 viewpager
-
3. 处理 tab
-
4. 设置 tab 和视图的对应

07 综合案例:慕淘旅游

第 2 章 闪屏页实现

创建几个包

创建闪屏 activity,作为启动页

package com.malugy.imooc;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;

/**
 * 闪屏页面
 */
public class SplashActivity extends AppCompatActivity {

    private Handler mHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);

        // 2秒后跳转
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                startActivity(new Intent(SplashActivity.this, MainActivity.class));
            }
        }, 2000);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
                android:background="@mipmap/bg_splash"
                tools:context=".SplashActivity">

    <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="80dp"
            android:src="@mipmap/logo"/>
</RelativeLayout>

设置为 NoActionBar,去掉顶部的 tabbar


第 3 章 主页面实现(重难点)

.3-1 主界面实现_功能菜单(重难点)-慕课网就业班 2019-08-03 12_53(更多 IT 教程 1x)

<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">

    <!-- 内容部分 -->
    <RelativeLayout
            android:id="@+id/container_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

    </RelativeLayout>

    <!-- 功能菜单 -->
    <LinearLayout
            android:id="@+id/container_menu"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:background="#ffffff"
            android:orientation="horizontal">

        <LinearLayout
                android:id="@+id/menu_main"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:gravity="center"
                android:orientation="vertical">

            <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@drawable/menu_main_icon_selector"/>

            <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="首页"
                    android:textColor="#000000"/>
        </LinearLayout>

        <LinearLayout
                android:id="@+id/menu_find"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:gravity="center"
                android:orientation="vertical">

            <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@drawable/menu_find_icon_selector"/>

            <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="发现"
                    android:textColor="#000000"/>
        </LinearLayout>

        <LinearLayout
                android:id="@+id/menu_me"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:gravity="center"
                android:orientation="vertical">

            <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@drawable/menu_me_icon_selector"/>

            <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="我的"
                    android:textColor="#000000"/>
        </LinearLayout>
    </LinearLayout>
</RelativeLayout>
<!-- res/drawable/menu_me_icon_selector.xml 其他两个和这个差不多 -->
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@mipmap/nav_me_click" android:state_pressed="true"/>
    <item android:drawable="@mipmap/nav_me_normal"/>
</selector>

.3-2 主界面实现_功能内容(重点)-慕课网就业班 2019-08-03 12_54(更多 IT 教程 1x)

package com.malugy.imooc.fragment;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import com.malugy.imooc.R;

/**
 * 其他2个差不多 me main
 */
public class FindFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        // 对于一个没有被载入或者想要动态载入的界面
        return inflater.inflate(R.layout.fragment_find, container, false);
    }
}
<!-- 其他2个差不多 me main -->
<?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="wrap_content"
            android:layout_height="wrap_content"
            android:text="这是发现页"/>
</RelativeLayout>
package com.malugy.imooc;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;

import com.malugy.imooc.fragment.FindFragment;
import com.malugy.imooc.fragment.MainFragment;
import com.malugy.imooc.fragment.MeFragment;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    protected LinearLayout menuMain;
    protected LinearLayout menuFind;
    protected LinearLayout menuMe;
    // 3个fragment
    protected MainFragment mainFragment = new MainFragment();// 首页
    protected FindFragment findFragment = new FindFragment();// 发现
    protected MeFragment meFragment = new MeFragment();// 个人

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        // 需要将fragment添加到activity_main的relativeLayout里

        // 1. 获取管理类
        this.getSupportFragmentManager()
                .beginTransaction() // 开启事务
                // 2. 事物添加 默认:显示首页;其他页面:隐藏
                .add(R.id.container_content, mainFragment)
                .add(R.id.container_content, findFragment)
                .hide(findFragment) // 隐藏
                .add(R.id.container_content, meFragment)
                .hide(meFragment)
                // 3. 提交
                .commit();
    }

    /*
     * 初始化视图
     */
    public void initView() {
        menuMain = this.findViewById(R.id.menu_main);
        menuFind = this.findViewById(R.id.menu_find);
        menuMe = this.findViewById(R.id.menu_me);

        menuMain.setOnClickListener(this);
        menuFind.setOnClickListener(this);
        menuMe.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.menu_main:
                this.getSupportFragmentManager()
                        .beginTransaction()
                        .show(mainFragment)
                        .hide(findFragment)
                        .hide(meFragment)
                        .commit();
                break;
            case R.id.menu_find:
                this.getSupportFragmentManager()
                        .beginTransaction()
                        .hide(mainFragment)
                        .show(findFragment)
                        .hide(meFragment)
                        .commit();
                break;
            case R.id.menu_me:
                this.getSupportFragmentManager()
                        .beginTransaction()
                        .hide(mainFragment)
                        .hide(findFragment)
                        .show(meFragment)
                        .commit();
                break;
        }
    }
}

第 4 章 我的页面实现

.4-1 我的实现_头部-慕课网就业班 2019-08-03 12_55(更多 IT 教程 1x)
<?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">

    <RelativeLayout
            android:id="@+id/layout_me_header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#008cc9">

        <Button
                android:id="@+id/btn_login"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="10dp"
                android:background="@mipmap/login_btn"/>

        <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@id/btn_login"
                android:layout_marginTop="5dp"
                android:layout_marginBottom="10dp"
                android:orientation="horizontal">

            <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:orientation="vertical">

                <ImageView
                        android:layout_width="24dp"
                        android:layout_height="24dp"
                        android:src="@mipmap/me_menu_sail"/>

                <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="优惠券"/>
            </LinearLayout>

            <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:orientation="vertical">

                <ImageView
                        android:layout_width="24dp"
                        android:layout_height="24dp"
                        android:src="@mipmap/me_menu_yh"/>

                <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="折扣券"/>
            </LinearLayout>

            <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:orientation="vertical">

                <ImageView
                        android:layout_width="24dp"
                        android:layout_height="24dp"
                        android:src="@mipmap/me_menu_go"/>

                <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="购物车"/>
            </LinearLayout>
        </LinearLayout>
    </RelativeLayout>
</LinearLayout>
4-2 我的实现_滚动列表-慕课网就业班 2019-08-03 13_01(更多 IT 教程 1x)

背景是白的

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

    <RelativeLayout
            android:id="@+id/layout_me_header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#008cc9">

        <!-- ... -->
    </RelativeLayout>

    <!-- 滚动列表 -->
    <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

            <!-- item -->
            <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:orientation="horizontal">
                <!-- 左侧图片 -->
                <ImageView
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:layout_gravity="center"
                        android:layout_marginLeft="10dp"
                        android:src="@mipmap/list_my_menu"/>

                <RelativeLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="10dp"
                        android:gravity="center">

                    <TextView
                            android:id="@id/txt_my_menu"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="我的订单"
                            android:textSize="28sp"/>

                    <View
                            android:layout_width="match_parent"
                            android:layout_height="1dp"
                            android:layout_below="@+id/txt_my_menu"
                            android:layout_marginRight="10dp"
                            android:background="#d8dde1"></View>
                </RelativeLayout>
            </LinearLayout>
            <!-- item -->
            <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:orientation="horizontal">
                <!-- 左侧图片 -->
                <ImageView
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:layout_gravity="center"
                        android:layout_marginLeft="10dp"
                        android:src="@mipmap/list_save"/>

                <RelativeLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="10dp"
                        android:gravity="center">

                    <TextView
                            android:id="@id/txt_my_save"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="我的收藏"
                            android:textSize="28sp"/>

                    <View
                            android:layout_width="match_parent"
                            android:layout_height="1dp"
                            android:layout_below="@+id/txt_my_save"
                            android:layout_marginRight="10dp"
                            android:background="#d8dde1"/>
                </RelativeLayout>
            </LinearLayout>
            <!-- item -->
            <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:orientation="horizontal">
                <!-- 左侧图片 -->
                <ImageView
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:layout_gravity="center"
                        android:layout_marginLeft="10dp"
                        android:src="@mipmap/list_pwd"/>

                <RelativeLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="10dp"
                        android:gravity="center">

                    <TextView
                            android:id="@id/txt_my_pwd"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="我的口令"
                            android:textSize="28sp"/>

                    <View
                            android:layout_width="match_parent"
                            android:layout_height="1dp"
                            android:layout_below="@+id/txt_my_pwd"
                            android:layout_marginRight="10dp"
                            android:background="#d8dde1"></View>
                </RelativeLayout>
            </LinearLayout>
            <!-- item -->
            <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:orientation="horizontal">
                <!-- 左侧图片 -->
                <ImageView
                        android:layout_width="48dp"
                        android:layout_height="48dp"
                        android:layout_gravity="center"
                        android:layout_marginLeft="10dp"
                        android:src="@mipmap/list_my"/>

                <RelativeLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="10dp"
                        android:gravity="center">

                    <TextView
                            android:id="@id/txt_my"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="我的锦囊"
                            android:textSize="28sp"/>

                    <View
                            android:layout_width="match_parent"
                            android:layout_height="1dp"
                            android:layout_below="@+id/txt_my"
                            android:layout_marginRight="10dp"
                            android:background="#d8dde1"></View>
                </RelativeLayout>
            </LinearLayout>

            <View
                    android:layout_width="match_parent"
                    android:layout_height="10dp"
                    android:layout_below="@+id/txt_my"
                    android:background="#d8dde1"></View>
        </LinearLayout>
    </ScrollView>
</LinearLayout>
.4-3 我的实现_登录功能-慕课网就业班 2019-08-03 12_57(更多 IT 教程 1x)
package com.malugy.imooc.fragment;

import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import com.malugy.imooc.LoginActivity;
import com.malugy.imooc.R;

public class MeFragment extends Fragment {

    protected Button btnLogin;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_me, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        btnLogin = getView().findViewById(R.id.btn_login);
        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 跳转登录
                startActivity(new Intent(getActivity(), LoginActivity.class));
            }
        });
    }
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:id="@+id/container"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:context=".LoginActivity">
    <!--头部-->
    <RelativeLayout
            android:id="@+id/layout_login_header"
            android:layout_width="match_parent"
            android:layout_height="120dp"
            android:background="#008cc9">


        <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_marginTop="10dp"
                android:layout_marginRight="10dp"
                android:src="@mipmap/login_close"/>

        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_marginLeft="20dp"
                android:layout_marginBottom="20dp"
                android:text="登录"
                android:textColor="@color/white"
                android:textSize="30sp"/>
    </RelativeLayout>

    <!--用户名-->
    <RelativeLayout
            android:id="@+id/layout_login_username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/layout_login_header"
            android:layout_marginHorizontal="20dp"
            android:layout_marginTop="10dp">

        <EditText
                android:id="@+id/edt_username"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@null"
                android:hint="请输入登录用户名/手机号/邮箱"
                android:textSize="24sp"/>

        <View
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:layout_below="@+id/edt_username"
                android:layout_marginBottom="10dp"

                android:background="#d8dde1"/>
    </RelativeLayout>

    <!--密码-->
    <RelativeLayout
            android:id="@+id/layout_login_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/layout_login_username"
            android:layout_marginHorizontal="20dp"
            android:layout_marginTop="10dp">

        <EditText
                android:id="@+id/edt_password"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@null"
                android:hint="请输入登录密码"
                android:textSize="24sp"/>

        <View
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:layout_below="@+id/edt_password"
                android:layout_marginBottom="10dp"

                android:background="#d8dde1"/>
    </RelativeLayout>

    <!--登录按钮-->
    <Button
            android:id="@+id/btn_login"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/layout_login_password"
            android:layout_marginHorizontal="20dp"
            android:layout_marginTop="10dp"
            android:background="#f4f5f7"
            android:text="登录"
            android:textSize="24sp"/>

    <!-- 找回密码 免费注册 -->
    <Button
            android:id="@+id/btn_forget_pwd"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/btn_login"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="10dp"
            android:background="@null"
            android:text="找回密码"
            android:textColor="#2999ce"
            android:textSize="24sp"/>

    <Button
            android:id="@+id/btn_register"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/btn_login"
            android:layout_alignParentRight="true"
            android:layout_marginTop="10dp"
            android:layout_marginRight="20dp"
            android:background="@null"
            android:text="免费注册"
            android:textColor="#2999ce"
            android:textSize="24sp"/>
</RelativeLayout>

第 5 章 首页实现(重点)

.5-1 首页实现_头部搜索框-慕课网就业班 2019-08-03 13_01(更多 IT 教程 1x)
<!-- main_search.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:gravity="center"
              android:orientation="horizontal">

    <!--扫一扫-->
    <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:gravity="center"
            android:orientation="vertical">

        <ImageView
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:src="@mipmap/main_scan"/>

        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="扫一扫"
                android:textColor="#ffffff"
                android:textSize="20sp"/>
    </LinearLayout>

    <!--搜索-->
    <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="48dp"
            android:layout_marginHorizontal="10dp"
            android:layout_weight="1"
            android:background="@mipmap/main_header_sourch"
            android:gravity="center"
            android:orientation="horizontal">

        <ImageView
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:src="@mipmap/search"/>

        <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@null"
                android:hint="目的地/景点/酒店/门票"
                android:textSize="18sp"/>
    </LinearLayout>

    <!--消息-->
    <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:gravity="center"
            android:orientation="vertical">

        <ImageView
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:src="@mipmap/news"/>

        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="消息"
                android:textColor="#ffffff"
                android:textSize="20sp"/>
    </LinearLayout>
</LinearLayout>
<!-- fragment_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="#00ff00">

    <!--引入布局-->
    <include
            android:layout_marginTop="10dp"
            layout="@layout/main_search"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

    </include>
</RelativeLayout>
.5-2 首页实现_头部广告页-慕课网就业班 2019-08-03 13_01(更多 IT 教程 11)
<!-- fragment_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="#00ff00">

    <!--轮播广告-->
    <androidx.viewpager.widget.ViewPager
            android:id="@+id/vpager_main_header"
            android:layout_width="match_parent"
            android:layout_height="120dp">

    </androidx.viewpager.widget.ViewPager>

    <!--引入布局-->
    <include
            layout="@layout/main_search"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"></include>

    <!--图片下面的角标-->
    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="80dp"
            android:src="@mipmap/nav_header_index"/>
</RelativeLayout>
// util/DataUtil
package com.malugy.imooc.util;

import android.content.Context;
import android.widget.ImageView;

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

public class DataUtil {
    /**
     * 顶部广告list
     *
     * @param context
     * @param icons   广告图片的路径src
     * @return
     */
    public static List<ImageView> getHeaderAdInfo(Context context, int icons[]) {
        List<ImageView> datas = new ArrayList<>();
        for (int i = 0; i < icons.length; i++) {
            ImageView icon = new ImageView(context);
            // 占满屏幕
            icon.setScaleType(ImageView.ScaleType.CENTER_CROP);
            icon.setImageResource(icons[i]);
            datas.add(icon);
        }
        return datas;
    }
}
package com.malugy.imooc.fragment;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;

import com.malugy.imooc.R;
import com.malugy.imooc.adapter.MainHeaderAdAdapter;
import com.malugy.imooc.util.DataUtil;

/**
 * 主界面视图
 */
public class MainFragment extends Fragment {

    protected int[] icons = {R.mipmap.header_pic_ad1, R.mipmap.header_pic_ad2};
    protected ViewPager viewPagerHeaderAd;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_main, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // 获得viewpager
        viewPagerHeaderAd = getView().findViewById(R.id.vpager_main_header);
        MainHeaderAdAdapter adAdapter = new MainHeaderAdAdapter(
                getActivity(),
                DataUtil.getHeaderAdInfo(getActivity(), icons)
        );
        // 设置适配器
        viewPagerHeaderAd.setAdapter(adAdapter);
    }
}
package com.malugy.imooc.adapter;


import static android.app.PendingIntent.getActivity;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;

import java.util.List;

public class MainHeaderAdAdapter extends PagerAdapter {

    protected Context context;
    protected List<ImageView> images;

    public MainHeaderAdAdapter(Context context, List<ImageView> images) {
        this.context = context;
        this.images = images;
    }

    @Override
    public int getCount() {
        return null != images ? images.size() : 0;
    }

    /**
     * 初始化视图
     *
     * @param container The containing View in which the page will be shown.
     * @param position  The page position to be instantiated.
     * @return
     */
    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        container.addView(images.get(position));
        return images.get(position);
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return view == object;
    }

    /**
     * 销毁
     *
     * @param container The containing View from which the page will be removed.
     * @param position  The page position to be removed.
     * @param object    The same object that was returned by
     *                  {@link #instantiateItem(View, int)}.
     */
    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        container.removeView(images.get(position));
    }

}
.5-3 首页实现_主菜单实现(难点)-慕课网就业班 2019-08-03 13_11(更多 IT 教程 1x)


package com.malugy.imooc.fragment;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.ViewPager;

import com.malugy.imooc.R;
import com.malugy.imooc.adapter.MainHeaderAdAdapter;
import com.malugy.imooc.adapter.MainMenuAdapter;
import com.malugy.imooc.util.DataUtil;

/**
 * 主界面视图
 */
public class MainFragment extends Fragment {

    protected int[] icons = {R.mipmap.header_pic_ad1, R.mipmap.header_pic_ad2};
    protected ViewPager viewPagerHeaderAd; // 广告

    // 菜单图标
    protected int[] menuIcons = {
            R.mipmap.menu_airport, R.mipmap.menu_hatol, R.mipmap.menu_course,
            R.mipmap.menu_hatol, R.mipmap.menu_nearby, R.mipmap.menu_car,
            R.mipmap.menu_ticket, R.mipmap.menu_train
    };
    String[] menus;
    protected RecyclerView recyclerViewMenu; // 主菜单

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_main, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // 获得viewpager
        viewPagerHeaderAd = getView().findViewById(R.id.vpager_main_header);
        MainHeaderAdAdapter adAdapter = new MainHeaderAdAdapter(
                getActivity(),
                DataUtil.getHeaderAdInfo(getActivity(), icons)
        );
        // 设置适配器
        viewPagerHeaderAd.setAdapter(adAdapter);

        recyclerViewMenu = getView().findViewById(R.id.recycleview_main_menu);
        // recyclerViewMenu 布局样式 参数2: 列数
        recyclerViewMenu.setLayoutManager(new GridLayoutManager(getActivity(), 4));
        menus = this.getActivity().getResources().getStringArray(R.array.main_menu);
        MainMenuAdapter menuAdapter = new MainMenuAdapter(
                getActivity(),
                DataUtil.getMainMenus(menuIcons, menus)
        );
        recyclerViewMenu.setAdapter(menuAdapter);
    }
}
package com.malugy.imooc.util;

import android.content.Context;
import android.widget.ImageView;

import com.malugy.imooc.entity.Menu;

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

public class DataUtil {
    /**
     * 顶部广告list
     *
     * @param context
     * @param icons   广告图片的路径src
     * @return
     */
    public static List<ImageView> getHeaderAdInfo(Context context, int icons[]) {
        // ...
    }

    /**
     * 获取主菜单信息
     *
     * @param icons
     * @param names
     * @return
     */
    public static List<Menu> getMainMenus(int icons[], String names[]) {
        List<Menu> menus = new ArrayList<>();

        for (int i = 0; i < icons.length; i++) {
            Menu menu = new Menu(icons[i], names[i]);
            menus.add(menu);
        }
        return menus;
    }
}
package com.malugy.imooc.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.malugy.imooc.R;
import com.malugy.imooc.entity.Menu;

import java.util.List;

public class MainMenuAdapter extends RecyclerView.Adapter<MainMenuViewHolder> {

    protected Context context;
    protected List<Menu> menus;

    public MainMenuAdapter(Context context, List<Menu> menus) {
        this.context = context;
        this.menus = menus;
    }

    @NonNull
    @Override
    public MainMenuViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new MainMenuViewHolder(
                LayoutInflater.from(context).inflate(R.layout.item_main_menu, null));
    }

    @Override
    public void onBindViewHolder(@NonNull MainMenuViewHolder holder, int position) {
        Menu menu = menus.get(position);
        holder.imageMenuIcon.setImageResource(menu.icon);
        holder.textMenuName.setText(menu.menuName);
    }

    @Override
    public int getItemCount() {
        return null != menus ? menus.size() : 0;
    }
}

class MainMenuViewHolder extends RecyclerView.ViewHolder {

    public ImageView imageMenuIcon;
    public TextView textMenuName;

    public MainMenuViewHolder(View itemView) {
        super(itemView);
        imageMenuIcon = itemView.findViewById(R.id.img_menu_icon);
        textMenuName = itemView.findViewById(R.id.txt_menu_name);
    }
}
<!-- item_main_menu.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:gravity="center"
              android:orientation="vertical">

    <ImageView
            android:id="@+id/img_menu_icon"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:src="@mipmap/menu_airport"/>

    <TextView
            android:id="@+id/txt_menu_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="飞机票"
            android:textColor="#000000"/>
</LinearLayout>
<!-- strings.xml -->
<resources>
    <string-array name="main_menu">
        <item>飞机票</item>
        <item>住酒店</item>
        <item>去旅游</item>
        <item>周边游</item>
        <item>买门票</item>
        <item>火车票</item>
        <item>汽车票</item>
        <item>领里程</item>
    </string-array>
</resources>
<!-- fragment_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">

    <!--轮播广告-->
    <androidx.viewpager.widget.ViewPager
            android:id="@+id/vpager_main_header"
            android:layout_width="match_parent"
            android:layout_height="120dp">

    </androidx.viewpager.widget.ViewPager>

    <!--引入布局-->
    <include
            layout="@layout/main_search"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"></include>

    <!--图片下面的角标-->
    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="80dp"
            android:src="@mipmap/nav_header_index"/>

    <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/vpager_main_header"
            android:layout_marginTop="10dp">

        <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

            <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/recycleview_main_menu"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

            </androidx.recyclerview.widget.RecyclerView>
        </RelativeLayout>

    </ScrollView>
</RelativeLayout>

skip

02 Android 网络操作与流行框架

01 步骤一:网络操作

01 Android 网络操作

第 1 章 课程介绍

第 2 章 网络基础知识

.2-1 http 协议(更多 IT 教程 1x)




.2-2 URL 解析(更多 IT 教程 1x)

第 3 章 网络请求(重点)

.3-1 网络请求方式(更多 IT 教程 1 )




.3-2 代码演示 get 请求(重难点)(更多 IT 教程 1 )

缺少请求权限


不能在主线程进行网络请求

主线程才能更新 UI



package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class NetworkActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView textView;
    private Button button;
    private static final String TAG = "NetworkActivity";
    private String result;
    private Button parseDataButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_network);

        findViews();
        setListeners();
    }

    private void setListeners() {
        button.setOnClickListener(this);
        parseDataButton.setOnClickListener(this);
    }

    private void findViews() {
        textView = findViewById(R.id.res_txt);
        button = findViewById(R.id.get_btn);
        parseDataButton = findViewById(R.id.parse_btn);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.get_btn:
                // 不能在主线程进行网络请求
                // 因为大部分带界面交互的应用,都是通过一个死循环的事件等待用户输入(触屏等)
                // 然后进行即时事件处理,如果这个时候进行耗时操作,就很容易卡死
                // 而网络请求就是耗时操作
                // 当事件处理完成,就通知主进程更新UI
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            URL url = new URL("https://api.wrdan.com/hitokoto");
                            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                            connection.setConnectTimeout(30 * 1000);
                            connection.setRequestMethod("GET");
                            connection.setRequestProperty("Content-Type", "application/json");
                            connection.setRequestProperty("Charset", "UTF-8");
                            connection.setRequestProperty("Accept-Charset", "UTF-8");
                            connection.connect(); // 发起连接

                            int resCode = connection.getResponseCode();
                            String resMsg = connection.getResponseMessage();

                            if (resCode == HttpURLConnection.HTTP_OK) {
                                InputStream inputStream = connection.getInputStream();
                                result = streamToString(inputStream);

                                // 只有主线程可以修改ui
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        textView.setText(result);
                                    }
                                });
                            } else {
                                // todo: error fail
                                Log.e(TAG, "run: error code:" + resCode + ", message:" + resMsg);
                            }
                        } catch (MalformedURLException e) {
                            throw new RuntimeException(e);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }).start();
                break;
            case R.id.parse_btn:

                break;
        }
    }

    /**
     * 将Unicode字符转换为UTF-8类型字符串
     */
    public static String decode(String unicodeStr) {
        if (unicodeStr == null) {
            return null;
        }
        StringBuilder retBuf = new StringBuilder();
        int maxLoop = unicodeStr.length();
        for (int i = 0; i < maxLoop; i++) {
            if (unicodeStr.charAt(i) == '\\') {
                if ((i < maxLoop - 5)
                        && ((unicodeStr.charAt(i + 1) == 'u') || (unicodeStr
                        .charAt(i + 1) == 'U')))
                    try {
                        retBuf.append((char) Integer.parseInt(unicodeStr.substring(i + 2, i + 6), 16));
                        i += 5;
                    } catch (NumberFormatException localNumberFormatException) {
                        retBuf.append(unicodeStr.charAt(i));
                    }
                else {
                    retBuf.append(unicodeStr.charAt(i));
                }
            } else {
                retBuf.append(unicodeStr.charAt(i));
            }
        }
        return retBuf.toString();
    }

    /**
     * 将输入流转换成字符串
     *
     * @param is 从网络获取的输入流
     * @return 字符串
     */
    public String streamToString(InputStream is) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            baos.close();
            is.close();
            byte[] byteArray = baos.toByteArray();
            return new String(byteArray);
        } catch (Exception e) {
            Log.e(TAG, e.toString());
            return null;
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".NetworkActivity">

    <Button
            android:id="@+id/get_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="获取数据"/>

    <Button
            android:id="@+id/parse_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="解析数据"/>

    <TextView
            android:id="@+id/res_txt"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:text="结果"
            android:textSize="18sp"/>
</LinearLayout>
.3-6 9.0 新特性(更多 IT 教程 1 )





<!-- network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true"/>
</network-security-config>
.3-7 post 请求(更多 IT 教程 1 )
    private void requestDataByPost() {
        try {
            URL url = new URL(post_url);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(30 * 1000);
            connection.setRequestMethod("POST");

            connection.setRequestProperty("Content-Type", "application/json");
            connection.setRequestProperty("Charset", "UTF-8");
            connection.setRequestProperty("Accept-Charset", "UTF-8");

            connection.setDoOutput(true);
            connection.setDoInput(true);

            connection.setUseCaches(false);
            connection.connect(); // 发起连接

            String data = "username=" + getEncodeVal("xxx") + "&number=111111111";

            OutputStream outputStream = connection.getOutputStream();
            outputStream.write(data.getBytes());
            outputStream.flush();
            outputStream.close();

            int resCode = connection.getResponseCode();
            String resMsg = connection.getResponseMessage();

            if (resCode == HttpURLConnection.HTTP_OK) {
                InputStream inputStream = connection.getInputStream();
                result = streamToString(inputStream);

                // 只有主线程可以修改ui
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText(result);
                    }
                });
            } else {
                // todo: error fail
                Log.e(TAG, "run: error code:" + resCode + ", message:" + resMsg);
            }
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private String getEncodeVal(String x) {
        try {
            // utf-8编码,防止特殊字符
            return URLEncoder.encode(x, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

第 4 章 数据解析(重点)

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.get_btn:
                // 不能在主线程进行网络请求
                // 因为大部分带界面交互的应用,都是通过一个死循环的事件等待用户输入(触屏等)
                // 然后进行即时事件处理,如果这个时候进行耗时操作,就很容易卡死
                // 而网络请求就是耗时操作
                // 当事件处理完成,就通知主进程更新UI
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        requestDataByGet();
                    }
                }).start();
                break;
            case R.id.parse_btn:
                handleJsonData(result);
                break;
        }
    }

    private void handleJsonData(String result) {
        try {
            JSONObject jsonObject = new JSONObject(result);

            int ret = jsonObject.getInt("ret");
            String text = jsonObject.getString("text");
            String source = jsonObject.getString("source");
            String author = jsonObject.getString("author");

            jsondata jsondata = new jsondata(ret, text, source, author);

            textView.setText(jsondata.toString());
        } catch (Exception e) {

        }
    }
package com.malugy.activitydemo.entity;

/**
 * 对应请求得到的json数据
 */
public class jsondata {
    public jsondata(int ret, String text, String source, String author) {
        this.ret = ret;
        this.text = text;
        this.source = source;
        this.author = author;
    }

    private int ret;
    private String text;
    private String source;
    private String author;

    @Override
    public String toString() {
        return "jsondata{" +
                "ret=" + ret +
                ", text='" + text + '\'' +
                ", source='" + source + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
}

02 Handler 通信

第 1 章 学习指南







第 2 章 Handler 简介

第 3 章 使用 Handler 的实现和优化(重点)

.3-1 代码实现最简单 Handler(重点)(更多 IT 教程 1 )



package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class HandlerActivity extends AppCompatActivity {

    private static final String TAG = "HandlerActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        TextView textView = findViewById(R.id.hi_txt);

        // 这里都是主线程在操作
        // 创建handler
        final Handler handler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                // 处理消息
                Log.i(TAG, "handleMessage: " + msg.what);
                if (msg.what == 1001) {
                    // 这里是主线程,进行消息处理
                    textView.setText("test");
                }
            }
        };

        findViewById(R.id.handle_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                textView.setText("origin");  // ok
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // 也可能做耗时操作,所以开线程
                        try {
                            Thread.sleep(2000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
//                        textView.setText("origin"); // error: 主线程才能更新
                        // 通知UI更新
                        handler.sendEmptyMessage(1001); // 通过handler事件来处理
                    }
                }).start();
            }
        });

        // 没有内容的消息,但是有编号(1001)
//        handler.sendEmptyMessage(1001);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
                android:gravity="center"
                tools:context=".HandlerActivity">

    <TextView
            android:id="@+id/hi_txt"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="hello world"/>

    <Button
            android:id="@+id/handle_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/hi_txt"
            android:text="handle_btn"/>
</RelativeLayout>
3-2 Handler 常见发送消息方法(更多 IT 教程 1 )


package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class HandlerActivity extends AppCompatActivity {

    private static final String TAG = "HandlerActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        TextView textView = findViewById(R.id.hi_txt);

        // 这里都是主线程在操作
        // 创建handler
        final Handler handler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                // 处理消息
                Log.i(TAG, "handleMessage: " + msg.what);
                if (msg.what == 1001) {
                    // 这里是主线程,进行消息处理
                    textView.setText("test");
                    Log.d(TAG,"handleMessage: "+msg.arg1 );
                }
            }
        };

        findViewById(R.id.handle_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                textView.setText("origin");  // ok
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // 也可能做耗时操作,所以开线程
                        try {
                            Thread.sleep(2000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
//                        textView.setText("origin"); // error: 主线程才能更新
                        // 通知UI更新
                        handler.sendEmptyMessage(1001); // 通过handler事件来处理

                        Message message = Message.obtain();
                        message.what = 1002;
                        message.arg1 = 1003;
                        message.arg2 = 1004;
                        message.obj = HandlerActivity.this;

                        handler.sendMessage(message);
                        // 3秒后发送
                        handler.sendMessageAtTime(message, SystemClock.uptimeMillis()+3000);
                        // 延迟两秒发送
                        handler.sendMessageDelayed(message,2000);

                        Runnable r = new Runnable() {
                            @Override
                            public void run() {
                                int a = 1 + 2 + 3;
                            }
                        };
                        handler.post(r);
                        r.run();

                        handler.postDelayed(r,2000);
                    }
                }).start();
            }
        });

        // 没有内容的消息,但是有编号(1001)
//        handler.sendEmptyMessage(1001);
    }
}
.3-5 下载文件并更新进度条(更多 IT 教程 1 )


package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.text.InputType;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class DownloadActivity extends AppCompatActivity {

    public static final int DOWNLOAD_MSG_CODE = 100001;
    private static final int DOWNLOAD_MSG_FAIL_CODE = 100002;
    public static final String DOWNLOAD_URL = "http://120.55.156.38/107904638/1693973615/develope-q8.lanzouc.com";
    private static final String TAG = "DownloadActivity";
    private Handler handler;
    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            "android.permission.READ_EXTERNAL_STORAGE",
            "android.permission.WRITE_EXTERNAL_STORAGE"};


    public static void verifyStoragePermissions(Activity activity) {

        try {
            //检测是否有写的权限
            int permission = ActivityCompat.checkSelfPermission(activity,
                    "android.permission.WRITE_EXTERNAL_STORAGE");
            if (permission != PackageManager.PERMISSION_GRANTED) {
                // 没有写的权限,去申请写的权限,会弹出对话框
                ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download);

        ProgressBar progressBar = findViewById(R.id.progressbar);

        // 主线程 -> 点击按钮 | 发起下载 | 开启子线程下载 | 下载过程中通知主线程 | 主线程更新进度条
        findViewById(R.id.down_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        verifyStoragePermissions(DownloadActivity.this);
                        download(DOWNLOAD_URL);
                    }
                }).start();
            }
        });

        handler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case DOWNLOAD_MSG_CODE:
                        progressBar.setProgress((Integer) msg.obj);
                        break;
                    case DOWNLOAD_MSG_FAIL_CODE:

                        break;
                }
            }
        };
    }

    private void download(String url) {
        try {
            URL download_url = new URL(url);
            URLConnection conn = download_url.openConnection();

            InputStream inputStream = conn.getInputStream();

            // 获取文件的总长度
            int contentLen = conn.getContentLength();

            String downloadFolderName =
//                    Environment.getExternalStorageDirectory()
                    getApplicationContext().getFilesDir().getAbsolutePath()
                            + File.separator + "malred" + File.separator;

            File file = new File(downloadFolderName);
            if (!file.exists()) {
                file.mkdir();
            }

            String filename = downloadFolderName + "hello.zip";

            File tar_file = new File(filename);

            if (tar_file.exists()) {
                tar_file.delete();
            }

            int downloadSize = 0;
            byte[] bytes = new byte[1024];

            int length = 0;

            OutputStream outputStream = new FileOutputStream(tar_file);
            while ((length = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, length);
                downloadSize += length;
                // update UI
                Message message = Message.obtain();
                message.obj = downloadSize * 100 / contentLen;
                message.what = DOWNLOAD_MSG_CODE;
                handler.sendMessage(message);
            }

            inputStream.close();
            outputStream.close();
        } catch (Exception e) {
            Message message = Message.obtain();
            message.what = DOWNLOAD_MSG_FAIL_CODE;
            handler.sendMessage(message);
            throw new RuntimeException(e);
        }
    }
}
.3-6 Handler 实现倒计时并优化内存(更多 IT 教程 1 )

package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

import java.lang.ref.WeakReference;

public class CutdownTime extends AppCompatActivity {

    public static final int COUNTDOWN_TIME_CODE = 100001;
    public static final int DELAY_MILLIS = 1000; // 倒计时间隔
    public static final int MAX_COUNT = 10; // 倒计时起始
    public static final int MIN_COUNT = 0; // 倒计时结束
    private TextView cutdown_time; // 显示文本的view

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cutdown_time);

        cutdown_time = findViewById(R.id.cutdown_txt);

        // handler是异步的,持有activity的引用(就算该activity被销毁),
        // handler不会被GC回收,可能导致内存泄漏
        CutdownTimeHandler handler = new CutdownTimeHandler(this);

        Message msg = Message.obtain();
        msg.what = COUNTDOWN_TIME_CODE;
        msg.arg1 = MAX_COUNT;

        handler.sendMessageDelayed(msg, DELAY_MILLIS);
    }

    // 静态handler
    public static class CutdownTimeHandler extends Handler {
        // 弱引用
        final WeakReference<CutdownTime> weakReference;

        public CutdownTimeHandler(CutdownTime cutdownTime) {
            this.weakReference = new WeakReference<>(cutdownTime);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            CutdownTime activity = weakReference.get();

            switch (msg.what) {
                case COUNTDOWN_TIME_CODE:
                    int val = msg.arg1;

                    // 再次发送msg
                    if (val > MIN_COUNT) {
                        activity.cutdown_time.setText(String.valueOf(val - 1));
                        Message new_msg = Message.obtain();
                        new_msg.what = COUNTDOWN_TIME_CODE;
                        new_msg.arg1 = val - 1;
                        sendMessageDelayed(new_msg, DELAY_MILLIS);
                    }
                    break;
            }
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".CutdownTime">

    <TextView
            android:id="@+id/cutdown_txt"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/maxTime"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

第 4 章 案例:打地鼠

package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.lang.ref.WeakReference;
import java.util.Random;

public class DiglettActivity extends AppCompatActivity implements View.OnClickListener, View.OnTouchListener {

    public static final int CODE = 10086;
    private static int RANDOM_NUM;
    private TextView resTxtView;
    private ImageView diglettImgV;
    private Button startBtn;

    public int width;
    public int height;

//    public int[][] positions = new int[][]{
//            {342, 180}, {432, 880},
//            {521, 256}, {429, 780},
//            {456, 976}, {145, 665},
//            {123, 678}, {564, 567},
//    };

    private int totalCount;
    private int successCount;

    public static final int MAX_COUNT = 10;

    private DiglettHandler handler = new DiglettHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_diglett);

        resTxtView = findViewById(R.id.res_txt);
        diglettImgV = findViewById(R.id.img_view);
        startBtn = findViewById(R.id.start_btn);

        startBtn.setOnClickListener(this);
        diglettImgV.setOnTouchListener(this);

        setTitle("打地鼠");
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.start_btn:
                start();
                break;
        }
    }

    public void start() {
        Display display = getWindowManager().getDefaultDisplay();
        Point point = new Point();
        display.getSize(point);
        width = display.getWidth();
        height = display.getHeight();

        resTxtView.setText("game开始");
        startBtn.setText("游戏中......");
        startBtn.setEnabled(false);

        // 发送消息 handler.sendMessage
        next(0);
    }

    // 随机出现下一个地鼠
    public void next(int delayTime) {

//        int position = new Random().nextInt(positions[0].length);

        Message msg = Message.obtain();
        msg.what = CODE;
//        msg.arg1 = position;
        msg.arg1 = new Random().nextInt(width);
        msg.arg2 = new Random().nextInt(height);

        handler.sendMessageDelayed(msg, delayTime);
        totalCount++;
    }

    public void clear() {
        totalCount = 0;
        successCount = 0;
        diglettImgV.setVisibility(View.GONE);
        startBtn.setText("点击开始");
        startBtn.setEnabled(true);
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        view.setVisibility(View.GONE);
        successCount++;
        resTxtView.setText("打到了" + successCount + "只, 共" + MAX_COUNT + "只.");
        return false;
    }

    public static class DiglettHandler extends Handler {
        public final WeakReference<DiglettActivity> weakReference;

        public DiglettHandler(DiglettActivity activity) {
            weakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            DiglettActivity activity = weakReference.get();

            switch (msg.what) {
                case CODE:
                    if (activity.totalCount > MAX_COUNT) {
                        activity.clear();
                        Toast.makeText(activity, "地鼠打完了!", Toast.LENGTH_LONG).show();
                        return;
                    }

//                    int position = msg.arg1;
                    int positionx = msg.arg1;
                    int positiony = msg.arg2;
                    // 设置地鼠出现位置
//                    activity.diglettImgV.setX(activity.positions[position][0]);
//                    activity.diglettImgV.setY(activity.positions[0][position]);
                    activity.diglettImgV.setX(positionx);
                    activity.diglettImgV.setY(positiony);
                    activity.diglettImgV.setVisibility(View.VISIBLE);

                    // 随机0~500(不包括500)的随机数
                    RANDOM_NUM = 500;
                    int rand_time = new Random().nextInt(RANDOM_NUM) + RANDOM_NUM;

                    activity.next(rand_time);
                    break;
            }
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".DiglettActivity">

    <ImageView
            android:id="@+id/img_view"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:layout_alignParentBottom="true"
            android:src="@drawable/diglett"
            android:visibility="gone"/>

    <TextView
            android:id="@+id/res_txt"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:textAppearance="?android:attr/textAppearanceLarge"/>

    <Button
            android:id="@+id/start_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:text="点击开始"/>
</RelativeLayout>

03 AsyncTask 异步任务

第 1 章 课程介绍



第 2 章 异步任务简介(重点)

2-1 多线程那些事儿(更多 IT 教程 1 )




.2-5 AsyncTask 常用方法详解(重难点)(更多 IT 教程 1 )

package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;

public class AsyncTaskActivity extends AppCompatActivity {
    private static final String TAG = "AsyncTaskActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_task);

        new DownloadAsyncTask().execute("i feel so gooooooooood!!!!");
    }

    /* 泛型里填包装类型 */
    public class DownloadAsyncTask extends AsyncTask<String, Integer, Boolean> {

        /**
         * (主线程)在异步任务前执行
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // 可操作ui
        }

        /**
         * (子进程)在另一个线程中处理事件
         *
         * @param strings 入参
         * @return 结果
         */
        @Override
        protected Boolean doInBackground(String... strings) {
            for (int i = 0; i < 100; i++) {
                Log.i(TAG, "doInBackground:  " + strings[0]);
                // 抛出进度
                publishProgress(i);
            }
            return true;
        }

        /**
         * (主线程)异步任务执行结束后
         *
         * @param aBoolean
         */
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            // 可以进行结果处理
        }

        /**
         * (主进程)当进度改变
         *
         * @param values
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            // 收到进度,然后处理
        }

        // 下面两个用的不多
        @Override
        protected void onCancelled(Boolean aBoolean) {
            super.onCancelled(aBoolean);
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            //
        }
    }
}

第 3 章 异步的实现(重点)

.3-1 下载之前的准备工作(更多 IT 教程 1 )


sdk22 及以下申请方法


动态申请是自己写代码实现的

package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

public class AsyncDownloadActivity extends AppCompatActivity {

    private static final String TAG ="AsyncDownloadActivity" ;
    private ProgressBar probar;
    private Button btn;
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_download);

        initView();
        setListener();
        // 初始化UI数据
        setData();
    }

    private void setData() {
        probar.setProgress(0);
        btn.setText("点击下载");
        tv.setText("准备下载");
    }

    private void setListener() {
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 下载

            }
        });
    }

    private void initView() {
        probar = findViewById(R.id.progressBar2);
        btn = findViewById(R.id.btn);
        tv = findViewById(R.id.down_txt);
    }

}
.3-2 使用 AsyncTask 实现异步下载(重难点)(更多 IT 教程 1 )
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class AsyncDownloadActivity extends AppCompatActivity {

    private static final String TAG = "AsyncDownloadActivity";
    private ProgressBar probar;
    private Button btn;
    private TextView tv;
    private static String down_url =
            "https://downloadr2.apkmirror.com/wp-content/uploads/2023/09/78/64f9c14aac22b/com.gamestar.perfectpiano_7.7.5-2100988_3arch_2dpi_14lang_2e877540aa2d3bb70bcbbc1e76f3aa1a_apkmirror.com.apkm?verify=1694090949-PYTtgkS5Q3G2esvOQBo6tRCDfxkb8DNH-LONy_shqNk";
    private String filePath;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_download);

        initView();
        setListener();
        // 初始化UI数据
        setData();
    }

    private void setData() {
        probar.setProgress(0);
        btn.setText("点击下载");
        tv.setText("准备下载");
    }

    private void setListener() {
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 下载
                DownloadAsyncTask task = new DownloadAsyncTask();
                task.execute(down_url);
            }
        });
    }

    private void initView() {
        probar = findViewById(R.id.progressBar2);
        btn = findViewById(R.id.btn);
        tv = findViewById(R.id.down_txt);
    }


    /* 泛型里填包装类型 */
    public class DownloadAsyncTask extends AsyncTask<String, Integer, Boolean> {

        /**
         * (主线程)在异步任务前执行
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // 可操作ui
            btn.setText("下载中");
            tv.setText("下载中");
            probar.setProgress(0);
        }

        /**
         * (子进程)在另一个线程中处理事件
         *
         * @param strings 入参
         * @return 结果
         */
        @Override
        protected Boolean doInBackground(String... strings) {
            if (null != strings && strings.length > 0) {
                String apkUrl = strings[0];

                try {
                    URL url = new URL(apkUrl);
                    // 构造链接并打开
                    URLConnection conn = url.openConnection();
                    InputStream input = conn.getInputStream();
                    // 获得下载内容的总长度
                    int contentLen = conn.getContentLength();

                    // 下载地址准备
//                    filePath = Environment.getExternalStorageDirectory() + File.separator + "imooc.apk";
                    // android 10(Q)开始增加了沙盒机制,不能直接把文件保存到/sdcard目录下,
                    // 只能保存到APP专属目录下;AndroidManifest.xml在标签下增加属性
                    // 【android:requestLegacyExternalStorage=“true”】可以暂时保存到/sdcard路径下,
                    // 但是Android11开始就失效了
                    //
                    //我们可以通过Context的getExternalFilesDir(null)方法获取APP专属目录
                    filePath = getExternalFilesDir(null) + File.separator + "imooc.apk";
                    // 下载地址处理
                    File apkFile = new File(filePath);
                    if (apkFile.exists()) {
                        boolean res = apkFile.delete();
                        if (!res) {
                            return false;
                        }
                    }

                    // 已下载的大小
                    int downloadSize = 0;

                    byte[] bytes = new byte[1024];
                    int len;

                    // 创建输入管道
                    OutputStream output = new FileOutputStream(filePath);
                    // 一直下载
                    while ((len = input.read(bytes)) != -1) {
                        output.write(bytes, 0, len);
                        downloadSize += len;

                        // 用long是因为数值太大int表示不了,甚至出现负数)
                        publishProgress((int) (Long.valueOf(downloadSize) * 100 /Long.valueOf( contentLen)));
                        Log.i(TAG, "len: " + len);
                        Log.i(TAG, "downloadSize: " + downloadSize);
                        Log.i(TAG, "contentLen: " + contentLen);
                    }

                    input.close();
                    output.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            } else {
                return false;
            }

            return true;
        }

        /**
         * (主线程)异步任务执行结束后
         *
         * @param aBoolean
         */
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            // 可以进行结果处理
            btn.setText(aBoolean ? "下载完成" : "下载失败");
            tv.setText(aBoolean ? "下载完成" + filePath : "下载失败");
        }

        /**
         * (主进程)当进度改变
         *
         * @param values
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            // 收到进度,然后处理
            if (values != null && values.length > 0) {
                Log.i(TAG, "onProgressUpdate: true");
                probar.setProgress(values[0]);
                Log.i(TAG, "values: " + values[0]);
            }
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".AsyncDownloadActivity">

    <ProgressBar
            android:id="@+id/progressBar2"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="15dp"/>

    <Button
            android:id="@+id/btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="点击下载"/>

    <TextView
            android:id="@+id/down_txt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="hello!"/>
</LinearLayout>
.3-4 自定义下载的封装思想(抛砖引玉,之后的学习方向)(更多 IT 教程 1 )
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.malugy.activitydemo.utils.DownloadHelper;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class AsyncDownloadActivity extends AppCompatActivity {

    private static final String TAG = "AsyncDownloadActivity";
    private ProgressBar probar;
    private Button btn;
    private TextView tv;
    private static String down_url =
            "https://downloadr2.apkmirror.com/wp-content/uploads/2023/09/78/64f9c14aac22b/com.gamestar.perfectpiano_7.7.5-2100988_3arch_2dpi_14lang_2e877540aa2d3bb70bcbbc1e76f3aa1a_apkmirror.com.apkm?verify=1694090949-PYTtgkS5Q3G2esvOQBo6tRCDfxkb8DNH-LONy_shqNk";
    private String filePath;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_download);

        initView();
        setListener();
        // 初始化UI数据
        setData();

    }

    private void setData() {
        probar.setProgress(0);
        btn.setText("点击下载");
        tv.setText("准备下载");
    }

    private void setListener() {
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 下载
                DownloadHelper.download(down_url, "", new DownloadHelper.OnDownloadListener.SimpleDownloadListener() {
                    @Override
                    public void onSuccess(int code, File f) {

                    }

                    @Override
                    public void onFail(int code, File f, String msg) {

                    }
                });
            }
        });
    }

    private void initView() {
        probar = findViewById(R.id.progressBar2);
        btn = findViewById(R.id.btn);
        tv = findViewById(R.id.down_txt);
    }
}
package com.malugy.activitydemo.utils;

import android.os.AsyncTask;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;

/**
 * 1. download方法 url localPath listener
 * 2. listener: start, success fail progress
 * 3. 用async task封装
 */
public class DownloadHelper {
    public static void download(String url, String localPath, OnDownloadListener listener) {
        DownloadAsyncTask task = new DownloadAsyncTask(url, localPath, listener);
        task.execute();
    }

    public interface OnDownloadListener {
        void onStart();

        void onSuccess(int code, File f);

        void onFail(int code, File f, String msg);

        void onProgress(int progress);

        // 用抽象类实现接口,让实现类可以不实现不需要的方法
        abstract class SimpleDownloadListener implements OnDownloadListener {
            @Override
            public void onStart() {

            }

            @Override
            public void onProgress(int progress) {

            }
        }
    }

    public static class DownloadAsyncTask extends AsyncTask<String, Integer, Boolean> {
        String url;
        String filePath;
        OnDownloadListener listener;

        public DownloadAsyncTask(String url, String filePath, OnDownloadListener listener) {
            this.url = url;
            this.filePath = filePath;
            this.listener = listener;
        }

        /**
         * (主线程)在异步任务前执行
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // 可操作ui
            if (listener != null) {
                listener.onStart();
            }
        }

        /**
         * (子进程)在另一个线程中处理事件
         *
         * @param strings 入参
         * @return 结果
         */
        @Override
        protected Boolean doInBackground(String... strings) {
            String apkUrl = url;

            try {
                URL url = new URL(apkUrl);
                // 构造链接并打开
                URLConnection conn = url.openConnection();
                InputStream input = conn.getInputStream();
                // 获得下载内容的总长度
                int contentLen = conn.getContentLength();

                // 下载地址处理
                File apkFile = new File(filePath);
                if (apkFile.exists()) {
                    boolean res = apkFile.delete();
                    if (!res) {
                        if (listener != null) {
                            listener.onFail(-1, apkFile, "文件删除失败");
                        }
                        return false;
                    }
                }

                // 已下载的大小
                int downloadSize = 0;

                byte[] bytes = new byte[1024];
                int len;

                // 创建输入管道
                OutputStream output = new FileOutputStream(filePath);
                // 一直下载
                while ((len = input.read(bytes)) != -1) {
                    output.write(bytes, 0, len);
                    downloadSize += len;

                    // 用long是因为数值太大int表示不了,甚至出现负数)
                    publishProgress((int) (Long.valueOf(downloadSize) * 100 / Long.valueOf(contentLen)));
                }

                input.close();
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
                if (listener != null) {
                    listener.onFail(-2, new File(filePath), e.getMessage());
                }
                return false;
            }

            if (listener != null) {
                listener.onSuccess(0, new File(filePath));
            }
            return true;
        }

        /**
         * (主线程)异步任务执行结束后
         *
         * @param aBoolean
         */
        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            // 可以进行结果处理
        }

        /**
         * (主进程)当进度改变
         *
         * @param values
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            // 收到进度,然后处理
            if (listener != null) {
                listener.onProgress(values[0]);
            }
        }
    }
}
.3-5 自由编程(更多 IT 教程 1 )

第 4 章 总结及扩展



02 步骤二:高级控件

01 ListView 展示列表数据

第 1 章 课程介绍


第 2 章 ListView 准备工作

.2-1 ListView 简介-慕课网就业班 2019-07-24 10_58(更多 IT 教程 1 )



<!-- item_app_list_view.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="horizontal">

    <ImageView
            android:id="@+id/icon_img_view"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:src="@mipmap/ic_launcher"/>

    <TextView
            android:id="@+id/app_txt_v"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:gravity="center_vertical"
            android:paddingLeft="6dp"
            android:text="@string/app_name"
            android:textSize="20sp"/>

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".ListViewActivity">

    <ListView
            android:id="@+id/app_list_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

    </ListView>
</LinearLayout>

第 3 章 ListView 简单应用

.3-1 Adapter 的数据绑定(重点)-慕课网就业班 2019-07-24 11_02(更多 IT 教程 1 )
package com.malugy.activitydemo;

import androidx.annotation.ContentView;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

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

public class ListViewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_view);

        ListView appListView = findViewById(R.id.app_list_view);

        // 数据
        List<String> appNames = new ArrayList<>();
        appNames.add("qq");
        appNames.add("wx");
        appNames.add("mooc");
    }

    public class AppListAdapter extends BaseAdapter {
        // 要填充的数据
        List<String> data;

        public AppListAdapter(List<String> data) {
            this.data = data;
        }

        @Override
        public int getCount() {
            // 有多少条数据
            return data.size();
        }

        @Override
        public Object getItem(int i) {
            // 当前位置这条
            return data.get(i);
        }

        @Override
        public long getItemId(int i) {
            // 当前位置这条的id
            return i;
        }

        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
            // 处理 view--data 填充数据的过程

            LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            // 得到view
            view = layoutInflater.inflate(R.layout.item_app_list_view, null);

            ImageView imgView = view.findViewById(R.id.icon_img_view);
            TextView tv = view.findViewById(R.id.app_txt_v);

            tv.setText(data.get(i));

            return view;
        }
    }
}
.3-3 最简单 ListView 效果演示-慕课网就业班 2019-07-24 11_02(更多 IT 教程 1 )
package com.malugy.activitydemo;

import androidx.annotation.ContentView;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

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

public class ListViewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_view);

        ListView appListView = findViewById(R.id.app_list_view);

        // 数据
        List<String> appNames = new ArrayList<>();
        appNames.add("qq");
        appNames.add("wx");
        appNames.add("mooc");

        appListView.setAdapter(new AppListAdapter(appNames));
    }

    public class AppListAdapter extends BaseAdapter {
        // ...
    }
}

.3-5 获取系统已安装应用列表(选修)-慕课网就业班 2019-07-24 11_02(更多 IT 教程 1 )
package com.malugy.activitydemo;

import androidx.annotation.ContentView;
import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

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

public class ListViewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_view);

        ListView appListView = findViewById(R.id.app_list_view);

        // 数据
//        List<String> appNames = new ArrayList<>();
//        appNames.add("qq");
//        appNames.add("wx");
//        appNames.add("mooc");

        List<ResolveInfo> appInfos = getAppInfos();
        appListView.setAdapter(new AppListAdapter(appInfos));

        // 添加头部view
        LayoutInflater inflater= (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View headerV=inflater.inflate(R.layout.header_list_view,null);
        appListView.addHeaderView(headerV);

        // 每个item的点击事件
        appListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                String packageName = appInfos.get(i).activityInfo.packageName;
                String className = appInfos.get(i).activityInfo.name;

                ComponentName componentName = new ComponentName(packageName, className);
                final Intent intent = new Intent();
                intent.setComponent(componentName);
                startActivity(intent);
            }
        });
    }

    // 获取系统已安装的应用列表
    private List<ResolveInfo> getAppInfos() {
        Intent intent = new Intent(Intent.ACTION_MAIN, null);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        return getPackageManager().queryIntentActivities(intent, 0);
    }

    public class AppListAdapter extends BaseAdapter {
        // 要填充的数据
//        List<String> data;
        List<ResolveInfo> data;

//        public AppListAdapter(List<String> data) {
//            this.data = data;
//        }

        public AppListAdapter(List<ResolveInfo> data) {
            this.data = data;
        }

        @Override
        public int getCount() {
            // 有多少条数据
            return data.size();
        }

        @Override
        public Object getItem(int i) {
            // 当前位置这条
            return data.get(i);
        }

        @Override
        public long getItemId(int i) {
            // 当前位置这条的id
            return i;
        }

        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
            // 处理 view--data 填充数据的过程

            LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            // 得到view
            view = layoutInflater.inflate(R.layout.item_app_list_view, null);

            ImageView imgView = view.findViewById(R.id.icon_img_view);
            TextView tv = view.findViewById(R.id.app_txt_v);

            tv.setText(data.get(i).activityInfo.loadLabel(getPackageManager()));
            imgView.setImageDrawable(data.get(i).activityInfo.loadIcon(getPackageManager()));

            // 点击跳转应用
//            view.setOnClickListener(new View.OnClickListener() {
//                @Override
//                public void onClick(View view) {
            // 包名
//                    String packageName = data.get(i).activityInfo.packageName;
//                    String className = data.get(i).activityInfo.name;
//
//                    ComponentName componentName = new ComponentName(packageName, className);
//                    final Intent intent = new Intent();
//                    intent.setComponent(componentName);
//                    startActivity(intent);
//                }
//            });
            return view;
        }
    }
}
<!-- header_list_view.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="60dp"
              android:orientation="vertical">

    <ImageView
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:scaleType="fitXY"
            android:src="@mipmap/banner"/>

</LinearLayout>
<!-- activity_list_view.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".ListViewActivity">

    <ListView
            android:id="@+id/app_list_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

    </ListView>
</LinearLayout>
<!-- item_app_list_view.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="horizontal">

    <ImageView
            android:id="@+id/icon_img_view"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:src="@mipmap/ic_launcher"/>

    <TextView
            android:id="@+id/app_txt_v"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:gravity="center_vertical"
            android:paddingLeft="6dp"
            android:text="@string/app_name"
            android:textSize="20sp"/>

</LinearLayout>
.3-6 优化列表-慕课网就业班 2019-07-24 11_02(更多 IT 教程 1 )
package com.malugy.activitydemo;

import androidx.annotation.ContentView;
import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

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

public class ListViewActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
    }

    // 获取系统已安装的应用列表
    private List<ResolveInfo> getAppInfos() {
        // ...
    }

    public class AppListAdapter extends BaseAdapter {
        // ...

        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
            // 处理 view--data 填充数据的过程

            ViewHolder viewHolder = new ViewHolder();
            // 优化: 之前每次都要重新创建item,现在可以保存数据到holder,复用
            if (view == null) {
                LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                // 得到view
                view = layoutInflater.inflate(R.layout.item_app_list_view, null);

                viewHolder.iconView = view.findViewById(R.id.icon_img_view);
                viewHolder.appNameV = view.findViewById(R.id.app_txt_v);

                // 将该view和holder建立对应关系
                // 保存到tag
                view.setTag(viewHolder);
            } else {
                // 从view中取出
                viewHolder = (ViewHolder) view.getTag();
            }

            viewHolder.appNameV.setText(data.get(i).activityInfo.loadLabel(getPackageManager()));
            viewHolder.iconView.setImageDrawable(data.get(i).activityInfo.loadIcon(getPackageManager()));

            // 点击跳转应用
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // 包名
                    String packageName = data.get(i).activityInfo.packageName;
                    String className = data.get(i).activityInfo.name;

                    ComponentName componentName = new ComponentName(packageName, className);
                    final Intent intent = new Intent();
                    intent.setComponent(componentName);
                    startActivity(intent);
                }
            });
            return view;
        }
    }

    public class ViewHolder {
        public ImageView iconView;
        public TextView appNameV;
    }
}

02 ListView 实现隔行效果

第 1 章 课程介绍

第 2 章 网络下载数据并显示(必备技能)

.2-1 使用异步访问网络(更多 IT 教程 1 )
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ListView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class RequestDataActivity extends AppCompatActivity {
    private String req_url = "https://www.imooc.com/api/teacher?type=2&page=1";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_request_data);

        ListView listView = this.findViewById(R.id.main_list_view);

        // data item.view
        listView.setAdapter();
    }

    public class RequestDataAsyncTask extends AsyncTask<Void, Void, String> {

        private String request(String reqUrl) {
            try {
                URL url = new URL(reqUrl);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();

                conn.setConnectTimeout(30000);
                conn.setRequestMethod("GET");
                conn.connect();

                int rescode = conn.getResponseCode();

                if (rescode != HttpURLConnection.HTTP_OK) {
                    InputStreamReader input_reader = new InputStreamReader(conn.getInputStream());
                    BufferedReader buf_reader = new BufferedReader(input_reader);
                    StringBuilder str_builder = new StringBuilder();

                    String line;

                    while ((line = buf_reader.readLine()) != null) {
                        str_builder.append(line);
                    }
                    return str_builder.toString();
                }
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            return null;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // loading

        }

        @Override
        protected String doInBackground(Void... voids) {
            return request(req_url);
        }


        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            // loading消失, 数据处理 刷新界面

        }
    }

}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".RequestDataActivity">

    <ListView
            android:id="@+id/main_list_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
</RelativeLayout>
.2-2 解析获取的 Json 数据(更多 IT 教程 1 )
        @Override
        protected void onPostExecute(String res) {
            super.onPostExecute(res);
            // loading消失, 数据处理 刷新界面
            LessonResult lessonResult = new LessonResult();

            try {
                JSONObject jsonObject = new JSONObject(res);

                final int status = jsonObject.getInt(STATUS);
                lessonResult.setStatus(status);
                final String msg = jsonObject.getString(MSG)
                lessonResult.setMsg(msg);

                List<LessonInfo> lessonInfos = new ArrayList<>();
                JSONArray dataArr = jsonObject.getJSONArray(DATA);

                for (int i = 0; i < dataArr.length(); i++) {
                    LessonInfo info = new LessonInfo();
                    JSONObject temJsonObj = (JSONObject) dataArr.get(i);
                    info.setName(temJsonObj.getString(NAME));
                    lessonInfos.add(info);
                }
                lessonResult.setLessonInfoList(lessonInfos);
                // GSON | fastjson
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
        }
package com.malugy.model;

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

public class LessonResult {
    private String msg;
    private int status;
    private List<LessonInfo> lessonInfoList = new ArrayList<>();

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public List<LessonInfo> getLessonInfoList() {
        return lessonInfoList;
    }

    public void setLessonInfoList(List<LessonInfo> lessonInfoList) {
        this.lessonInfoList = lessonInfoList;
    }
}
package com.malugy.model;

public class LessonInfo {
    String name;
    String description;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}
.2-4 加载数据到 ListView(更多 IT 教程 1 )
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ListView;

import com.malugy.adapter.RequestDataAdapter;
import com.malugy.model.LessonInfo;
import com.malugy.model.LessonResult;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class RequestDataActivity extends AppCompatActivity {
    public static final String STATUS = "status";
    public static final String MSG = "msg";
    public static final String DATA = "data";
    public static final String NAME = "name";
    private String req_url = "http://192.168.255.137:8888/teacher";
    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_request_data);

        listView = this.findViewById(R.id.main_list_view);

        // data item.view
        new RequestDataAsyncTask().execute();

        // 添加底部view
        LayoutInflater inflater =
                (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View headerV = inflater.inflate(R.layout.header_list_view, null);
        listView.addFooterView(headerV);
    }

    public class RequestDataAsyncTask extends AsyncTask<Void, Void, String> {

        private static final String TAG = "RequestDataAsyncTask";

        private String request(String reqUrl) {
            try {
                URL url = new URL(reqUrl);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();

                conn.setConnectTimeout(30000);
                conn.setRequestMethod("GET");
                conn.connect();

                int rescode = conn.getResponseCode();

                if (rescode == HttpURLConnection.HTTP_OK) {
                    InputStreamReader input_reader = new InputStreamReader(conn.getInputStream());
                    BufferedReader buf_reader = new BufferedReader(input_reader);
                    StringBuilder str_builder = new StringBuilder();

                    String line;

                    while ((line = buf_reader.readLine()) != null) {
                        str_builder.append(line);
                        Log.i(TAG, "request: "+line);
                    }
                    Log.i(TAG, "request: 111");
                    return str_builder.toString();
                }
            } catch (MalformedURLException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            return "";
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // loading

        }

        @Override
        protected String doInBackground(Void... voids) {
            return request(req_url);
        }


        @Override
        protected void onPostExecute(String res) {
            super.onPostExecute(res);
            Log.i(TAG, "onPostExecute: " + res);
            // loading消失, 数据处理 刷新界面
            LessonResult lessonResult = new LessonResult();

            try {
                JSONObject jsonObject = new JSONObject(res);

                final int status = jsonObject.getInt(STATUS);
                lessonResult.setStatus(status);
                final String msg = jsonObject.getString(MSG);
                lessonResult.setMsg(msg);

                List<LessonInfo> lessonInfos = new ArrayList<>();
                JSONArray dataArr = jsonObject.getJSONArray(DATA);

                for (int i = 0; i < dataArr.length(); i++) {
                    LessonInfo info = new LessonInfo();
                    JSONObject temJsonObj = (JSONObject) dataArr.get(i);
                    info.setName(temJsonObj.getString(NAME));
                    lessonInfos.add(info);
                }
                lessonResult.setLessonInfoList(lessonInfos);
                // GSON | fastjson
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }

            listView.setAdapter(new RequestDataAdapter(
                    lessonResult.getLessonInfoList(), RequestDataActivity.this));
        }
    }

}
package com.malugy.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.malugy.activitydemo.R;
import com.malugy.model.LessonInfo;

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

public class RequestDataAdapter extends BaseAdapter {

    List<LessonInfo> lessonInfos = new ArrayList<>();
    Context context;

    public RequestDataAdapter(List<LessonInfo> lessonInfos, Context context) {
        this.lessonInfos = lessonInfos;
        this.context = context;
    }

    @Override
    public int getCount() {
        return lessonInfos.size();
    }

    @Override
    public Object getItem(int i) {
        return lessonInfos.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder viewHolder = new ViewHolder();

        if (view == null) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.item_app_list_view, null);

            viewHolder.iconView = view.findViewById(R.id.icon_img_view);
            viewHolder.appNameV = view.findViewById(R.id.app_txt_v);

            view.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) view.getTag();
        }

        viewHolder.appNameV.setText(lessonInfos.get(i).getName());
        viewHolder.iconView.setVisibility(View.GONE);

        return view;
    }


    public class ViewHolder {
        public ImageView iconView;
        public TextView appNameV;
    }
}

第 3 章 不同 item 的引用(必杀技)

package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

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

public class ChatActivity extends AppCompatActivity {

    private static ViewHolder viewHolder;
    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);

        listView = findViewById(R.id.chat_list_view);

        // 准备数据
        ArrayList<ChatMessage> chatMessages = new ArrayList<>();
        chatMessages.add(new ChatMessage(
                1, 2, "刘小明", "8:20", "你好", true));
        chatMessages.add(new ChatMessage(
                2, 1, "小军", "8:21", "我好", false));
        chatMessages.add(new ChatMessage(
                1, 2, "刘小明", "8:22", "今天天气怎么样", true));
        chatMessages.add(new ChatMessage(
                2, 1, "小军", "8:23", "热死啦", false));

        listView.setAdapter(new ChatMessageAdapter(chatMessages, this));
    }

    public class ChatMessage {
        private int id;
        private int friend_id;
        private String name;
        private String date;
        private String content;
        private boolean isComeMsg; // 是不是对方发来的消息

        public ChatMessage(int id, int friend_id, String name, String date, String content, boolean isComeMsg) {
            this.id = id;
            this.friend_id = friend_id;
            this.name = name;
            this.date = date;
            this.content = content;
            this.isComeMsg = isComeMsg;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public int getFriend_id() {
            return friend_id;
        }

        public void setFriend_id(int friend_id) {
            this.friend_id = friend_id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getDate() {
            return date;
        }

        public void setDate(String date) {
            this.date = date;
        }

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }

        public boolean isComeMsg() {
            return isComeMsg;
        }

        public void setComeMsg(boolean comeMsg) {
            isComeMsg = comeMsg;
        }
    }

    public static class ChatMessageAdapter extends BaseAdapter {

        List<ChatMessage> chatMessages = new ArrayList<>();
        private Context context;

        interface MessageViewType {
            int COM_MSG = 0;
            int TO_MSG = 1;
        }

        public ChatMessageAdapter(List<ChatMessage> chatMessages, Context context) {
            this.chatMessages = chatMessages;
            this.context = context;
        }

        @Override
        public int getCount() {
            return chatMessages.size();
        }

        @Override
        public Object getItem(int i) {
            return chatMessages.get(i);
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public int getItemViewType(int position) {
            // 返回该view的type
            ChatMessage chatMessage = chatMessages.get(position);
            return chatMessage.isComeMsg() ? MessageViewType.COM_MSG : MessageViewType.TO_MSG;
        }

        // 有几种不同的视图
        @Override
        public int getViewTypeCount() {
            return 2;
        }

        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
            ChatMessage chatMessage = chatMessages.get(i);

            if (view == null) {
                if (chatMessage.isComeMsg()) {
                    view = LayoutInflater.from(context)
                            .inflate(R.layout.chat_left, null);
                } else {
                    view = LayoutInflater.from(context)
                            .inflate(R.layout.chat_right, null);
                }
                viewHolder = new ViewHolder();
                viewHolder.chat_content = view.findViewById(R.id.chat_content);
                viewHolder.chat_date = view.findViewById(R.id.chat_date);
                viewHolder.chat_user = view.findViewById(R.id.chat_name);

                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) view.getTag();
            }

            viewHolder.chat_date.setText(chatMessage.date);
            viewHolder.chat_content.setText(chatMessage.content);
            viewHolder.chat_user.setText(chatMessage.name);

            return view;
        }
    }

    public static class ViewHolder {
        private TextView chat_date;
        private TextView chat_content;
        private TextView chat_user;
    }
}
<!-- activity_chat.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".ChatActivity">

    <ListView
            android:id="@+id/chat_list_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    </ListView>
</LinearLayout>
<!-- chat_left.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical">

    <TextView
            android:id="@+id/chat_date"
            android:layout_width="match_parent"
            android:layout_height="20dp"
            android:gravity="center"
            android:text="11:00"
            android:textSize="16sp"/>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">

        <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="100dp"
                android:orientation="vertical"

        >

            <ImageView
                    android:id="@+id/chat_photo"
                    android:layout_width="70dp"
                    android:layout_height="70dp"
                    android:src="@mipmap/ic_launcher"/>

            <TextView
                    android:id="@+id/chat_name"
                    android:layout_width="70dp"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:text="TextView"/>

        </LinearLayout>

        <TextView
                android:id="@+id/chat_content"
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:layout_marginRight="50dp"
                android:gravity="center"
                android:text="TextView"
                android:textSize="20sp"/>
    </LinearLayout>
</LinearLayout>
<!-- chat_right.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical">

    <TextView
            android:id="@+id/chat_date"
            android:layout_width="match_parent"
            android:layout_height="20dp"
            android:gravity="center"
            android:text="11:00"
            android:textSize="16sp"/>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">


        <TextView
                android:id="@+id/chat_content"
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:layout_marginLeft="50dp"
                android:layout_weight="1"
                android:gravity="center"
                android:text="TextView"
                android:textSize="20sp"/>

        <LinearLayout
                android:layout_width="70dp"
                android:layout_height="100dp"
                android:orientation="vertical">

            <ImageView
                    android:id="@+id/chat_photo"
                    android:layout_width="70dp"
                    android:layout_height="70dp"
                    android:src="@mipmap/ic_launcher"/>

            <TextView
                    android:id="@+id/chat_name"
                    android:layout_width="70dp"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:text="TextView"/>

        </LinearLayout>
    </LinearLayout>
</LinearLayout>

03 CardView 实现卡片布局效果

第 1 章 CardView 基础

.1-1 CardView 介绍(更多 IT 教程 1 )

.1-3 CardView 常用属性介绍(更多 IT 教程 1 )


.1-5 CardView 属性效果展示(更多 IT 教程 1 )



第 2 章 CardView 案例实现(重点)

.2-1 案例展示(更多 IT 教程 1 )

.2-2 CardView 基本操作(更多 IT 教程 1 )
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".BaseCardActivity">

    <androidx.cardview.widget.CardView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            app:cardCornerRadius="10dp"
            app:contentPadding="2dp"
            app:cardBackgroundColor="#ff0044"
            app:cardElevation="10dp">

        <TextView
                android:layout_width="200dp"
                android:layout_height="50dp"
                android:gravity="center"
                android:text="hello world"/>
    </androidx.cardview.widget.CardView>

</FrameLayout>
.2-3 案例-布局搭建(更多 IT 教程 1 )
<!-- item_msg.xml -->
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">

    <androidx.cardview.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="16dp"
            android:layout_marginVertical="8dp"
            app:cardCornerRadius="8dp"
            app:cardElevation="4dp">

        <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

            <ImageView
                    android:id="@+id/card_img"
                    android:layout_width="match_parent"
                    android:layout_height="150dp"
                    android:scaleType="centerCrop"
                    android:src="@mipmap/img01"/>

            <!-- 不希望出现在最终的内容,而只是测试看看效果的属性值,可以用tools:开头 -->
            <TextView
                    android:id="@+id/card_title"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_margin="8dp"
                    android:textColor="#000000"
                    android:textSize="16sp"
                    android:textStyle="bold"
                    tools:text="使用慕课网学习android技术"/>

            <TextView
                    android:id="@+id/card_content"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="8dp"
                    android:layout_marginRight="8dp"
                    android:layout_marginBottom="8dp"
                    tools:text="使用慕课网学习android技术"/>
        </LinearLayout>
    </androidx.cardview.widget.CardView>
</FrameLayout>
<!-- activity_card_demo.xml -->
<?xml version="1.0" encoding="utf-8"?>
<ListView 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:id="@+id/id_lv_msg_list"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:background="#ffffff"
          android:divider="@null"
          android:paddingTop="8dp"
          android:orientation="vertical"
          tools:context=".CardDemoActivity">

</ListView>
.2-4 案例-实体类创建(更多 IT 教程 1 )
package com.malugy.activitydemo.entity;

import com.malugy.activitydemo.R;

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

// 提供假数据
public class MsgLab {
    public static List<Msg> generateMockList() {
        List<Msg> msgs = new ArrayList<>();
        Msg msg = new Msg(1,
                R.mipmap.img01,
                "如何才能不错过人工智能的时代?",
                "下一个时代就是机器学习的时代,慕课网发大招,与你一起预见未来!");
        msgs.add(msg);

        msg = new Msg(2,
                R.mipmap.img02,
                "关于你的面试、实习心路历程",
                "奖品丰富,更设有参与奖,随机抽取5名幸运用户,获得慕课网付费面试课程中的任意一门!");
        msgs.add(msg);

        msg = new Msg(3,
                R.mipmap.img03,
                "狗粮不是你想吃,就能吃的!",
                "你的朋友圈开始了吗?一半秀恩爱,一半扮感伤!不怕,还有慕课网陪你坚强地走下去!!");
        msgs.add(msg);

        msg = new Msg(4,
                R.mipmap.img04,
                "前端跳槽面试那些事儿",
                "工作有几年了,项目偏简单有点拿不出手怎么办? 目前还没毕业,正在自学前端,请问可以找到一份前端工作吗,我该怎么办?");
        msgs.add(msg);

        msg = new Msg(5,
                R.mipmap.img05,
                "图解程序员怎么过七夕?",
                "哈哈哈哈,活该单身25年!");
        msgs.add(msg);

        return msgs;
    }
}
package com.malugy.activitydemo.entity;

public class Msg {
    private int id;
    private int imgResId;
    private String title;
    private String content;

    public Msg(int id, int imgResId, String title, String content) {
        this.id = id;
        this.imgResId = imgResId;
        this.title = title;
        this.content = content;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getImgResId() {
        return imgResId;
    }

    public void setImgResId(int imgResId) {
        this.imgResId = imgResId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}
.2-5 案例-功能实现(更多 IT 教程 1 )
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ListView;

import com.malugy.activitydemo.entity.Msg;
import com.malugy.activitydemo.entity.MsgLab;
import com.malugy.adapter.MsgAdapter;

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

public class CardDemoActivity extends AppCompatActivity {

    private ListView cardListView;
    private List<Msg> datas = new ArrayList<>();
    private MsgAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_card_demo);

        cardListView = findViewById(R.id.id_lv_msg_list);

        datas.addAll(MsgLab.generateMockList());
        datas.addAll(MsgLab.generateMockList());

        adapter = new MsgAdapter(this, datas);
        cardListView.setAdapter(adapter);
    }
}
package com.malugy.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.malugy.activitydemo.R;
import com.malugy.activitydemo.entity.Msg;

import java.util.List;

public class MsgAdapter extends BaseAdapter {
    private Context context;
    private List<Msg> datas;
    private LayoutInflater inflater;

    public MsgAdapter(Context context, List<Msg> datas) {
        this.context = context;
        inflater = LayoutInflater.from(context);
        this.datas = datas;
    }

    @Override
    public int getCount() {
        return datas.size();
    }

    @Override
    public Object getItem(int i) {
        return datas.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder holder = null;

        if (view == null) {
            view = inflater.inflate(R.layout.item_msg, viewGroup, false);
            holder = new ViewHolder();

            holder.card_img = view.findViewById(R.id.card_img);
            holder.card_title = view.findViewById(R.id.card_title);
            holder.card_content = view.findViewById(R.id.card_content);

            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }

        Msg msg = datas.get(i);
        holder.card_img.setImageResource(msg.getImgResId());
        holder.card_title.setText(msg.getTitle());
        holder.card_content.setText(msg.getContent());

        return view;
    }

    public static class ViewHolder {
        ImageView card_img;
        TextView card_title;
        TextView card_content;
    }
}
.2-6 案例-适配(更多 IT 教程 1 )

使用限定符(-后面的)来限制属性在某些 sdk 版本(如 Android 5.0(API 级别 21)-v21 )










04 屏幕适配

第 1 章 课程介绍





第 2 章 常见的屏幕尺寸单位(重点)

.2-1 屏幕尺寸、分辨率(更多 IT 教程 1 )





.2-2 屏幕像素密度(更多 IT 教程 1 )




.2-3 常见单位的概念与区别(dpi、dp、sp 等)(更多 IT 教程 1 )




.2-4 ldpi、mdpi、hdpi、xdpi 等的区别(更多 IT 教程 1 )

第 3 章 屏幕适配技巧



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".MediaActivity">

    <!--1.wrap_content 2.match_parent 3.dp-->

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

        <!--
            wrap_content先按照内容设定大小
            再根据weight分配空间
        -->
        <TextView
                android:layout_width="wrap_content"
                android:layout_height="30dp"
                android:layout_weight="1"
                android:background="#ff0000"
                android:text="11111111111111"/>

        <TextView
                android:layout_width="wrap_content"
                android:layout_height="30dp"
                android:layout_weight="2"
                android:background="#0000ff"
                android:text="2"/>

    </LinearLayout>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
        <!--
            match_parent: (不受内容变化影响)
                控件大小=父容器大小+权重比例*剩余空间大小
                红色=1match_parent + 1/(1+2)*(1match_parent - 2match_parent)
        -->
        <TextView
                android:layout_width="match_parent"
                android:layout_height="30dp"
                android:layout_weight="1"
                android:background="#ff0000"
                android:text="111111111111111111"/>

        <TextView
                android:layout_width="match_parent"
                android:layout_height="30dp"
                android:layout_weight="2"
                android:background="#0000ff"
                android:text="211111111111111111111111"/>
    </LinearLayout>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
        <!--
            0dp: 直接按设定的比例分配空间
         -->
        <TextView
                android:layout_width="0dp"
                android:layout_height="30dp"
                android:layout_weight="1"
                android:background="#ff0000"
                android:text="111111111111111111"/>

        <TextView
                android:layout_width="0dp"
                android:layout_height="30dp"
                android:layout_weight="2"
                android:background="#0000ff"
                android:text="211111111111111111111111"/>
    </LinearLayout>

</LinearLayout>

第 4 章 不丢帧的图片

随便找一张图


拖动左、上部分,创建可拉伸区域

拖动右、下部分,创建内容显示区域

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".MediaImgActivity">

    <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/reactor"
            android:text="@string/media_text"
            android:textSize="32sp"/>
</LinearLayout>

03 步骤三:数据存储

01 Android 本地文件操作

第 1 章 课程介绍

第 2 章 SharePreference

2-1 SharePreference 介绍(更多 IT 教程 1 )


.2-2 SharePreference 使用(更多 IT 教程 1 )
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class StorageSharePerferenceActivity extends AppCompatActivity {

    private static final String TAG = "SharePerferenceActivity";
    private EditText uname_edt, upass_edt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_share_perference);

        uname_edt = findViewById(R.id.uname_edit);
        upass_edt = findViewById(R.id.upass_edit);

        // sharePreference的读取
        // 1. 获取SharedPreferences对象(参数1: 文件名, 参数2: 模式)
        SharedPreferences share = getSharedPreferences("myshare", MODE_PRIVATE);
        // 2. 根据key获取内容(参数1: key 参数2: key不存在时返回的默认值)
        String share_uname = share.getString("account", "");
        String share_pwd = share.getString("pwd", "");

        Log.i(TAG, "onCreate: " + share_uname);
        Log.i(TAG, "onCreate: " + share_pwd);

        uname_edt.setText(share_uname);
        upass_edt.setText(share_pwd);

        findViewById(R.id.login_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 1. 获取输入框内容
                String uname = uname_edt.getText().toString();
                String pwd = upass_edt.getText().toString();
                // 2. 验证
                if (uname.equals("admin") && pwd.equals("123")) {
                    // 2.1 存储信息到sharePerference
                    // 1. 获取SharedPreferences对象(参数1: 文件名, 参数2: 模式)
                    SharedPreferences share = getSharedPreferences("myshare", MODE_PRIVATE);
                    // 2. 获取edit对象
                    SharedPreferences.Editor edit = share.edit();
                    // 3. 存储信息
                    edit.putString("account", uname);
                    edit.putString("pwd", pwd);
                    // 4. 指定提交操作
                    edit.commit();

                    Toast.makeText(StorageSharePerferenceActivity.this, "登录成功", Toast.LENGTH_SHORT)
                            .show();
                } else {
                    // 2.2 验证失败, 提示
                    Toast.makeText(StorageSharePerferenceActivity.this, "登录失败", Toast.LENGTH_SHORT)
                            .show();
                }

            }
        });
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".StorageSharePerferenceActivity">

    <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginVertical="8dp"
            android:orientation="horizontal">

        <TextView
                android:id="@+id/uname_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginHorizontal="8dp"
                android:text="账号:"
                android:textSize="18sp"/>

        <EditText
                android:id="@+id/uname_edit"
                android:layout_width="340dp"
                android:layout_height="wrap_content"/>
    </LinearLayout>

    <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

        <TextView
                android:id="@+id/upass_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginHorizontal="8dp"
                android:text="密码:"
                android:textSize="18sp"/>

        <EditText
                android:id="@+id/upass_edit"
                android:layout_width="340dp"
                android:layout_height="wrap_content"/>
    </LinearLayout>


    <Button
            android:id="@+id/login_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="8dp"
            android:layout_marginTop="8dp"
            android:text="登录"/>
</LinearLayout>

第 3 章 外部存储(重点)

.3-1 外部存储的介绍(更多 IT 教程 1 )


.3-3 外部存储的操作(更多 IT 教程 1 )


0 这个文件夹会被映射成 sdcard 文件夹



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".StorageExternalActivity">

    <EditText
            android:id="@+id/ext_content"
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:layout_margin="10dp"
            android:hint="请输入待存储内容......"/>

    <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:orientation="horizontal">

        <Button
                android:id="@+id/ext_save"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:onClick="onClick"
                android:text="保存"/>

        <Button
                android:id="@+id/ext_read"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:onClick="onClick"
                android:text="读取"/>

    </LinearLayout>

    <TextView
            android:id="@+id/ext_txt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="10dp"
            android:layout_marginTop="100dp"
            android:layout_weight="1"
            tools:text="123"/>
</LinearLayout>
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class StorageExternalActivity extends AppCompatActivity implements View.OnClickListener {
    EditText content_edt;
    TextView ext_txt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_external);

        content_edt = findViewById(R.id.ext_content);
        ext_txt = findViewById(R.id.ext_txt);
    }


    @Override
    public void onClick(View v) {
//        String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/imooc.txt";
        String path = getExternalFilesDir(null) + File.separator + "imooc.txt";
        Log.i("TAG", "operate: " + path);
//        if (!Environment.getExternalStorageState().equals("mounted")) { // sd卡是否存在
//            return;
//        }
        switch (v.getId()) {
            case R.id.ext_save:
                File f = new File(path);
                FileOutputStream fos = null;
                try {
                    if (!f.exists()) {
                        // 创建文件
                        f.createNewFile();
                    }

                    // 参数2:是否追加
                    fos = new FileOutputStream(path, true);
                    String str = content_edt.getText().toString();
                    fos.write(str.getBytes());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                } finally {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
                break;
            case R.id.ext_read:
                FileInputStream fis = null;
                try {
                    fis = new FileInputStream(path);
                    byte[] b = new byte[1024];
                    int len = fis.read(b);
                    String str = new String(b, 0, len);
                    Log.i("TAG", "onClick: " + str);
                    ext_txt.setText(str);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        fis.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                break;
        }
    }
}
.3-5 动态权限(难点)(更多 IT 教程 1 )

没用,android6.0 以上能用,但是现在已经 11~13 了

package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class StorageExternalActivity extends AppCompatActivity implements View.OnClickListener {
    EditText content_edt;
    TextView ext_txt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_external);

        content_edt = findViewById(R.id.ext_content);
        ext_txt = findViewById(R.id.ext_txt);

        // 动态申请权限
        int permission = ContextCompat.checkSelfPermission(
                this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        // 如果权限没有被赋予
        if (permission != PackageManager.PERMISSION_GRANTED) {
            // 申请
            ActivityCompat.requestPermissions(this, new String[]{
                            Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    1); // 触发onRequestPermissionsResult, 携带code 1
        }
    }

    @Override
    public void onRequestPermissionsResult(int reqcode, @NonNull String[] permissions, @NonNull int[] grant_res) {
        super.onRequestPermissionsResult(reqcode, permissions, grant_res);
        if (reqcode == 1) {

        }
    }

    @Override
    public void onClick(View v) {
        // ...
    }
}
.3-6 外部存储私有目录(更多 IT 教程 1 )

存在这两个目录里的文件,在应用卸载时也会被卸载


第 4 章 内部存储(重点)

操作内部存储不需要权限





package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class StorageInsideActivity extends AppCompatActivity implements View.OnClickListener {

    EditText in_edt;
    TextView in_txt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_inside);

        in_edt = this.findViewById(R.id.in_edt);
        in_txt = this.findViewById(R.id.in_txt);
    }

    @Override
    public void onClick(View view) {
        File f = new File(getFilesDir(), "getFilesDir.txt");
        switch (view.getId()) {
            case R.id.in_save:
                // /data/user/0/com.malugy.activitydemo/files
                Log.i("TAG", getFilesDir().toString());
                FileOutputStream fos = null;
                try {
                    if (!f.exists()) {
                        f.createNewFile();
                    }
                    fos = new FileOutputStream(f);
                    fos.write(in_edt.getText().toString().getBytes());
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                break;
            case R.id.in_read:
                try {
                    FileInputStream fis = new FileInputStream(f);
                    byte[] b = new byte[1024];
                    int len = fis.read(b);
                    String str = new String(b, 0, len);
                    in_txt.setText(str);
                    fis.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical">

    <EditText
            android:id="@+id/in_edt"
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:layout_margin="10dp"
            android:hint="请输入待存储内容......"/>

    <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:orientation="horizontal">

        <Button
                android:id="@+id/in_save"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:onClick="onClick"
                android:text="保存"/>

        <Button
                android:id="@+id/in_read"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:onClick="onClick"
                android:text="读取"/>

    </LinearLayout>

    <TextView
            android:id="@+id/in_txt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="10dp"
            android:layout_marginTop="100dp"
            android:layout_weight="1"
            tools:text="123"/>
</LinearLayout>

第 5 章 经验分享

.5-1 FileNotFound 异常解决方案(更多 IT 教程 1 )


5.2 DDMS 中 data、SDCard 目录无法展开

02 Android 数据库操作

第 1 章 学习指南




第 2 章 数据库简介



第 3 章 数据库语句操作

.3-1 SQLiteExpert 工具介绍、建库-慕课网就业班 2019-07-25 09_30(更多 IT 教程 1 )



3-2 数据库建表-慕课网就业班 2019-07-25 09_31(更多 IT 教程 1 )

create table info_tb (
       _id integer primary key autoincrement,
       name varchar(30) not null,
       age integer,
       gender varchar(2) not null
)
.3-4 添加语句-慕课网就业班 2019-07-25 09_30(更多 IT 教程 1x)

insert into info_tb (name,age,gender) values ('张三',23,'男')


insert into info_tb values (2,'赵六',33,'男')

.3-6 删除语句-慕课网就业班 2019-07-25 09_37(更多 IT 教程 1 )



.3-8 修改语句-慕课网就业班 2019-07-25 09_31(更多 IT 教程 11)


.3-10 查询语句-慕课网就业班 2019-07-25 09_31(更多 IT 教程 11)


.3-12 手机数据库文件的导入-慕课网就业班 2019-07-25 09_31(更多 IT 教程 1 )

第 4 章 Android 中操作 SQL 语句(重点)

.4-1 界面布局-慕课网就业班 2019-07-25 09_33(更多 IT 教程 1x)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              android:padding="10dp"
              tools:context=".StorageSqliteActivity">

    <EditText
            android:id="@+id/sql_name_edt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="姓名: "/>

    <EditText
            android:id="@+id/sql_age_edt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="年龄: "
            android:numeric="integer"/>

    <RadioGroup
            android:id="@+id/gender_gp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="horizontal">

        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:text="性别"/>

        <RadioButton
                android:id="@+id/male"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:checked="true"
                android:text=""/>

        <RadioButton
                android:id="@+id/female"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:text=""/>
    </RadioGroup>

    <EditText
            android:id="@+id/sql_id_edt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="编号"/>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

        <Button
                android:id="@+id/sql_save"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="添加"/>

        <Button
                android:id="@+id/sql_select"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="查询"/>

        <Button
                android:id="@+id/sql_upt"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="修改"/>

        <Button
                android:id="@+id/sql_del"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="删除"/>
    </LinearLayout>
</LinearLayout>
.4-2 SQLiteOpenHelp 类的作用-慕课网就业班 2019-07-25 09_33(更多 IT 教程 1x)


package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import java.io.File;

public class StorageSqliteActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_sqlite);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save:
                // 参数2: 数据库名称(存在则使用, 不存在则创建)
                // 只有数据库名,则默认去私有目录找 (com.xxx.xxx)
                // 带有路径, 则数据库在指定路径下
                String path = getExternalFilesDir(null) + File.separator + "test.db";

                SQLiteOpenHelper helper = new SQLiteOpenHelper(
                        this, path, null, 1) {
                    @Override
                    public void onCreate(SQLiteDatabase sqLiteDatabase) {
                        // 创建
                        Toast.makeText(StorageSqliteActivity.this, "数据库创建", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
                        // 更新
                        Toast.makeText(StorageSqliteActivity.this, "数据库更新", Toast.LENGTH_SHORT).show();
                    }
                };

                // 获取数据库对象
                helper.getReadableDatabase();
                break;
            case R.id.sql_select:

                break;
            case R.id.sql_upt:

                break;
            case R.id.sql_del:

                break;
        }
    }
}
.4-5 SQLiteDatabase 类的操作-慕课网就业班 2019-07-25 09_33(更多 IT 教程 1x)




    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save:
                // 参数2: 数据库名称(存在则使用, 不存在则创建)
                // 只有数据库名,则默认去私有目录找 (com.xxx.xxx)
                // 带有路径, 则数据库在指定路径下
                String path = getExternalFilesDir(null) + File.separator + "test.db";

                SQLiteOpenHelper helper = new SQLiteOpenHelper(
                        this, path, null, 1) {
                    @Override
                    public void onCreate(SQLiteDatabase sqLiteDatabase) {
                        // 创建
                        Toast.makeText(StorageSqliteActivity.this, "数据库创建", Toast.LENGTH_SHORT).show();

                        String sql = "create table test_db (_id integer primary key autoincrement, " +
                                "name varchar(20), " +
                                "age integer);" ;
                        sqLiteDatabase.execSQL(sql);
                    }

                    @Override
                    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
                        // 更新
                        Toast.makeText(StorageSqliteActivity.this, "数据库更新", Toast.LENGTH_SHORT).show();
                    }
                };

                // 获取数据库对象
                SQLiteDatabase db = helper.getReadableDatabase();
                break;
            case R.id.sql_select:

                break;
            case R.id.sql_upt:

                break;
            case R.id.sql_del:

                break;
        }
    }
.4-6 数据添加-慕课网就业班 2019-07-25 09_33(更多 IT 教程 1x)
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.Toast;

import java.io.File;

public class StorageSqliteActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText name_edt, age_edt, id_edt;
    private RadioGroup gender_gp;
    private String gender_str = "男";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_sqlite);

        name_edt = findViewById(R.id.sql_name_edt);
        age_edt = findViewById(R.id.sql_age_edt);
        id_edt = findViewById(R.id.sql_id_edt);

        gender_gp = findViewById(R.id.gender_gp);
        gender_gp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int i) {
                if (i == R.id.male) {
                    // 男
                    gender_str = "男";
                } else {
                    // 女
                    gender_str = "女";
                }
            }
        });
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save:
                // 参数2: 数据库名称(存在则使用, 不存在则创建)
                // 只有数据库名,则默认去私有目录找 (com.xxx.xxx)
                // 带有路径, 则数据库在指定路径下
                String path = getExternalFilesDir(null) + File.separator + "stu .db";

                SQLiteOpenHelper helper = new SQLiteOpenHelper(
                        this, path, null, 1) {
                    @Override
                    public void onCreate(SQLiteDatabase sqLiteDatabase) {
                        // 创建
                        Toast.makeText(StorageSqliteActivity.this, "数据库创建", Toast.LENGTH_SHORT).show();

                        String sql = "create table info_tb (\n" +
                                "       _id integer primary key autoincrement,\n" +
                                "       name varchar(30) not null,\n" +
                                "       age integer,\n" +
                                "       gender varchar(2) not null\n" +
                                ")";
                        sqLiteDatabase.execSQL(sql);
                    }

                    @Override
                    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
                        // 更新
                        Toast.makeText(StorageSqliteActivity.this, "数据库更新", Toast.LENGTH_SHORT).show();
                    }
                };

                // 获取数据库对象
                SQLiteDatabase db = helper.getReadableDatabase();

                String name_str = name_edt.getText().toString();
                String age_str = age_edt.getText().toString();

//                String sql = "insert into info_tb (name,age,gender) values " +
//                        "('" + name_str + "'," + age_str + ",'" + gender_str + "');";
                String sql = "insert into info_tb (name,age,gender) values (?,?,?)";
                db.execSQL(sql, new String[]{name_str, age_str, gender_str});

                Toast.makeText(StorageSqliteActivity.this, "添加成功", Toast.LENGTH_SHORT).show();
                break;
            case R.id.sql_select:

                break;
            case R.id.sql_upt:

                break;
            case R.id.sql_del:

                break;
        }
    }
}
.4-7 SQL 查询数据-慕课网就业班 2019-07-25 09_33(更多 IT 教程 11)
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.view.View;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;

import java.io.File;

public class StorageSqliteActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText name_edt, age_edt, id_edt;
    private RadioGroup gender_gp;
    private String gender_str = "男";
    private ListView stu_list;
    private SQLiteDatabase db;
    private SQLiteOpenHelper helper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_sqlite);

        name_edt = findViewById(R.id.sql_name_edt);
        age_edt = findViewById(R.id.sql_age_edt);
        id_edt = findViewById(R.id.sql_id_edt);

        stu_list = findViewById(R.id.stu_list);

        gender_gp = findViewById(R.id.gender_gp);
        gender_gp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int i) {
                if (i == R.id.male) {
                    // 男
                    gender_str = "男";
                } else {
                    // 女
                    gender_str = "女";
                }
            }
        });

        // 参数2: 数据库名称(存在则使用, 不存在则创建)
        // 只有数据库名,则默认去私有目录找 (com.xxx.xxx)
        // 带有路径, 则数据库在指定路径下
        String path = getExternalFilesDir(null) + File.separator + "stu .db";

        helper = new SQLiteOpenHelper(
                StorageSqliteActivity.this, path, null, 1) {
            @Override
            public void onCreate(SQLiteDatabase sqLiteDatabase) {
                // 创建
                Toast.makeText(StorageSqliteActivity.this, "数据库创建", Toast.LENGTH_SHORT).show();

                String sql = "create table info_tb (\n" +
                        "       _id integer primary key autoincrement,\n" +
                        "       name varchar(30) not null,\n" +
                        "       age integer,\n" +
                        "       gender varchar(2) not null\n" +
                        ")";
                db.execSQL(sql);
            }

            @Override
            public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
                // 更新
                Toast.makeText(StorageSqliteActivity.this, "数据库更新", Toast.LENGTH_SHORT).show();
            }
        };

        // 获取数据库对象
        db = helper.getReadableDatabase();
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save:
                String name_str = name_edt.getText().toString();
                String age_str = age_edt.getText().toString();

//                String sql = "insert into info_tb (name,age,gender) values " +
//                        "('" + name_str + "'," + age_str + ",'" + gender_str + "');";
                String sql = "insert into info_tb (name,age,gender) values (?,?,?)";
                db.execSQL(sql, new String[]{name_str, age_str, gender_str});

                Toast.makeText(StorageSqliteActivity.this, "添加成功", Toast.LENGTH_SHORT).show();
                break;
            case R.id.sql_select:
                String select_sql = "select * from info_tb";
                // 如果输入id, 则根据id查询
                String idstr = id_edt.getText().toString();
                if (!idstr.equals("")) {
                    select_sql += " where _id=" + idstr;
                }

                Cursor c = db.rawQuery(select_sql, null);

                // 参数3: 数据源
                SimpleCursorAdapter adapter = new SimpleCursorAdapter(
                        this, R.layout.stu_sql_item, c,
                        new String[]{"_id", "name", "age", "gender"},
                        new int[]{R.id.stu_id_item, R.id.stu_name_item, R.id.stu_age_item, R.id.stu_gender_item},
                        // 表示提醒页面更新
                        CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
                stu_list.setAdapter(adapter);
                break;
            case R.id.sql_upt:

                break;
            case R.id.sql_del:

                break;
        }
    }
}
<!-- stu_sql_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="match_parent"
              android:orientation="horizontal">

    <TextView
            android:id="@+id/stu_id_item"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>

    <TextView
            android:id="@+id/stu_name_item"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>

    <TextView
            android:id="@+id/stu_age_item"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>

    <TextView
            android:id="@+id/stu_gender_item"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>
</LinearLayout>
<!-- activity_storage_sqlite.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              android:padding="10dp"
              tools:context=".StorageSqliteActivity">

    <EditText
            android:id="@+id/sql_name_edt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="姓名: "/>

    <EditText
            android:id="@+id/sql_age_edt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="年龄: "
            android:numeric="integer"/>

    <RadioGroup
            android:id="@+id/gender_gp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="horizontal">

        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:text="性别"/>

        <RadioButton
                android:id="@+id/male"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:checked="true"
                android:text=""/>

        <RadioButton
                android:id="@+id/female"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:text=""/>
    </RadioGroup>

    <EditText
            android:id="@+id/sql_id_edt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="编号"/>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

        <Button
                android:id="@+id/sql_save"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="onClick"
                android:text="添加"/>

        <Button
                android:id="@+id/sql_select"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="onClick"
                android:text="查询"/>

        <Button
                android:id="@+id/sql_upt"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="onClick"
                android:text="修改"/>

        <Button
                android:id="@+id/sql_del"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="onClick"
                android:text="删除"/>
    </LinearLayout>

    <ListView
            android:id="@+id/stu_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

    </ListView>
</LinearLayout>

sqlite 使用第三方(非自建) db 必须要有_id 字段

.4-8 SQL 删除数据-慕课网就业班 2019-07-25 09_33(更多 IT 教程 1 )
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.view.View;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;

import java.io.File;

public class StorageSqliteActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText name_edt, age_edt, id_edt;
    private RadioGroup gender_gp;
    private String gender_str = "男";
    private ListView stu_list;
    private SQLiteDatabase db;
    private SQLiteOpenHelper helper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save:
                // ...
            case R.id.sql_select:
                // ...
            case R.id.sql_del:
                String delete_sql = "delete from info_tb where _id=?";
                String id = id_edt.getText().toString();
                if (id.equals("")) {
                    Toast.makeText(StorageSqliteActivity.this, "ID不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                db.execSQL(delete_sql, new String[]{id});
                Toast.makeText(StorageSqliteActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
                break;
            case R.id.sql_upt:

                break;
        }
    }
}
.4-9 SQL 修改数据-慕课网就业班 2019-07-25 09_34(更多 IT 教程 11)
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;

import java.io.File;

public class StorageSqliteActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText name_edt, age_edt, id_edt;
    private RadioGroup gender_gp;
    private RadioButton male_btn;
    private RadioButton female_btn;
    private String gender_str = "男";
    private ListView stu_list;
    private SQLiteDatabase db;
    private SQLiteOpenHelper helper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_sqlite);

        name_edt = findViewById(R.id.sql_name_edt);
        age_edt = findViewById(R.id.sql_age_edt);
        id_edt = findViewById(R.id.sql_id_edt);

        stu_list = findViewById(R.id.stu_list);

        gender_gp = findViewById(R.id.gender_gp);

        male_btn = findViewById(R.id.male);
        female_btn = findViewById(R.id.female);

        gender_gp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int i) {
                if (i == R.id.male) {
                    // 男
                    gender_str = "男";
                } else {
                    // 女
                    gender_str = "女";
                }
            }
        });

        // 参数2: 数据库名称(存在则使用, 不存在则创建)
        // 只有数据库名,则默认去私有目录找 (com.xxx.xxx)
        // 带有路径, 则数据库在指定路径下
        String path = getExternalFilesDir(null) + File.separator + "stu .db";

        helper = new SQLiteOpenHelper(
                StorageSqliteActivity.this, path, null, 1) {
            @Override
            public void onCreate(SQLiteDatabase sqLiteDatabase) {
                // 创建
                Toast.makeText(StorageSqliteActivity.this, "数据库创建", Toast.LENGTH_SHORT).show();

                String sql = "create table info_tb (\n" +
                        "       _id integer primary key autoincrement,\n" +
                        "       name varchar(30) not null,\n" +
                        "       age integer,\n" +
                        "       gender varchar(2) not null\n" +
                        ")";
                db.execSQL(sql);
            }

            @Override
            public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
                // 更新
                Toast.makeText(StorageSqliteActivity.this, "数据库更新", Toast.LENGTH_SHORT).show();
            }
        };

        // 获取数据库对象
        db = helper.getReadableDatabase();
    }

    private void clear() {
        name_edt.setText("");
        age_edt.setText("");
        id_edt.setText("");
        male_btn.setChecked(true);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save: {
                String name_str = name_edt.getText().toString();
                String age_str = age_edt.getText().toString();
                if (name_str.equals("") || age_str.equals("")) {
                    Toast.makeText(StorageSqliteActivity.this, "姓名或年龄不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }
//                String sql = "insert into info_tb (name,age,gender) values " +
//                        "('" + name_str + "'," + age_str + ",'" + gender_str + "');";
                String sql = "insert into info_tb (name,age,gender) values (?,?,?)";
                db.execSQL(sql, new String[]{name_str, age_str, gender_str});

                Toast.makeText(StorageSqliteActivity.this, "添加成功", Toast.LENGTH_SHORT).show();
                clear();
                break;
            }
            case R.id.sql_select: {
                String select_sql = "select * from info_tb";
                // 如果输入id, 则根据id查询
                String idstr = id_edt.getText().toString();
                if (!idstr.equals("")) {
                    select_sql += " where _id=" + idstr;
                }

                Cursor c = db.rawQuery(select_sql, null);

                // 参数3: 数据源
                SimpleCursorAdapter adapter = new SimpleCursorAdapter(
                        this, R.layout.stu_sql_item, c,
                        new String[]{"_id", "name", "age", "gender"},
                        new int[]{R.id.stu_id_item, R.id.stu_name_item, R.id.stu_age_item, R.id.stu_gender_item},
                        // 表示提醒页面更新
                        CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
                stu_list.setAdapter(adapter);
                clear();
                break;
            }
            case R.id.sql_del: {
                String delete_sql = "delete from info_tb where _id=?";
                String id = id_edt.getText().toString();
                if (id.equals("")) {
                    Toast.makeText(StorageSqliteActivity.this, "ID不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                db.execSQL(delete_sql, new String[]{id});
                Toast.makeText(StorageSqliteActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
                clear();
                break;
            }
            case R.id.sql_upt: {
                String name_str = name_edt.getText().toString();
                String age_str = age_edt.getText().toString();
                if (name_str.equals("") || age_str.equals("")) {
                    Toast.makeText(StorageSqliteActivity.this, "姓名或年龄不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                String id = id_edt.getText().toString();
                if (id.equals("")) {
                    Toast.makeText(StorageSqliteActivity.this, "ID不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                String update_sql = "update info_tb set name=?, age=?, gender=? where _id=?";
                db.execSQL(update_sql, new String[]{
                        name_str, age_str, gender_str, id
                });
                Toast.makeText(StorageSqliteActivity.this, "修改成功",
                        Toast.LENGTH_SHORT).show();
                clear();
                break;
            }
        }
    }
}

第 5 章 使用相关 API 操作数据库(重点)

.5-1 API 数据添加-慕课网就业班 2019-07-25 09_36(更多 IT 教程 1x)

package com.malugy.activitydemo;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.view.View;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import java.io.File;

public class StorageSqliteAPIActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText name_edt, age_edt, id_edt;
    private String name_str, age_str, id_str;
    private RadioGroup gender_gp;
    private RadioButton male_btn;
    private RadioButton female_btn;
    private String gender_str = "男";
    private ListView stu_list;
    private SQLiteDatabase db;
    private SQLiteOpenHelper helper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
    }

    private void clear() {
        name_edt.setText("");
        age_edt.setText("");
        id_edt.setText("");
        male_btn.setChecked(true);
    }

    private void get_edt_val() {
        name_str = name_edt.getText().toString();
        age_str = age_edt.getText().toString();
        id_str = id_edt.getText().toString();
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save: {
                ContentValues values = new ContentValues();
                get_edt_val();
                if (name_str.equals("") || age_str.equals("")) {
                    Toast.makeText(StorageSqliteAPIActivity.this, "姓名或年龄不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                values.put("name", name_str);
                values.put("age", age_str);
                values.put("gender", gender_str);

                // 参数1: 数据库表名
                // 参数2: 可以为空的列
                // (如果参数3为null或没有数据,则会报错,可以通过参数2设置可空列,将该列设为null,但是依然可能报错)
                long id = db.insert("info_tb", null, values);

                Toast.makeText(StorageSqliteAPIActivity.this, "添加成功, 编号: " + id,
                        Toast.LENGTH_SHORT).show();
                clear();
                break;
            }
        }
    }
}
.5-2 API 数据查询-慕课网就业班 2019-07-25 09_36(更多 IT 教程 1x)


    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save: {
                // ...
            }
            case R.id.sql_select: {
                get_edt_val();
                Cursor c;
                if (!id_str.equals("")) {
//                    select_sql += " where _id=" + id_str;
                    c = db.query(
                            "info_tb", null,
                            "_id=?", new String[]{id_str},
                            null, null,
                            null
                    );
                } else {
                    // 参数2: 要查询的列 {"name", "age"} ...
                    // 查询所有 -> null
                    // 参数3: 条件 "xxx=? and xxx=?" (针对列)
                    // 参数4: 条件的值 {"xxx","xxx"}
                    // 参数5: 分组
                    // 参数6: 当group by分组后, 通过having来去除不符合条件的组
                    // 参数7: order by
                    c = db.query(
                            "info_tb", null,
                            null, null,
                            null, null,
                            null
                    );
                }

                // 参数3: 数据源
                SimpleCursorAdapter adapter = new SimpleCursorAdapter(
                        this, R.layout.stu_sql_item, c,
                        new String[]{"_id", "name", "age", "gender"},
                        new int[]{R.id.stu_id_item, R.id.stu_name_item, R.id.stu_age_item, R.id.stu_gender_item},
                        // 表示提醒页面更新
                        CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
                stu_list.setAdapter(adapter);
                clear();
                break;
            }
        }
    }
.5-3 API 数据删除-慕课网就业班 2019-07-25 09_36(更多 IT 教程 11)
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save: {
                // ...
            }
            case R.id.sql_select: {
                // ...
            }
            case R.id.sql_del: {
                get_edt_val();
                if (id_str.equals("")) {
                    Toast.makeText(StorageSqliteAPIActivity.this, "ID不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                int count = db.delete("info_tb", "_id=?", new String[]{id_str});
                if (count > 0) {
                    Toast.makeText(StorageSqliteAPIActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(StorageSqliteAPIActivity.this, "删除失败", Toast.LENGTH_SHORT).show();
                }
                clear();
                break;
            }
        }
    }
5-4 API 数据修改-慕课网就业班 2019-07-25 09_36(更多 IT 教程 1x)
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save: {
                // ...
            }
            case R.id.sql_select: {
                // ...
            }
            case R.id.sql_del: {
                // ...
            }
            case R.id.sql_upt: {
                get_edt_val();
                if (name_str.equals("") || age_str.equals("")) {
                    Toast.makeText(StorageSqliteAPIActivity.this, "姓名或年龄不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                if (id_str.equals("")) {
                    Toast.makeText(StorageSqliteAPIActivity.this, "ID不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                ContentValues values = new ContentValues();
                values.put("name", name_str);
                values.put("age", age_str);
                values.put("gender", gender_str);

                int count = db.update("info_tb", values, "_id=?", new String[]{id_str});
                if (count > 0) {
                    Toast.makeText(StorageSqliteAPIActivity.this, "修改成功", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(StorageSqliteAPIActivity.this, "修改失败", Toast.LENGTH_SHORT).show();
                }
                clear();
                break;
            }
        }
    }
.5-5 Sqlite 基础总结-慕课网就业班 2019-07-25 09_37(更多 IT 教程 11)

第 6 章 使用面向对象思想封装操作(进阶)

.6-1 本章简介及建立 DAO 类-慕课网就业班 2019-07-25 09_38(更多 IT 教程 1x)
package com.malugy.activitydemo.dao;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;

import com.malugy.activitydemo.StorageSqliteAPIActivity;

import java.io.File;

public class StudentDao {
    private SQLiteDatabase db;
    private SQLiteOpenHelper helper;

    public StudentDao(Context context) {
        // 参数2: 数据库名称(存在则使用, 不存在则创建)
        // 只有数据库名,则默认去私有目录找 (com.xxx.xxx)
        // 带有路径, 则数据库在指定路径下
        String path = context.getExternalFilesDir(null) + File.separator + "stu .db";

        helper = new SQLiteOpenHelper(context, path, null, 1) {
            @Override
            public void onCreate(SQLiteDatabase sqLiteDatabase) {
            }

            @Override
            public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
            }
        };

        // 获取数据库对象
        db = helper.getReadableDatabase();
    }
}
package com.malugy.activitydemo.entity;

public class Student {
    private int id;
    private String name;
    private int age;
    private String gender;

    public Student() {
    }

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public Student(int id, String name, int age, String gender) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}
.6-2 添加操作-慕课网就业班 2019-07-25 09_43(更多 IT 教程 11)
package com.malugy.activitydemo;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.view.View;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.malugy.activitydemo.dao.StudentDao;
import com.malugy.activitydemo.entity.Student;

import java.io.File;

public class StorageSqliteSelfActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText name_edt, age_edt, id_edt;
    private String name_str, age_str, id_str;
    private RadioGroup gender_gp;
    private RadioButton male_btn;
    private RadioButton female_btn;
    private String gender_str = "男";
    private ListView stu_list;
    private SQLiteDatabase db;
    private SQLiteOpenHelper helper;
    private StudentDao dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_sqlite);

        dao = new StudentDao(this);

        // ...
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save: {
                get_edt_val();
                if (name_str.equals("") || age_str.equals("")) {
                    Toast.makeText(this, "姓名或年龄不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                Student stu = new Student(
                        name_str, Integer.parseInt(age_str), gender_str
                );
                dao.addStudent(stu);

                Toast.makeText(this, "添加成功",
                        Toast.LENGTH_SHORT).show();
                clear();
                break;
            }
        }
    }
}
package com.malugy.activitydemo.dao;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;

import com.malugy.activitydemo.StorageSqliteAPIActivity;
import com.malugy.activitydemo.StorageSqliteActivity;
import com.malugy.activitydemo.entity.Student;

import java.io.File;

public class StudentDao {
    private SQLiteDatabase db;
    private SQLiteOpenHelper helper;

    public StudentDao(Context context) {
        // 参数2: 数据库名称(存在则使用, 不存在则创建)
        // 只有数据库名,则默认去私有目录找 (com.xxx.xxx)
        // 带有路径, 则数据库在指定路径下
        String path = context.getExternalFilesDir(null) + File.separator + "stu .db";

        helper = new SQLiteOpenHelper(context, path, null, 1) {
            @Override
            public void onCreate(SQLiteDatabase sqLiteDatabase) {
                // 创建
                Toast.makeText(context, "数据库创建", Toast.LENGTH_SHORT).show();

                String sql = "create table info_tb (\n" +
                        "       _id integer primary key autoincrement,\n" +
                        "       name varchar(30) not null,\n" +
                        "       age integer,\n" +
                        "       gender varchar(2) not null\n" +
                        ")";
                db.execSQL(sql);
            }

            @Override
            public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
                // 更新
                Toast.makeText(context, "数据库更新", Toast.LENGTH_SHORT).show();
            }
        };

        // 获取数据库对象
        db = helper.getReadableDatabase();
    }

    public void addStudent(Student student) {
        String sql = "insert into info_tb (name,age,gender) values (?,?,?)";
        db.execSQL(sql, new String[]{student.getName(), student.getAge() + "", student.getGender()});
    }

    public void getStudent() {

    }

    public void delStudent() {

    }

    public void uptStudent() {

    }
}
.6-3 查询操作-慕课网就业班 2019-07-25 09_38(更多 IT 教程 1x)
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save: {
                // ...
            }
            case R.id.sql_select: {
                get_edt_val();
                Cursor c;
                ArrayList<String> params = new ArrayList();

                if (!name_str.equals("")) {
                    params.add("name");
                    params.add(name_str);
                }
                if (!age_str.equals("")) {
                    params.add("age");
                    params.add(age_str);
                }
                if (!id_str.equals("")) {
                    params.add("_id");
                    params.add(id_str);
                }

                String[] param = params.toArray(new String[]{});

                c = dao.getStudent(param);

                // 参数3: 数据源
                SimpleCursorAdapter adapter = new SimpleCursorAdapter(
                        this, R.layout.stu_sql_item, c,
                        new String[]{"_id", "name", "age", "gender"},
                        new int[]{R.id.stu_id_item, R.id.stu_name_item, R.id.stu_age_item, R.id.stu_gender_item},
                        // 表示提醒页面更新
                        CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
                stu_list.setAdapter(adapter);
                clear();
                break;
            }
        }
    }
package com.malugy.activitydemo.dao;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import android.widget.Toast;

import com.malugy.activitydemo.entity.Student;

import java.io.File;

public class StudentDao {
    private SQLiteDatabase db;
    private SQLiteOpenHelper helper;

    public StudentDao(Context context) {
        // ...
    }

    public void addStudent(Student student) {
        // ...
    }

    public Cursor getStudent(String... strs) {
        // 1.查询所有
        String sql = "select * from info_tb";
        // 2.条件查询(姓名/年龄/id) (第一个参数指明条件, 第二个参数指明条件值
        if (strs.length != 0) {
            sql += " where ";
            for (int i = 0; i < strs.length; i += 2) {
                sql += strs[i] + "='" + strs[i + 1] + "'";
                if (i != strs.length - 2) {
                    sql += " and ";
                }
            }
        }
        Log.i("TAG", sql);
        Cursor c = db.rawQuery(sql, null);
        return c;
    }

    public void delStudent() {

    }

    public void uptStudent() {

    }
}
.6-4 删除操作-慕课网就业班 2019-07-25 09_38(更多 IT 教程 1 )
            case R.id.sql_del: {
                get_edt_val();
                get_sql_params();

                // 感觉最好不要一键删除, 还是要带给条件来删
                if (id_str.equals("") && name_str.equals("") && age_str.equals("")) {
                    Toast.makeText(StorageSqliteSelfActivity.this, "条件不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                String[] param = params.toArray(new String[]{});

                dao.delStudent(param);
                Toast.makeText(StorageSqliteSelfActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
                clear();
                break;
            }
    public void delStudent(String... strs) {
        // 1.删除所有
        String sql = "delete from info_tb";
        // 2.条件删除(姓名/年龄/id) (第一个参数指明条件, 第二个参数指明条件值
        if (strs.length != 0) {
            sql = concat_sql_params(sql, strs);
        }

        db.execSQL(sql);
    }
.6-5 修改操作-慕课网就业班 2019-07-25 09_43(更多 IT 教程 1x)
package com.malugy.activitydemo;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.view.View;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.malugy.activitydemo.dao.StudentDao;
import com.malugy.activitydemo.entity.Student;

import java.io.File;
import java.util.ArrayList;

public class StorageSqliteSelfActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText name_edt, age_edt, id_edt;
    private String name_str, age_str, id_str;
    private RadioGroup gender_gp;
    private RadioButton male_btn;
    private RadioButton female_btn;
    private String gender_str = "男";
    private ListView stu_list;
    private SQLiteDatabase db;
    private SQLiteOpenHelper helper;
    private StudentDao dao;
    private ArrayList<String> params;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_storage_sqlite);

        dao = new StudentDao(this);

        name_edt = findViewById(R.id.sql_name_edt);
        age_edt = findViewById(R.id.sql_age_edt);
        id_edt = findViewById(R.id.sql_id_edt);

        stu_list = findViewById(R.id.stu_list);

        gender_gp = findViewById(R.id.gender_gp);

        male_btn = findViewById(R.id.male);
        female_btn = findViewById(R.id.female);

        gender_gp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int i) {
                if (i == R.id.male) {
                    // 男
                    gender_str = "男";
                } else {
                    // 女
                    gender_str = "女";
                }
            }
        });
    }

    private void clear() {
        name_edt.setText("");
        age_edt.setText("");
        id_edt.setText("");
        male_btn.setChecked(true);
        params = null;
    }

    private void get_edt_val() {
        name_str = name_edt.getText().toString();
        age_str = age_edt.getText().toString();
        id_str = id_edt.getText().toString();
    }


    private void get_sql_params() {
        params = new ArrayList();

        if (!name_str.equals("")) {
            params.add("name");
            params.add(name_str);
        }
        if (!age_str.equals("")) {
            params.add("age");
            params.add(age_str);
        }
        if (!id_str.equals("")) {
            params.add("_id");
            params.add(id_str);
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.sql_save: {
                get_edt_val();
                if (name_str.equals("") || age_str.equals("")) {
                    Toast.makeText(this, "姓名或年龄不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                Student stu = new Student(
                        name_str, Integer.parseInt(age_str), gender_str
                );
                dao.addStudent(stu);

                Toast.makeText(this, "添加成功",
                        Toast.LENGTH_SHORT).show();
                clear();
                break;
            }
            case R.id.sql_select: {
                get_edt_val();
                Cursor c;
                get_sql_params();

                String[] param = params.toArray(new String[]{});

                c = dao.getStudent(param);

                // 参数3: 数据源
                SimpleCursorAdapter adapter = new SimpleCursorAdapter(
                        this, R.layout.stu_sql_item, c,
                        new String[]{"_id", "name", "age", "gender"},
                        new int[]{R.id.stu_id_item, R.id.stu_name_item, R.id.stu_age_item, R.id.stu_gender_item},
                        // 表示提醒页面更新
                        CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
                stu_list.setAdapter(adapter);
                clear();
                break;
            }
            case R.id.sql_del: {
                get_edt_val();
                get_sql_params();

                // 感觉最好不要一键删除, 还是要带给条件来删
                if (id_str.equals("") && name_str.equals("") && age_str.equals("")) {
                    Toast.makeText(StorageSqliteSelfActivity.this, "条件不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                String[] param = params.toArray(new String[]{});

                dao.delStudent(param);
                Toast.makeText(StorageSqliteSelfActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
                clear();
                break;
            }
            case R.id.sql_upt: {
                get_edt_val();
                if (name_str.equals("") || age_str.equals("")) {
                    Toast.makeText(StorageSqliteSelfActivity.this, "姓名或年龄不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }
                if (id_str.equals("")) {
                    Toast.makeText(StorageSqliteSelfActivity.this, "ID不能为空",
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                Student stu = new Student(Integer.parseInt(id_str), name_str, Integer.parseInt(age_str), gender_str);

                dao.uptStudent(stu);

                Toast.makeText(StorageSqliteSelfActivity.this, "修改成功", Toast.LENGTH_SHORT).show();
                clear();
                break;
            }
        }
    }
}
package com.malugy.activitydemo.dao;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import android.widget.Toast;

import com.malugy.activitydemo.entity.Student;

import java.io.File;

public class StudentDao {
    private SQLiteDatabase db;
    private SQLiteOpenHelper helper;

    public StudentDao(Context context) {
        // 参数2: 数据库名称(存在则使用, 不存在则创建)
        // 只有数据库名,则默认去私有目录找 (com.xxx.xxx)
        // 带有路径, 则数据库在指定路径下
        String path = context.getExternalFilesDir(null) + File.separator + "stu .db";

        helper = new SQLiteOpenHelper(context, path, null, 1) {
            @Override
            public void onCreate(SQLiteDatabase sqLiteDatabase) {
                // 创建
                Toast.makeText(context, "数据库创建", Toast.LENGTH_SHORT).show();

                String sql = "create table info_tb (\n" +
                        "       _id integer primary key autoincrement,\n" +
                        "       name varchar(30) not null,\n" +
                        "       age integer,\n" +
                        "       gender varchar(2) not null\n" +
                        ")";
                db.execSQL(sql);
            }

            @Override
            public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
                // 更新
                Toast.makeText(context, "数据库更新", Toast.LENGTH_SHORT).show();
            }
        };

        // 获取数据库对象
        db = helper.getReadableDatabase();
    }

    public void addStudent(Student student) {
        String sql = "insert into info_tb (name,age,gender) values (?,?,?)";
        db.execSQL(sql, new String[]{student.getName(), student.getAge() + "", student.getGender()});
    }

    public Cursor getStudent(String... strs) {
        // 1.查询所有
        String sql = "select * from info_tb";
        // 2.条件查询(姓名/年龄/id) (第一个参数指明条件, 第二个参数指明条件值
        if (strs.length != 0) {
            sql = concat_sql_params(sql, strs);
        }

        Cursor c = db.rawQuery(sql, null);
        return c;
    }

    public void delStudent(String... strs) {
        // 1.删除所有
        String sql = "delete from info_tb";
        // 2.条件删除(姓名/年龄/id) (第一个参数指明条件, 第二个参数指明条件值
        if (strs.length != 0) {
            sql = concat_sql_params(sql, strs);
        }

        db.execSQL(sql);
    }

    public void uptStudent(Student stu) {
        String sql = "update info_tb set name=?,age=?,gender=? where _id=?";
        db.execSQL(sql, new Object[]{
                stu.getName(), stu.getAge(), stu.getGender(), stu.getId()
        });
    }

    private static String concat_sql_params(String sql, String[] strs) {
        sql += " where ";
        for (int i = 0; i < strs.length; i += 2) {
            sql += strs[i] + "='" + strs[i + 1] + "'";
            if (i != strs.length - 2) {
                sql += " and ";
            }
        }
        return sql;
    }

}
.6-6 数据库操作的封装小结-慕课网就业班 2019-07-25 09_39(更多 IT 教程 1x)

03 手风琴特效

第 1 章 课程介绍

第 2 章 ExpandableListView 的基本使用

适用于分组列表




第 3 章 ExpandablelistView 案例开发

.3-1 案例说明-慕课网就业班 2019-07-25 10_11(更多 IT 教程 1x)


.3-2 案例开发之 bean 创建-慕课网就业班 2019-07-25 10_11(更多 IT 教程 11)
package com.malugy.activitydemo.bean;

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

public class Chapter {
    private int id;
    private String name;

    private List<ChapterItem> children = new ArrayList<>();

    public Chapter(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public Chapter() {
    }

    public void addChild(ChapterItem chapterItem) {
        chapterItem.setPid(getId());
        children.add(chapterItem);
    }

    public void addChild(int cid, String cname) {
        ChapterItem chapterItem = new ChapterItem(cid, cname);
        chapterItem.setPid(getId());
        children.add(chapterItem);
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<ChapterItem> getChildren() {
        return children;
    }

    public void setChildren(List<ChapterItem> children) {
        this.children = children;
    }
}
package com.malugy.activitydemo.bean;

public class ChapterItem {
    private int id;
    private String name;
    private int pid; // 所属Chapter的id

    public ChapterItem() {
    }

    public ChapterItem(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }
}
3-3 案例开发之适配器实现-慕课网就业班 2019-07-25 10_51(更多 IT 教程 1x)
package com.malugy.activitydemo.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;

import com.malugy.activitydemo.R;
import com.malugy.activitydemo.bean.Chapter;
import com.malugy.activitydemo.bean.ChapterItem;

import java.util.List;

public class ChapterAdapter extends BaseExpandableListAdapter {
    private List<Chapter> datas;
    private LayoutInflater inflater;
    private Context context;

    public ChapterAdapter(List<Chapter> datas, Context context) {
        this.datas = datas;
        this.context = context;
        inflater = LayoutInflater.from(context);
    }

    @Override
    public int getGroupCount() {
        return datas.size();
    }

    @Override
    public int getChildrenCount(int i) {
        // 子元素有多少
        return datas.get(i).getChildren().size();
    }

    @Override
    public Object getGroup(int i) {
        return datas.get(i);
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return datas.get(groupPosition).getChildren().get(childPosition);
    }

    @Override
    public long getGroupId(int i) {
        return i;
    }

    @Override
    public long getChildId(int i, int i1) {
        return i1;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    // groupview的具体样式
    @Override
    public View getGroupView(int i, boolean b, View view, ViewGroup parent) {
        ParentViewHolder holder = null;
        if (view == null) {
            // 如果没穿parent. 则layout_width等属性会失效, 使用默认的大小
            view = inflater.inflate(R.layout.item_parent_chapter, parent, false);
            holder = new ParentViewHolder();
            holder.tv_name = view.findViewById(R.id.id_tv_name);
            view.setTag(holder);
        } else {
            holder = (ParentViewHolder) view.getTag();
        }

        Chapter chapter = datas.get(i);
        holder.tv_name.setText(chapter.getName());
        return view;
    }

    // ChildView的具体样式
    @Override
    public View getChildView(int i, int i1, boolean b, View view, ViewGroup viewGroup) {
        ChildViewHolder holder = null;
        if (view == null) {
            view = inflater.inflate(R.layout.item_child_chapter, viewGroup, false);
            holder = new ChildViewHolder();
            holder.tv_name = view.findViewById(R.id.id_tv_child);
            view.setTag(holder);
        } else {
            holder = (ChildViewHolder) view.getTag();
        }

        ChapterItem chapterItem = datas.get(i).getChildren().get(i1);
        holder.tv_name.setText(chapterItem.getName());
        return view;
    }

    @Override
    public boolean isChildSelectable(int i, int i1) {
        // 是否可以选中
        return true;
    }

    public static class ParentViewHolder {
        TextView tv_name;
    }

    public static class ChildViewHolder {
        TextView tv_name;
    }
}
<!-- item_parent_chapter.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="56dp"
              android:background="#86b2f9"
              android:orientation="horizontal">

    <TextView
            android:id="@+id/id_tv_name"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:textSize="24dp"
            android:textStyle="bold"
            tools:text="frejfghwerfhu"/>

</LinearLayout>
<!-- item_child_chapter.xml -->
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:id="@+id/id_tv_child"
          android:layout_height="40dp"
          android:layout_gravity="center_vertical"
          android:textSize="16dp">
    <!--官方推荐sp, 而sp可能随着用户调整而变大, 所以也有用dp的, dp大小是固定的-->
</TextView>
.3-5 案例开发之模拟数据创建-慕课网就业班 2019-07-25 10_13(更多 IT 教程 1x)
package com.malugy.activitydemo.bean;

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

public class ChapterLab {
    public static List<Chapter> generateMockDatas() {
        List<Chapter> chapters = new ArrayList<>();

        Chapter root1 = new Chapter(1, "Android");
        Chapter root2 = new Chapter(2, "IOS");
        Chapter root3 = new Chapter(3, "Unity 3D");
        Chapter root4 = new Chapter(4, "Cocos2d-x");

        root1.addChild(1, "PullToRefresh");
        root1.addChild(2, "Android 8.0通知栏解决方案");
        root1.addChild(4, "Android 与WebView的js交互");
        root1.addChild(8, "Android UiAutomator 2.0 入门实战");
        root1.addChild(10, "移动端音频视频入门");

        root2.addChild(11, "iOS开发之LeanCloud");
        root2.addChild(12, "iOS开发之传感器");
        root2.addChild(13, "iOS开发之网络协议");
        root2.addChild(14, "iOS之分享集成");
        root2.addChild(15, "iOS之FTP上传");


        root3.addChild(16, "Unity 3D 翻牌游戏开发");
        root3.addChild(17, "Unity 3D基础之变体Transform");
        root3.addChild(20, "带你开发类似Pokemon Go的AR游戏");
        root3.addChild(21, "Unity 3D游戏开发之脚本系统");
        root3.addChild(22, "Unity 3D地形编辑器");


        root4.addChild(25, "Cocos2d-x游戏之七夕女神抓捕计划");
        root4.addChild(26, "Cocos2d-x游戏开发初体验-C++篇");
        root4.addChild(27, "Cocos2d-x全民俄罗斯");
        root4.addChild(28, "Cocos2d-x坦克大战");
        root4.addChild(30, "新春特辑-Cocos抢红包");


        chapters.add(root1);
        chapters.add(root2);
        chapters.add(root3);
        chapters.add(root4);

        return chapters;
    }
}
.3-6 案例开发之 ExpandableListView 属性方法设置-慕课网就业班 2019-07-25 10_13(更多 IT 教程 1x)
    @Override
    public View getGroupView(int i, boolean isExpanded, View view, ViewGroup parent) {
        ParentViewHolder holder = null;
        if (view == null) {
            view = inflater.inflate(R.layout.item_parent_chapter, parent, false);
            holder = new ParentViewHolder();
            holder.tv_name = view.findViewById(R.id.id_tv_name);
            holder.iv_indicator = view.findViewById(R.id.id_iv_indicator);
            view.setTag(holder);
        } else {
            holder = (ParentViewHolder) view.getTag();
        }

        Chapter chapter = datas.get(i);
        holder.tv_name.setText(chapter.getName());
        holder.iv_indicator.setSelected(isExpanded);
        return view;
    }
<!-- activity_expandable_list_view_base.xml -->
<?xml version="1.0" encoding="utf-8"?>
<ExpandableListView 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:id="@+id/expand_list"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:childDivider="#44ff0000"
                    android:groupIndicator="@null"
                    tools:context=".ExpandableListViewBaseActivity">
    <!--
        子元素的图标start-end, 显示范围
        android:childIndicator="@mipmap/indicator_expand"
        android:childIndicatorStart="0dp"
        android:childIndicatorEnd="48dp"
        比较常用自己写的imageview作为图标
        android:indicatorLeft="10dp"
        android:indicatorRight="40dp"
        android:groupIndicator="@drawable/group_indicator"-->

</ExpandableListView>
<!-- group_indicator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!--  默认使用expand状态  -->
    <!--    <item android:drawable="@mipmap/indicator_expand" android:state_expanded="true"/>-->
    <!--自己写控件作为图标的, 用selected-->
    <item android:drawable="@mipmap/indicator_expand" android:state_selected="true"/>
    <item android:drawable="@mipmap/indicator_collapse"/>
</selector>
<!-- item_parent_chapter.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="56dp"
              android:background="#86b2f9"
              android:orientation="horizontal">

    <ImageView
            android:id="@+id/id_iv_indicator"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_gravity="center"
            android:layout_marginRight="4dp"
            android:background="@drawable/group_indicator"/>

    <TextView
            android:id="@+id/id_tv_name"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:textSize="24dp"
            android:textStyle="bold"
            tools:text="frejfghwerfhu"/>

</LinearLayout>
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.TextView;

import com.malugy.activitydemo.adapter.ChapterAdapter;
import com.malugy.activitydemo.bean.Chapter;
import com.malugy.activitydemo.bean.ChapterLab;

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

public class ExpandableListViewBaseActivity extends AppCompatActivity {
    private static final String TAG = "ExpandableList";
    private ExpandableListView expandableList;
    private BaseExpandableListAdapter adapter;
    private List<Chapter> datas = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_expandable_list_view_base);

        init_views();
        init_event();
    }

    private void init_event() {
        expandableList.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView expandableListView,
                                        View view, int i, int i1, long l) {
                Log.d(TAG, "onChildClick: " + "groupPosition = " + i + ", childPosition = " + i1);

                return false;
            }
        });

        expandableList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
            @Override
            public boolean onGroupClick(ExpandableListView expandableListView, View view, int i, long l) {
                Log.d(TAG, "onChildClick: " + "groupPosition = " + i);

                return false;
            }
        });

        expandableList.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {
            @Override
            public void onGroupCollapse(int i) {
                Log.d(TAG, "onChildClick: " + "groupPosition = " + i);
            }
        });

        expandableList.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
            @Override
            public void onGroupExpand(int i) {
                Log.d(TAG, "onChildClick: " + "groupPosition = " + i);
            }
        });
    }

    private void init_views() {
        expandableList = findViewById(R.id.expand_list);
        datas.clear();
        datas.addAll(ChapterLab.generateMockDatas());
        adapter = new ChapterAdapter(datas, this);
        expandableList.setAdapter(adapter);
    }
}
.3-9 案例开发之数据逻辑处理-慕课网就业班 2019-07-25 10_14(更多 IT 教程 1x)
package com.malugy.activitydemo.biz;

import android.content.Context;
import android.os.AsyncTask;

import com.malugy.activitydemo.bean.Chapter;

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


public class ChapterBiz {
    public void loadDatas(Context context, CallBack callback, boolean useCache) {
        AsyncTask<Boolean, Void, List<Chapter>> asyncTask = new AsyncTask<Boolean, Void, List<Chapter>>() {
            private Exception ex;

            @Override
            protected List<Chapter> doInBackground(Boolean... booleans) {
                boolean isUseCache = booleans[0];
                List<Chapter> chapterList = new ArrayList<>();

                try {
                    if (isUseCache) {
                        // load datas from db
                        //                    chapterList.addAll();
                    }
                    if (chapterList.isEmpty()) {
                        // load from net
                        List<Chapter> chapterList1FromNet = loadFormNet(context);
                        // cache to db
                        chapterList.addAll(chapterList1FromNet);
                    }
                } catch (Exception e) {
                    this.ex = e;
                    e.printStackTrace();
                }
                return chapterList;
            }

            @Override
            protected void onPostExecute(List<Chapter> chapters) {
                if (ex != null) {
                    callback.onFailed(ex);
                    return;
                }
                callback.onSuccess(chapters);
            }
        };
        asyncTask.execute(useCache);
    }

    private List<Chapter> loadFormNet(Context context) {
        return null;
    }

    public static interface CallBack {
        void onSuccess(List<Chapter> chapterList);

        void onFailed(Exception ex);
    }
}
.3-10 案例开发之网络数据处理-慕课网就业班 2019-07-25 10_14(更多 IT 教程 1x)
package com.malugy.activitydemo.biz;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import com.malugy.activitydemo.bean.Chapter;
import com.malugy.activitydemo.bean.ChapterItem;
import com.malugy.activitydemo.utils.HttpUtils;

import org.json.JSONArray;
import org.json.JSONObject;

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


public class ChapterBiz {
    public void loadDatas(Context context, CallBack callback, boolean useCache) {
        AsyncTask<Boolean, Void, List<Chapter>> asyncTask = new AsyncTask<Boolean, Void, List<Chapter>>() {
            private Exception ex;

            @Override
            protected List<Chapter> doInBackground(Boolean... booleans) {
                boolean isUseCache = booleans[0];
                List<Chapter> chapterList = new ArrayList<>();

                try {
                    if (isUseCache) {
                        // load datas from db
                        //                    chapterList.addAll();
                    }
                    if (chapterList.isEmpty()) {
                        // load from net
                        List<Chapter> chapterList1FromNet = loadFormNet(context);
                        // cache to db
                        chapterList.addAll(chapterList1FromNet);
                    }
                } catch (Exception e) {
                    this.ex = e;
                    e.printStackTrace();
                }
                return chapterList;
            }

            @Override
            protected void onPostExecute(List<Chapter> chapters) {
                if (ex != null) {
                    callback.onFailed(ex);
                    return;
                }
                callback.onSuccess(chapters);
            }
        };
        asyncTask.execute(useCache);
    }

    private List<Chapter> loadFormNet(Context context) {
        List<Chapter> chapterList = new ArrayList<>();

        String url = "http://192.168.57.137:8888/expandablelistview";
        // 1. 发请求
        String content = HttpUtils.doGet(url);
        Log.d("TAG", "loadFormNet: " + content);
        // 2. content -> List<Chapter>
        if (content != null) {
            chapterList = parseContent(content);
            Log.i("TAG", "parse finish: " + chapterList);
        }
        return chapterList;
    }

    private List<Chapter> parseContent(String content) {
        List<Chapter> chapterList = new ArrayList<>();

        try {
            JSONObject root = new JSONObject(content);
            int errorCode = root.optInt("errorCode");
            if (errorCode == 0) {
                JSONArray dataJsonArr = root.optJSONArray("data");
                for (int i = 0; i < dataJsonArr.length(); i++) {
                    // chapter
                    JSONObject chapterJsonObj = dataJsonArr.getJSONObject(i);
                    int id = chapterJsonObj.optInt("id");
                    String name = chapterJsonObj.optString("name");
                    Chapter chapter = new Chapter(id, name);
                    chapterList.add(chapter);

                    // parse chapter items
                    JSONArray childJsonArr = chapterJsonObj.optJSONArray("children");
                    if (childJsonArr != null) {
                        for (int j = 0; j < childJsonArr.length(); j++) {
                            JSONObject chapterItemJsonObj = childJsonArr.getJSONObject(j);
                            int cid = chapterItemJsonObj.optInt("id");
                            String cname = chapterItemJsonObj.optString("name");
                            ChapterItem chapterItem = new ChapterItem(cid, cname);
                            chapter.addChild(chapterItem);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return chapterList;
    }

    public static interface CallBack {
        void onSuccess(List<Chapter> chapterList);

        void onFailed(Exception ex);
    }
}
package com.malugy.activitydemo.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class HttpUtils {
    public static String doGet(String url_str) {
        HttpURLConnection conn = null;
        ByteArrayOutputStream baos = null;
        InputStream is = null;

        try {
            URL url = new URL(url_str);
            conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(5000);
            conn.setConnectTimeout(5000);
            conn.setRequestMethod("GET");
            if (conn.getResponseCode() == 200) {
                is = conn.getInputStream();
                baos = new ByteArrayOutputStream();

                int len = -1;
                byte[] buf = new byte[1024];
                while ((len = is.read(buf)) != -1) {
                    baos.write(buf, 0, len);
                }
                baos.flush();
                return baos.toString();
            }
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (baos != null) {
                    baos.close();
                }
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }
}
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.TextView;

import com.malugy.activitydemo.adapter.ChapterAdapter;
import com.malugy.activitydemo.bean.Chapter;
import com.malugy.activitydemo.bean.ChapterLab;
import com.malugy.activitydemo.biz.ChapterBiz;

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

public class ExpandableListViewBaseActivity extends AppCompatActivity {
    private static final String TAG = "ExpandableList";
    private ExpandableListView expandableList;
    private BaseExpandableListAdapter adapter;
    private List<Chapter> datas = new ArrayList<>();
    private ChapterBiz chapterBiz = new ChapterBiz();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_expandable_list_view_base);

        init_views();
        init_event();
        load_datas();
    }

    private void load_datas() {
        chapterBiz.loadDatas(this, new ChapterBiz.CallBack() {
            @Override
            public void onSuccess(List<Chapter> chapterList) {
                datas.addAll(chapterList);
                adapter.notifyDataSetChanged();
            }

            @Override
            public void onFailed(Exception ex) {
                ex.printStackTrace();
            }
        }, false);
    }

    private void init_event() {
        // ...
    }

    private void init_views() {
        expandableList = findViewById(R.id.expand_list);
        datas.clear();
//        datas.addAll(ChapterLab.generateMockDatas());
        adapter = new ChapterAdapter(datas, this);
        expandableList.setAdapter(adapter);
    }
}

.3-11 案例开发之 Biz 业务逻辑层实现-慕课网就业班 2019-07-25 10_14(更多 IT 教程 1x)
package com.malugy.activitydemo.biz;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import com.malugy.activitydemo.bean.Chapter;
import com.malugy.activitydemo.bean.ChapterItem;
import com.malugy.activitydemo.dao.ChapterDao;
import com.malugy.activitydemo.utils.HttpUtils;

import org.json.JSONArray;
import org.json.JSONObject;

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


public class ChapterBiz {
    private ChapterDao chapterDao = new ChapterDao();

    public void loadDatas(Context context, CallBack callback, boolean useCache) {
        AsyncTask<Boolean, Void, List<Chapter>> asyncTask = new AsyncTask<Boolean, Void, List<Chapter>>() {
            private Exception ex;

            @Override
            protected List<Chapter> doInBackground(Boolean... booleans) {
                boolean isUseCache = booleans[0];
                List<Chapter> chapterList = new ArrayList<>();

                try {
                    if (isUseCache) {
                        // load datas from db
                        //                    chapterList.addAll();
                        List<Chapter> chapters = chapterDao.loadFromDb(context);
                        Log.i("TAG", "chapters: " + chapters);
                        chapterList.addAll(chapters);
                    }
                    if (chapterList.isEmpty()) {
                        // load from net
                        List<Chapter> chapterList1FromNet = loadFormNet(context);
                        // cache to db
                        chapterDao.insert2Db(context, chapterList1FromNet);

                        // cache to db
                        chapterList.addAll(chapterList1FromNet);
                    }
                } catch (Exception e) {
                    this.ex = e;
                    e.printStackTrace();
                }
                return chapterList;
            }

            @Override
            protected void onPostExecute(List<Chapter> chapters) {
                if (ex != null) {
                    callback.onFailed(ex);
                    return;
                }
                callback.onSuccess(chapters);
            }
        };
        asyncTask.execute(useCache);
    }

    private List<Chapter> loadFormNet(Context context) {
        // ...
    }

    private List<Chapter> parseContent(String content) {
        // ...
    }

    public static interface CallBack {
        void onSuccess(List<Chapter> chapterList);

        void onFailed(Exception ex);
    }
}
package com.malugy.activitydemo.bean;

public class ChapterItem {
    private int id;
    private String name;
    private int pid; // 所属Chapter的id

    public static final String TABLE_NAME = "tb_chapter_item";
    public static final String COL_ID = "_id";
    public static final String COL_NAME = "name";
    public static final String COL_PID = "pid";

    // ...
}
package com.malugy.activitydemo.bean;

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

public class Chapter {
    private int id;
    private String name;
    public static final String TABLE_NAME="tb_chapter";
    public static final String COL_ID="_id";
    public static final String COL_NAME="name";
    // ...
}
package com.malugy.activitydemo.dao;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import com.malugy.activitydemo.bean.Chapter;
import com.malugy.activitydemo.bean.ChapterItem;

public class ChapterDbHelper extends SQLiteOpenHelper {
    private static final String DB_NAME = "db_chapter.db";
    private static final int VERSION = 1;
    private static ChapterDbHelper instance;

    public static synchronized ChapterDbHelper getInstance(Context context) {
        if (instance == null) {
            instance = new ChapterDbHelper(context.getApplicationContext());
        }
        return instance;
    }

    public ChapterDbHelper(Context context) {
        super(context, DB_NAME, null, VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table if not exists " + Chapter.TABLE_NAME + "(" +
                Chapter.COL_ID + " integer primary key, " +
                Chapter.COL_NAME + " varchar " +
                ")");
        db.execSQL("create table if not exists " + ChapterItem.TABLE_NAME + "(" +
                ChapterItem.COL_ID + " integer primary key, " +
                ChapterItem.COL_NAME + " varchar, " +
                ChapterItem.COL_PID + " integer " +
                ")");
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}
package com.malugy.activitydemo.dao;

import android.content.Context;

import com.malugy.activitydemo.bean.Chapter;

import java.util.List;

public class ChapterDao {
    public List<Chapter> loadFromDb(Context context) {
        return null;
    }

    public void insert2Db(Context context, List<Chapter> chapterList) {

    }
}
.3-13 案例开发之 UI 表示层实现-慕课网就业班 2019-07-25 10_14(更多 IT 教程 11)
package com.malugy.activitydemo.dao;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.malugy.activitydemo.bean.Chapter;
import com.malugy.activitydemo.bean.ChapterItem;

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

public class ChapterDao {
    public List<Chapter> loadFromDb(Context context) {
        ChapterDbHelper dbHelper = ChapterDbHelper.getInstance(context);
        SQLiteDatabase db = dbHelper.getWritableDatabase();

        List<Chapter> chapterList = new ArrayList<>();
        Chapter chapter = null;
        Cursor cursor = db.rawQuery("select * from " + Chapter.TABLE_NAME, null);
        while (cursor.moveToNext()) {
            chapter = new Chapter();
            int id_index = cursor.getColumnIndex(Chapter.COL_ID);
            int id = cursor.getInt(id_index);
            int name_idx = cursor.getColumnIndex(Chapter.COL_NAME);
            String name = cursor.getString(name_idx);
            chapter.setId(id);
            chapter.setName(name);
            chapterList.add(chapter);
        }
        cursor.close();

        ChapterItem chapterItem = null;
        for (Chapter tmpChapter : chapterList) {
            int pid = tmpChapter.getId();
            cursor = db.rawQuery("select * from " + ChapterItem.TABLE_NAME + " where " + ChapterItem.COL_PID + " = ? ", new String[]{pid + ""});
            while (cursor.moveToNext()) {
                chapterItem = new ChapterItem();
                int id_idx = cursor.getColumnIndex(ChapterItem.COL_ID);
                int id = cursor.getInt(id_idx);
                int name_idx = cursor.getColumnIndex(ChapterItem.COL_NAME);
                String name = cursor.getString(name_idx);
                chapterItem.setId(id);
                chapterItem.setName(name);
                chapterItem.setPid(pid);
                tmpChapter.addChild(chapterItem);
            }
            cursor.close();
        }

        return chapterList;
    }

    public void insert2Db(Context context, List<Chapter> chapterList) {
        if (context == null) {
            throw new IllegalArgumentException("context can not be null.");
        }
        if (chapterList == null || chapterList.isEmpty()) {
            return;
        }

        ChapterDbHelper dbHelper = ChapterDbHelper.getInstance(context);
        SQLiteDatabase db = dbHelper.getWritableDatabase();

        db.beginTransaction();

        ContentValues contentValues = null;
        for (Chapter chapter : chapterList) {
            contentValues = new ContentValues();
            contentValues.put(Chapter.COL_ID, chapter.getId());
            contentValues.put(Chapter.COL_NAME, chapter.getName());

            db.insertWithOnConflict(Chapter.TABLE_NAME,
                    null,
                    contentValues,
                    SQLiteDatabase.CONFLICT_REPLACE);

            List<ChapterItem> children = chapter.getChildren();
            for (ChapterItem chapterItem : children) {
                contentValues = new ContentValues();
                contentValues.put(ChapterItem.COL_ID, chapterItem.getId());
                contentValues.put(ChapterItem.COL_NAME, chapterItem.getName());
                contentValues.put(ChapterItem.COL_PID, chapter.getId());
                db.insertWithOnConflict(ChapterItem.TABLE_NAME,
                        null,
                        contentValues,
                        SQLiteDatabase.CONFLICT_REPLACE);
            }
        }
    }
}
package com.malugy.activitydemo.biz;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import com.malugy.activitydemo.bean.Chapter;
import com.malugy.activitydemo.bean.ChapterItem;
import com.malugy.activitydemo.dao.ChapterDao;
import com.malugy.activitydemo.utils.HttpUtils;

import org.json.JSONArray;
import org.json.JSONObject;

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


public class ChapterBiz {
    private ChapterDao chapterDao = new ChapterDao();

    public void loadDatas(Context context, CallBack callback, boolean useCache) {
        AsyncTask<Boolean, Void, List<Chapter>> asyncTask = new AsyncTask<Boolean, Void, List<Chapter>>() {
            private Exception ex;

            @Override
            protected List<Chapter> doInBackground(Boolean... booleans) {
                boolean isUseCache = booleans[0];
                List<Chapter> chapterList = new ArrayList<>();

                try {
                    if (isUseCache) {
                        // load datas from db
                        //                    chapterList.addAll();
                        List<Chapter> chapters = chapterDao.loadFromDb(context);
                        Log.i("TAG", "chapters: " + chapters);
                        chapterList.addAll(chapters);
                    }
                    if (chapterList.isEmpty()) {
                        // load from net
                        List<Chapter> chapterList1FromNet = loadFormNet(context);
                        // cache to db
                        chapterDao.insert2Db(context, chapterList1FromNet);

                        // cache to db
                        chapterList.addAll(chapterList1FromNet);
                    }
                } catch (Exception e) {
                    this.ex = e;
                    e.printStackTrace();
                }
                return chapterList;
            }

            @Override
            protected void onPostExecute(List<Chapter> chapters) {
                if (ex != null) {
                    callback.onFailed(ex);
                    return;
                }
                callback.onSuccess(chapters);
            }
        };
        asyncTask.execute(useCache);
    }

    private List<Chapter> loadFormNet(Context context) {
        List<Chapter> chapterList = new ArrayList<>();

        String url = "http://192.168.10.137:8888/expandablelistview";
        // 1. 发请求
        String content = HttpUtils.doGet(url);
        Log.d("TAG", "loadFormNet: " + content);
        // 2. content -> List<Chapter>
        if (content != null) {
            chapterList = parseContent(content);
            Log.i("TAG", "parse finish: " + chapterList);
        }
        return chapterList;
    }

    private List<Chapter> parseContent(String content) {
        List<Chapter> chapterList = new ArrayList<>();

        try {
            JSONObject root = new JSONObject(content);
            int errorCode = root.optInt("errorCode");
            if (errorCode == 0) {
                JSONArray dataJsonArr = root.optJSONArray("data");
                for (int i = 0; i < dataJsonArr.length(); i++) {
                    // chapter
                    JSONObject chapterJsonObj = dataJsonArr.getJSONObject(i);
                    int id = chapterJsonObj.optInt("id");
                    String name = chapterJsonObj.optString("name");
                    Chapter chapter = new Chapter(id, name);
                    chapterList.add(chapter);

                    // parse chapter items
                    JSONArray childJsonArr = chapterJsonObj.optJSONArray("children");
                    if (childJsonArr != null) {
                        for (int j = 0; j < childJsonArr.length(); j++) {
                            JSONObject chapterItemJsonObj = childJsonArr.getJSONObject(j);
                            int cid = chapterItemJsonObj.optInt("id");
                            String cname = chapterItemJsonObj.optString("name");
                            ChapterItem chapterItem = new ChapterItem(cid, cname);
                            chapter.addChild(chapterItem);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return chapterList;
    }

    public static interface CallBack {
        void onSuccess(List<Chapter> chapterList);

        void onFailed(Exception ex);
    }
}
package com.malugy.activitydemo.dao;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import com.malugy.activitydemo.bean.Chapter;
import com.malugy.activitydemo.bean.ChapterItem;

public class ChapterDbHelper extends SQLiteOpenHelper {
    private static final String DB_NAME = "db_chapter.db";
    private static final int VERSION = 1;
    private static ChapterDbHelper instance;

    public static synchronized ChapterDbHelper getInstance(Context context) {
        if (instance == null) {
            instance = new ChapterDbHelper(context.getApplicationContext());
        }
        return instance;
    }

    public ChapterDbHelper(Context context) {
        super(context, DB_NAME, null, VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table if not exists " + Chapter.TABLE_NAME + "(" +
                Chapter.COL_ID + " integer primary key, " +
                Chapter.COL_NAME + " varchar " +
                ")");
        db.execSQL("create table if not exists " + ChapterItem.TABLE_NAME + "(" +
                ChapterItem.COL_ID + " integer primary key, " +
                ChapterItem.COL_NAME + " varchar, " +
                ChapterItem.COL_PID + " integer " +
                ")");
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.BaseExpandableListAdapter;
import android.widget.Button;
import android.widget.ExpandableListView;
import android.widget.TextView;

import com.malugy.activitydemo.adapter.ChapterAdapter;
import com.malugy.activitydemo.bean.Chapter;
import com.malugy.activitydemo.bean.ChapterLab;
import com.malugy.activitydemo.biz.ChapterBiz;

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

public class ExpandableListViewBaseActivity extends AppCompatActivity {
    private static final String TAG = "ExpandableList";
    private ExpandableListView expandableList;
    private BaseExpandableListAdapter adapter;
    private List<Chapter> datas = new ArrayList<>();
    private ChapterBiz chapterBiz = new ChapterBiz();
    private Button refresh_btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_expandable_list_view_base);

        init_views();
        init_event();
        load_datas(true);
    }

    private void load_datas(boolean useCache) {
        chapterBiz.loadDatas(this, new ChapterBiz.CallBack() {
            @Override
            public void onSuccess(List<Chapter> chapterList) {
                datas.clear();
                datas.addAll(chapterList);
                adapter.notifyDataSetChanged();
            }

            @Override
            public void onFailed(Exception ex) {
                ex.printStackTrace();
            }
        }, useCache);
    }

    private void init_event() {
        refresh_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                load_datas(false);
            }
        });

        expandableList.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView expandableListView,
                                        View view, int i, int i1, long l) {
                Log.d(TAG, "onChildClick: " + "groupPosition = " + i + ", childPosition = " + i1);

                return false;
            }
        });

        expandableList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
            @Override
            public boolean onGroupClick(ExpandableListView expandableListView, View view, int i, long l) {
                Log.d(TAG, "onChildClick: " + "groupPosition = " + i);

                return false;
            }
        });

        expandableList.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {
            @Override
            public void onGroupCollapse(int i) {
                Log.d(TAG, "onChildClick: " + "groupPosition = " + i);
            }
        });

        expandableList.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
            @Override
            public void onGroupExpand(int i) {
                Log.d(TAG, "onChildClick: " + "groupPosition = " + i);
            }
        });
    }

    private void init_views() {
        expandableList = findViewById(R.id.expand_list);
        datas.clear();
//        datas.addAll(ChapterLab.generateMockDatas());
        adapter = new ChapterAdapter(datas, this);
        expandableList.setAdapter(adapter);
        refresh_btn = findViewById(R.id.load_data_refresh);
    }
}
<!-- activity_expandable_list_view_base.xml -->
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">

    <ExpandableListView
            android:id="@+id/expand_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:childDivider="#44ff0000"
            android:groupIndicator="@null"
            tools:context=".ExpandableListViewBaseActivity">
        <!--
            子元素的图标start-end, 显示范围
            android:childIndicator="@mipmap/indicator_expand"
            android:childIndicatorStart="0dp"
            android:childIndicatorEnd="48dp"
            比较常用自己写的imageview作为图标
            android:indicatorLeft="10dp"
            android:indicatorRight="40dp"
            android:groupIndicator="@drawable/group_indicator"-->

    </ExpandableListView>

    <Button
            android:id="@+id/load_data_refresh"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|right"
            android:layout_margin="16dp"
            android:text="刷新"/>
</FrameLayout>

04 BroadcastReceiver

第 1 章 课程介绍

第 2 章 广播基本流程


第 3 章 注册监听系统广播(重点)

.3-1 静态注册系统广播-慕课网就业班 2019-07-25 10_38(更多 IT 教程 11)



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

    <application
            android:allowBackup="true"
            android:dataExtractionRules="@xml/data_extraction_rules"
            android:fullBackupContent="@xml/backup_rules"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:networkSecurityConfig="@xml/network_security_config"
            android:requestLegacyExternalStorage="true"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.ActivityDemo"
            tools:targetApi="31">
        <!-- ... -->
        <activity
                android:name=".MainActivity"
                android:exported="true"
                android:label="main"
                android:launchMode="singleInstance">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

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

            <meta-data
                    android:name="android.app.lib_name"
                    android:value=""/>
        </activity>
        <!--静态注册广播接收器-->
        <receiver
                android:name=".ImoocBroadcastReceiver"
                android:exported="true">
            <!--接收哪些广播-->
            <intent-filter>
                <!--开机广播-->
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <!--当电量变低触发-->
                <action android:name="android.intent.action.BATTERY_LOW"/>
                <!--有应用被卸载时触发-->
                <action android:name="android.intent.action.PACKAGE_REMOVED"/>
                <!--有应用被安装时触发-->
                <action android:name="android.intent.action.PACKAGE_INSTALL"/>
                <!--数据类型-->
                <data android:scheme="package"/>
            </intent-filter>
        </receiver>
    </application>

</manifest>
package com.malugy.activitydemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

// 广播
public class ImoocBroadcastReceiver extends BroadcastReceiver {
    // 快捷输入: logt
    private static final String TAG = "ImoocBroadcastReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent != null) {
            // 接收到什么广播
            String action = intent.getAction();
            Log.i(TAG, "onReceive: " + action);
        }
    }
}
.3-4 动态注册系统广播(重点)-慕课网就业班 2019-07-25 10_38(更多 IT 教程 11)

亲测可,静态注册没成功(静态注册随着系统升级,逐渐被禁用了一部分,能用动态就用动态)

package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;

public class BroadcastDemoActivity extends AppCompatActivity {

    private ImoocBroadcastReceiver broadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcast_demo);

        broadcastReceiver = new ImoocBroadcastReceiver();
        // 接收哪些广播
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        intentFilter.addDataScheme("package");

        registerReceiver(broadcastReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 取消注册
        if (broadcastReceiver != null) {
            unregisterReceiver(broadcastReceiver);
        }
    }
}
3-8 生命周期-慕课网就业班 2019-07-25 10_38(更多 IT 教程 1x)

广播是在主线程执行

第 4 章 自定义广播(重点)

.4-1 自定义广播(重点)-慕课网就业班 2019-07-25 10_39(更多 IT 教程 11)


可以用在状态共享(登录、注册、支付)

package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class BroadcastSelfActivity extends AppCompatActivity {
    public static final String EXTRA_NAME = "broadcast_content";
    BroadcastReceiver broadcast_receiver;
    public static final String MY_ACTION = "com.malguy.activitydemo.testaction";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcast_self);

        EditText broadcast_edt = findViewById(R.id.broadcast_edt);
        Button broadcast_send = findViewById(R.id.broadcast_send);
        TextView broadacast_txt = findViewById(R.id.broadcast_txt);

        // 新建广播接收器
        broadcast_receiver = new ImoocBroadcastReceiver(broadacast_txt);

        // 添加action
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.intent.action.PACKAGE_REMOVED");
        intentFilter.addAction(MY_ACTION);

        // 注册广播接收器
        registerReceiver(broadcast_receiver, intentFilter);
        broadcast_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 新建广播
                Intent intent = new Intent(MY_ACTION);
                // 放入广播要携带的数据
                intent.putExtra(EXTRA_NAME, broadcast_edt.getText().toString());
                // 发送广播
                sendBroadcast(intent);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 取消注册
        if (broadcast_receiver != null) {
            unregisterReceiver(broadcast_receiver);
        }
    }
}
package com.malugy.activitydemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

// 广播
public class ImoocBroadcastReceiver extends BroadcastReceiver {
    public ImoocBroadcastReceiver() {
    }

    TextView textView;

    public ImoocBroadcastReceiver(TextView textView) {
        this.textView = textView;
    }

    // 快捷输入: logt
    private static final String TAG = "ImoocBroadcastReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent != null) {
            // 接收到什么广播
            String action = intent.getAction();
            Log.i(TAG, "onReceive: " + action);

            // 如果接收到我们自己发送的广播
            if (TextUtils.equals(action, BroadcastSelfActivity.MY_ACTION)) {
                // 获取广播携带的数据
                String content = intent.getStringExtra(BroadcastSelfActivity.EXTRA_NAME);
                if (textView != null) {
                    textView.setVisibility(View.VISIBLE);
                    textView.setText("接收到action: " + action + "\n接收到的内容: \n" + content);
                }
            }
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".BroadcastSelfActivity">

    <EditText
            android:id="@+id/broadcast_edt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:hint="请输入发送内容..."/>

    <Button
            android:id="@+id/broadcast_send"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:text="发送广播"/>

    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:text="收到的内容"/>

    <TextView
            android:id="@+id/broadcast_txt"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:visibility="gone"/>
</LinearLayout>
.4-3 APP 间的广播-慕课网就业班 2019-07-25 10_41(更多 IT 教程 1x)

05 Application 全局应用

第 1 章 课程介绍


第 2 章 Application 理论

第 3 章 Application 实操

.3-1 自定义 Application 类(重点)-慕课网就业班 2019-07-25 11_19(更多 IT 教程 11)



package com.malugy.activitydemo.application;

import android.app.Application;

public class MyApp extends Application {
}
.3-4 Application 的生命周期-慕课网就业班 2019-07-25 11_20(更多 IT 教程 11)

package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.malugy.activitydemo.service.MyService;

public class ApplicationActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "ApplicationActivity-app";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_application);

        Log.i(TAG, "onCreate: " + getApplication() + ", " + this);
        setTitle("ApplicationActivity");
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.app_btn:
                startActivity(new Intent(this, ApplicationTestActivity.class));
                break;
            case R.id.start_serivce:
                startService(new Intent(this, MyService.class));
                break;
        }
    }
}
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

public class ApplicationTestActivity extends AppCompatActivity {
    private static final String TAG = "ApplicationTestActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_application_test);
        Log.d(TAG, "onCreate: " + getApplication() + ", " + this);
        setTitle("ApplicationTestActivity");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }
}
package com.malugy.activitydemo.service;

import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
import android.util.TimeUtils;

import androidx.annotation.Nullable;

import java.util.concurrent.TimeUnit;

public class MyService extends IntentService {
    private static final String TAG = "MyService";

    public MyService() {
        super("MyService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.d(TAG, "onHandleIntent: " + getApplication() + ", " + this);
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: " + getApplication() + ", " + this);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".ApplicationActivity">

    <Button
            android:id="@+id/app_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="go to activity 2"/>

    <Button
            android:id="@+id/start_serivce"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="start_serivce"/>
</LinearLayout>

startservice 没反应, 在 manifest.xml 里添加


<service
        android:name=".service.MyService"
        android:enabled="true"
        android:label="MyService"
        android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.RESPOND_VIA_MESSAGE"/>

    </intent-filter>
</service>
.3-7 Application 的回调方法-慕课网就业班 2019-07-25 11_20(更多 IT 教程 1x)

不能做耗时操作

系统会尽可能长地将应用存入内存,所以就算应用滑走,application 还是可能存在

package com.malugy.activitydemo.application;

import android.app.Application;
import android.content.res.Configuration;
import android.util.Log;

import androidx.annotation.NonNull;

public class MyApp extends Application {
    private static final String TAG = "MyApp";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: " + this);
        Log.d(TAG, "onCreate: " + Thread.currentThread());
    }

    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        // 系统配置变更, 比如语言设置改变
        super.onConfigurationChanged(newConfig);
        Log.d(TAG, "onConfigurationChanged: " + newConfig);
    }

    @Override
    public void onLowMemory() {
        // 系统低内存, 可以释放掉自己的缓存
        super.onLowMemory();
        Log.d(TAG, "onLowMemory: ");
    }
}
.3-10 Application 对象的作用-慕课网就业班 2019-07-25 11_20(更多 IT 教程 1 )

android 里的对象是松散耦合的,彼此通过 intent 传递数据

<!-- activity_application.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".ApplicationActivity">

    <Button
            android:id="@+id/app_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="go to activity 2"/>

    <Button
            android:id="@+id/start_serivce"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="start_serivce"/>

    <Button
            android:id="@+id/set_uname"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="set_uname"/>

    <Button
            android:id="@+id/get_uname"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="get_uname"/>
</LinearLayout>
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.malugy.activitydemo.application.MyApp;

public class ApplicationTestActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "ApplicationTestActivity";

    // ...

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.test_get_app_uname:
                MyApp app = (MyApp) getApplication();
                Toast.makeText(this, "get uname" + app.getUname(), Toast.LENGTH_SHORT).show();
                break;
        }
    }
}
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.malugy.activitydemo.application.MyApp;
import com.malugy.activitydemo.service.MyService;

public class ApplicationActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "ApplicationActivity-app";

    // ...

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            // ...
            case R.id.set_uname:
                MyApp app = (MyApp) getApplication();
                app.setUname("imooc");
                Toast.makeText(this, "set uname" + app.getUname(), Toast.LENGTH_SHORT).show();
                break;
            case R.id.get_uname:
                MyApp app1 = (MyApp) getApplication();
                Toast.makeText(this, "get uname" + app1.getUname(), Toast.LENGTH_SHORT).show();
                break;
        }
    }
}
.3-13 Application 对象 vs 静态单例-慕课网就业班 2019-07-25 11_20(更多 IT 教程 1x)

otto 需要引依赖





04 步骤四:流行框架(上)

01 OkHttp 网络操作框架

第 1 章 课程简介


第 2 章 Okio

.2-1 Okio-简介-慕课网就业班 2019-07-25 12_00(更多 IT 教程 1x)



.2-2 Okio-ByteString-慕课网就业班 2019-07-25 11_42(更多 IT 教程 1x)





base64 可以将图片转换为 2 进制文本

    @Test
    public void t2() {
        String str = "this is a string";
        System.out.println(str);

        ByteString byteString = ByteString.encodeUtf8(str);
        System.out.println(byteString);

        String base64 = byteString.base64();
        System.out.println(base64);

        ByteString md5 = byteString.md5();
        System.out.println(md5);
        System.out.println(md5.hex());

        ByteString byteString1 = ByteString.decodeBase64(base64);
        System.out.println(byteString1);

        String sha1 = byteString1.sha1().hex();
        System.out.println(sha1);

        try {
            FileInputStream fileInputStream = new FileInputStream(IMG_PATH);
            ByteString read = ByteString.read(fileInputStream, fileInputStream.available());
            System.out.println(read);

            FileOutputStream fos = new FileOutputStream("out.png");
            read.write(fos);

            fileInputStream.close();
            fos.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
.2-3 Okio-Buffer-慕课网就业班 2019-07-25 11_42(更多 IT 教程 1x)







读取会弹出读取内容

public class ExampleUnitTest {

    // ...

    @Test
    public void t3() {
        Buffer buffer = new Buffer();
        System.out.println(buffer);

        // buffer可以写入, 也可以读出
        buffer.writeUtf8("abc");
        System.out.println(buffer);

        // 当buffer里还有数据
        while (!buffer.exhausted()) {
            try {
                System.out.println(buffer.readUtf8(1));
            } catch (EOFException e) {
                throw new RuntimeException(e);
            }
        }

        // 存其他类型数据
        for (int i = 0; i < 10; i++) {
            buffer.writeInt(i);
        }

        while (!buffer.exhausted()) {
            System.out.println(buffer.readInt());
        }

        // 读文件
        try {
            // 安卓的单元测试, 读取文件的目录是根目录(app/)
            buffer.readFrom(new FileInputStream("in.png"));
            System.out.println(buffer);
            buffer.writeTo(new FileOutputStream("out.png"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // source/sink buffer读写内容
        try {
            // Okio.buffer可以source读入, 也可以sink输出
            BufferedSource source =
                    Okio.buffer(Okio.source(new FileInputStream("hello.txt")));
            BufferedSink sink =
                    Okio.buffer(Okio.sink(new FileOutputStream("out.txt")));
            // 将source读取的内容, 存入sink
            source.readAll(sink);

            source.close();
            sink.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

第 3 章 OkHttp(重点)

.3-1 OkHttp-简介-慕课网就业班 2019-07-25 11_42(更多 IT 教程 1x)





.3-2 OkHttp-get-Request-慕课网就业班 2019-07-25 11_42(更多 IT 教程 1x)







package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.textclassifier.TextLinks;
import android.widget.TextView;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class OkHTTPActivitys extends AppCompatActivity {
    private static final String TAG = "OkHTTPActivitys";
    private final OkHttpClient client = new OkHttpClient();
    private TextView content_tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ok_httpactivitys);

        content_tv = findViewById(R.id.okhttp_tv_content);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.okhttp_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_get:
                get();
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    private void get() {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(new Runnable() {
            @Override
            public void run() {
                Request.Builder builder = new Request.Builder();
                builder.url("https://github.com/square/okhttp/blob/master/README.md");
                Request req = builder.build();
                Log.d(TAG, "run: " + req);

                Call call = client.newCall(req);
                try {
                    // 请求是同步的, 所以不能在UI线程进行
                    Response resp = call.execute();
                    if (resp.isSuccessful()) {
                        String content = resp.body().string();
                        // 改变ui要在ui线程
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                content_tv.setText(content);
                            }
                        });
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        executor.shutdown();
    }
}
<!-- okhttp_menu.xml -->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
            android:id="@+id/menu_get"
            android:title="Get"/>
</menu>
<!-- activity_ok_httpactivitys.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".OkHTTPActivitys">

    <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        <TextView
                android:id="@+id/okhttp_tv_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
    </ScrollView>
</RelativeLayout>
3-3 OkHttp-get-Response-慕课网就业班 2019-07-25 11_43(更多 IT 教程 1x)
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_get:
                get();
                break;
            case R.id.menu_resp:
                response();
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    private void response() {
        Request.Builder builder = new Request.Builder();
        builder.url("https://github.com/square/okhttp/blob/master/README.md");
        Request req = builder.build();
        Call call = client.newCall(req);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG, "onFailure: " + call);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d(TAG, "onResponse: " + call);
                int code = response.code();
                Headers headers = response.headers();
                String content = response.body().string();
                final StringBuilder buf = new StringBuilder();
                buf.append("code: " + code);
                buf.append("\nHeaders: " + headers);
                buf.append("\nbody: " + content);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        content_tv.setText(buf.toString());
                    }
                });
            }
        });
    }
.3-4 OkHttp-post-慕课网就业班 2019-07-25 11_43(更多 IT 教程 1x)
<!-- okhttp_menu.xml -->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
            android:id="@+id/menu_get"
            android:title="Get"/>
    <item
            android:id="@+id/menu_resp"
            android:title="Response"/>
    <item
            android:id="@+id/menu_post"
            android:title="Post"/>
</menu>
package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.textclassifier.TextLinks;
import android.widget.TextView;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.Okio;
import okio.Source;

public class OkHTTPActivitys extends AppCompatActivity {
    private static final String TAG = "OkHTTPActivitys";
    private final OkHttpClient client = new OkHttpClient();
    private TextView content_tv;
    private static final MediaType MEDIA_TYPE =
            MediaType.parse("application/json; charset=utf-8");
    private static final String POST_URl = "http://192.168.218.137:8888/student";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ok_httpactivitys);

        content_tv = findViewById(R.id.okhttp_tv_content);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.okhttp_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_get:
                get();
                break;
            case R.id.menu_resp:
                response();
                break;
            case R.id.menu_post:
                post();
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    private void post() {
        Request.Builder builder = new Request.Builder();
        builder.url(POST_URl);
        JSONObject student_json = new JSONObject();
        try {
            student_json.put("name", "hanmeimei");
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        builder.post(RequestBody.create(MEDIA_TYPE, String.valueOf(student_json)));
        Request req = builder.build();
        Call call = client.newCall(req);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String content = response.body().string();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            content_tv.setText(content);
                        }
                    });
                }
            }
        });
    }

    private void response() {
        // ...
    }

    private void get() {
        // ...
    }
}

02 EventBus 事件总线

第 1 章 课程介绍





第 2 章 案例介绍

.2-1 案例-慕课网就业班 2019-07-25 14_04(更多 IT 教程 1x)





package com.malugy.activitydemo;

import android.os.Bundle;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;

import androidx.appcompat.app.AppCompatActivity;

import android.view.View;
import android.widget.ImageView;

import androidx.core.view.WindowCompat;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;

import com.malugy.activitydemo.databinding.ActivityEventBusBaseDemoBinding;
import com.malugy.activitydemo.fragments.PublisherDialogFragment;

public class EventBusBaseDemoActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_bus_base_demo);
        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            // 显示 dialog fragment (publisher)
            @Override
            public void onClick(View view) {
                PublisherDialogFragment fragment = new PublisherDialogFragment();
                fragment.show(getSupportFragmentManager(), "publisher");
            }
        });
    }

    /**
     * 更新图片资源
     *
     * @param resId
     */
    private void setImageSrc(int resId) {
        ImageView iv = findViewById(R.id.emotionImageView);
        iv.setImageResource(resId);
    }

}
<!-- activity_event_bus_base_demo.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
                android:fitsSystemWindows="true"
                tools:context=".EventBusBaseDemoActivity">

    <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentBottom="true"
            android:layout_gravity="bottom|end"
            android:layout_marginEnd="@dimen/fab_margin"
            android:layout_marginBottom="16dp"
            android:tint="@android:color/white"
            app:srcCompat="@drawable/ic_add"/>

    <ImageView
            android:id="@+id/emotionImageView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:contentDescription="@null"/>
</RelativeLayout>
package com.malugy.activitydemo.fragments;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;

public class PublisherDialogFragment extends DialogFragment {
    private static final String TAG = "PublisherDialogFragment";

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Publisher");
        final String[] items = {
                "Success", "Failure"
        };
        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                switch (i) {
                    case 0:
                        // success

                        break;
                    case 1:
                        // failure
                        break;
                }
            }
        });
        return builder.create();
    }
}
.2-2 常规方案-监听器-慕课网就业班 2019-07-25 14_04(更多 IT 教程 1x)

package com.malugy.activitydemo;

import android.os.Bundle;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;

import androidx.appcompat.app.AppCompatActivity;

import android.view.View;
import android.widget.ImageView;

import androidx.core.view.WindowCompat;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;

import com.malugy.activitydemo.databinding.ActivityEventBusBaseDemoBinding;
import com.malugy.activitydemo.fragments.PublisherDialogFragment;

public class EventBusBaseDemoActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_bus_base_demo);
        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            // 显示 dialog fragment (publisher)
            @Override
            public void onClick(View view) {
                PublisherDialogFragment fragment = new PublisherDialogFragment();
                fragment.setEventListener(new PublisherDialogFragment.OnEventListener() {
                    @Override
                    public void onSuccess() {
                        setImageSrc(R.drawable.ic_alarm);
                    }

                    @Override
                    public void onFailure() {
                        setImageSrc(R.drawable.ic_alarm_off);
                    }
                });
                fragment.show(getSupportFragmentManager(), "publisher");
            }
        });
    }

    // ...
}
package com.malugy.activitydemo.fragments;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;

public class PublisherDialogFragment extends DialogFragment {
    private static final String TAG = "PublisherDialogFragment";
    private OnEventListener listener;

    public interface OnEventListener {
        void onSuccess();

        void onFailure();
    }

    public void setEventListener(OnEventListener listener) {
        this.listener = listener;
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Publisher");
        final String[] items = {
                "Success", "Failure"
        };
        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                switch (i) {
                    case 0:
                        // success
                        if (listener != null) {
                            listener.onSuccess();
                        }
                        break;
                    case 1:
                        // failure
                        if (listener != null) {
                            listener.onFailure();
                        }
                        break;
                }
            }
        });
        return builder.create();
    }
}

当前的问题:需要定义和传递接口(有很多回调函数)、事件接收者和发布者耦合度高

.2-3 常规方案-本地广播-慕课网就业班 2019-07-25 14_06(更多 IT 教程 1x)

package com.malugy.activitydemo.fragments;

import static com.malugy.activitydemo.EventBusBaseLocalBroadcastActivity.HANDLE_EVENT_ACTION;
import static com.malugy.activitydemo.EventBusBaseLocalBroadcastActivity.KEY;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;

public class PublisherDialogLocalBroadcastFragment extends DialogFragment {
    private static final String TAG = "PublisherDialogFragment";

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Publisher");
        final String[] items = {
                "Success", "Failure"
        };
        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                switch (i) {
                    case 0:
                        // success
                    {
                        Intent intent = new Intent();
                        intent.setAction(HANDLE_EVENT_ACTION);
                        intent.putExtra(KEY, true);
                        LocalBroadcastManager.getInstance(getActivity())
                                .sendBroadcast(intent);
                    }
                    break;
                    case 1:
                        // failure
                    {
                        Intent intent = new Intent();
                        intent.setAction(HANDLE_EVENT_ACTION);
                        intent.putExtra(KEY, false);
                        LocalBroadcastManager.getInstance(getActivity())
                                .sendBroadcast(intent);
                    }
                    break;
                }
            }
        });
        return builder.create();
    }
}
package com.malugy.activitydemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.malugy.activitydemo.fragments.PublisherDialogFragment;
import com.malugy.activitydemo.fragments.PublisherDialogLocalBroadcastFragment;

public class EventBusBaseLocalBroadcastActivity extends AppCompatActivity {
    public static final String HANDLE_EVENT_ACTION = "handle_event_action";
    public static final String KEY = "status";
    private final BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (HANDLE_EVENT_ACTION.equals(action)) {
                boolean status = intent.getBooleanExtra(KEY, false);
                if (status) {
                    setImageSrc(R.drawable.ic_alarm);
                } else {
                    setImageSrc(R.drawable.ic_alarm_off);
                }
            }
        }
    };

    @Override
    protected void onStart() {
        super.onStart();
        IntentFilter filter = new IntentFilter(HANDLE_EVENT_ACTION);
        LocalBroadcastManager.getInstance(this)
                .registerReceiver(receiver, filter);
    }

    @Override
    protected void onStop() {
        super.onStop();
        LocalBroadcastManager.getInstance(this)
                .unregisterReceiver(receiver);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_bus_base_demo);
        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            // 显示 dialog fragment (publisher)
            @Override
            public void onClick(View view) {
                PublisherDialogLocalBroadcastFragment fragment =
                        new PublisherDialogLocalBroadcastFragment();

                fragment.show(getSupportFragmentManager(), "publisher");
            }
        });
    }

    /**
     * 更新图片资源
     *
     * @param resId
     */
    private void setImageSrc(int resId) {
        ImageView iv = findViewById(R.id.emotionImageView);
        iv.setImageResource(resId);
    }

}

第 3 章 基础应用





    implementation("org.greenrobot:eventbus:3.1.1")
package com.malugy.activitydemo;

import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.malugy.activitydemo.Event.FailureEvent;
import com.malugy.activitydemo.Event.SuccessEvent;
import com.malugy.activitydemo.fragments.PublisherDialogEventBusFragment;
import com.malugy.activitydemo.fragments.PublisherDialogFragment;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;

public class EventBusBaseAppActivity extends AppCompatActivity {
    @Override
    protected void onStart() {
        super.onStart();
        // 注册
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }

    // 注册回调函数
    @Subscribe
    public void onSuccessEvent(SuccessEvent event) {
        setImageSrc(R.drawable.ic_alarm);
    }

    @Subscribe
    public void onFailureEvent(FailureEvent event) {
        setImageSrc(R.drawable.ic_alarm_off);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_bus_base_demo);
        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            // 显示 dialog fragment (publisher)
            @Override
            public void onClick(View view) {
                PublisherDialogEventBusFragment fragment
                        = new PublisherDialogEventBusFragment();
                fragment.show(getSupportFragmentManager(), "publisher");
            }
        });
    }

    /**
     * 更新图片资源
     *
     * @param resId
     */
    private void setImageSrc(int resId) {
        ImageView iv = findViewById(R.id.emotionImageView);
        iv.setImageResource(resId);
    }

}
package com.malugy.activitydemo.fragments;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment; ;

import com.malugy.activitydemo.Event.FailureEvent;
import com.malugy.activitydemo.Event.SuccessEvent;

import org.greenrobot.eventbus.EventBus;

public class PublisherDialogEventBusFragment extends DialogFragment {
    private static final String TAG = "PublisherDialogEventBusFragment";

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Publisher");
        final String[] items = {
                "Success", "Failure"
        };
        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                switch (i) {
                    case 0:
                        // success
                        EventBus.getDefault().post(new SuccessEvent());
                        break;
                    case 1:
                        // failure
                        EventBus.getDefault().post(new FailureEvent());
                        break;
                }
            }
        });
        return builder.create();
    }
}
package com.malugy.activitydemo.Event;

public class SuccessEvent {
}
package com.malugy.activitydemo.Event;

public class FailureEvent {
}

第 4 章 EventBus 线程模式(重点)

.4-1 线程模式-简介-慕课网就业班 2019-07-25 14_16(更多 IT 教程 1x)


.4-2 线程模式-PSOTING-慕课网就业班 2019-07-25 14_17(更多 IT 教程 1x)

发布事件时的线程在哪,就在哪执行回调

package com.malugy.activitydemo;

import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.malugy.activitydemo.Event.FailureEvent;
import com.malugy.activitydemo.Event.PostingEvent;
import com.malugy.activitydemo.Event.SuccessEvent;
import com.malugy.activitydemo.fragments.PublisherDialogEventBusFragment;
import com.malugy.activitydemo.fragments.PublisherDialogFragment;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class EventBusBaseAppActivity extends AppCompatActivity {
    // ...

    @Subscribe(threadMode = ThreadMode.POSTING)
    public void onPostingEvent(PostingEvent event) {
        final String threadInfo = Thread.currentThread().toString();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                setPublisherThreadInfo(event.threadInfo);
                setSubscriberThreadInfo(threadInfo);
            }
        });
    }

    private void setPublisherThreadInfo(String threadInfo) {
        setTextView(R.id.publisherThreadTextView, threadInfo);
    }

    private void setSubscriberThreadInfo(String threadInfo) {
        setTextView(R.id.subscriberThreadTextView, threadInfo);
    }

    // 应该运行在UI线程
    private void setTextView(int resId, String text) {
        TextView view = findViewById(resId);
        view.setText(text);
        // 透明度更换
        view.setAlpha(.5f);
        view.animate().alpha(1).start();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
    }

    // ...

}
package com.malugy.activitydemo.fragments;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;;

import com.malugy.activitydemo.Event.FailureEvent;
import com.malugy.activitydemo.Event.PostingEvent;
import com.malugy.activitydemo.Event.SuccessEvent;

import org.greenrobot.eventbus.EventBus;

public class PublisherDialogEventBusFragment extends DialogFragment {
    private static final String TAG = "PublisherDialogEventBusFragment";

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Publisher");
        final String[] items = {
                "Success", "Failure", "Posting"
        };
        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                switch (i) {
                    // ...
                    case 2:
                        // failure
                        if (Math.random() > .5) {
                            new Thread("posting-002") {
                                @Override
                                public void run() {
                                    EventBus.getDefault().post(new PostingEvent(
                                            Thread.currentThread().toString()
                                    ));
                                }
                            }.start();
                        } else {
                            EventBus.getDefault().post(new PostingEvent(
                                    Thread.currentThread().toString()
                            ));
                        }
                        break;
                }
            }
        });
        return builder.create();
    }
}
package com.malugy.activitydemo.Event;

public class PostingEvent {
    public final String threadInfo;

    public PostingEvent(String threadInfo) {
        this.threadInfo = threadInfo;
    }
}
<!-- activity_event_bus_base_demo.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
                android:fitsSystemWindows="true"
                tools:context=".EventBusBaseDemoActivity">

    <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_alignParentBottom="true"
            android:layout_gravity="bottom|end"
            android:layout_marginEnd="@dimen/fab_margin"
            android:layout_marginBottom="16dp"
            android:tint="@android:color/white"
            app:srcCompat="@drawable/ic_add"/>

    <ImageView
            android:id="@+id/emotionImageView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:contentDescription="@null"/>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:orientation="vertical">

        <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Publisher:"/>

        <TextView
                android:id="@+id/publisherThreadTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#00f"
                android:textSize="20sp"/>

        <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Subscriber:"/>

        <TextView
                android:id="@+id/subscriberThreadTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="#00f"
                android:textSize="20sp"/>
    </LinearLayout>
</RelativeLayout>
.4-3 线程模式-MAIN-慕课网就业班 2019-07-25 14_18(更多 IT 教程 1x)



package com.malugy.activitydemo.fragments;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;;

import com.malugy.activitydemo.Event.FailureEvent;
import com.malugy.activitydemo.Event.MainEvent;
import com.malugy.activitydemo.Event.PostingEvent;
import com.malugy.activitydemo.Event.SuccessEvent;

import org.greenrobot.eventbus.EventBus;

public class PublisherDialogEventBusFragment extends DialogFragment {
    private static final String TAG = "PublisherDialogEventBusFragment";

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Publisher");
        final String[] items = {
                "Success", "Failure", "Posting", "Main"
        };
        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                switch (i) {
                    // ...
                    case 3:
                        if (Math.random() > .5) {
                            new Thread("working-thread") {
                                @Override
                                public void run() {
                                    EventBus.getDefault().post(new MainEvent(
                                            Thread.currentThread().toString()
                                    ));
                                }
                            }.start();
                        } else {
                            EventBus.getDefault().post(
                                    new MainEvent(Thread.currentThread().toString()));
                        }
                        break;
                }
            }
        });
        return builder.create();
    }
}
package com.malugy.activitydemo.Event;

public class MainEvent {
    public final String threadInfo;

    public MainEvent(String threadInfo) {
        this.threadInfo = threadInfo;
    }
}
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMainEvent(MainEvent event) {
        setPublisherThreadInfo(event.threadInfo);
        setSubscriberThreadInfo(Thread.currentThread().toString());
    }
.4-4 线程模式-MAIN_ORDERED-慕课网就业班 2019-07-25 14_17(更多 IT 教程 1x)

main 模式,如果回调函数执行耗时操作会被阻塞,而 main ordered 运行回调同时立刻执行后续操作


package com.malugy.activitydemo.Event;

public class MainOrderedEvent {
    public final String threadInfo;

    public MainOrderedEvent(String threadInfo) {
        this.threadInfo = threadInfo;
    }
}
    @Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
    public void onMainOrderedEvent(MainOrderedEvent event) {
        Log.d("TAG", "onMainOrderedEvent enter: " + SystemClock.uptimeMillis());
        setPublisherThreadInfo(event.threadInfo);
        setSubscriberThreadInfo(Thread.currentThread().toString());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        Log.d("TAG", "onMainOrderedEvent exit: " + SystemClock.uptimeMillis());
    }
 case 4:
    Log.d(TAG, "onClick before: " + SystemClock.uptimeMillis());
    EventBus.getDefault().post(new MainOrderedEvent(Thread.currentThread().toString()));
    Log.d(TAG, "onClick after: " + SystemClock.uptimeMillis());
    break;
.4-5 线程模式-BACKGROUND-慕课网就业班 2019-07-25 14_17(更多 IT 教程 1x)


package com.malugy.activitydemo.Event;

public class BackgroundEvent {
    public final String threadInfo;

    public BackgroundEvent(String threadInfo) {
        this.threadInfo = threadInfo;
    }
}
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onBackgroundEvent(BackgroundEvent event) {
        String threadInfo = Thread.currentThread().toString();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                setPublisherThreadInfo(event.threadInfo);
                setSubscriberThreadInfo(threadInfo);
            }
        });
    }
case 5: {
    if (Math.random() > .5) {
        // 非ui线程发布
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(new Runnable() {
            @Override
            public void run() {
                EventBus.getDefault().post(
                        new BackgroundEvent(Thread.currentThread().toString()));
            }
        });
        pool.shutdown();
    } else {
        EventBus.getDefault().post(
                new BackgroundEvent(Thread.currentThread().toString()));
    }
}
break;
.4-6 线程模式-ASYNC-慕课网就业班 2019-07-25 15_35(更多 IT 教程 1x)

一定会运行在和发布线程不同的线程上,可以做耗时操作

package com.malugy.activitydemo.Event;

public class AsyncEvent {
    public final String threadInfo;

    public AsyncEvent(String threadInfo) {
        this.threadInfo = threadInfo;
    }
}
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onAsyncEvent(AsyncEvent event){
        String threadInfo = Thread.currentThread().toString();
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                setPublisherThreadInfo(event.threadInfo);
                setSubscriberThreadInfo(threadInfo);
            }
        });
    }
case 6: {
    if (Math.random() > .5) {
        // 非ui线程发布
        ExecutorService pool = Executors.newFixedThreadPool(1);
        pool.submit(new Runnable() {
            @Override
            public void run() {
                EventBus.getDefault().post(
                        new AsyncEvent(Thread.currentThread().toString()));
            }
        });
        pool.shutdown();
    } else {
        EventBus.getDefault().post(
                new AsyncEvent(Thread.currentThread().toString()));
    }
}
break;

第 5 章 EventBus 扩展知识

5-1 粘性事件-慕课网就业班 2019-07-25 15_36(更多 IT 教程 1x)


package com.malugy;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.malugy.activitydemo.Event.StickyMsgEvent;
import com.malugy.activitydemo.R;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;

public class EventBusStickySkipToActivity extends AppCompatActivity {
    @Override
    protected void onStart() {
        super.onStart();
        // 注册
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(sticky = true)
    public void onStickyMsgEvent(StickyMsgEvent event) {
        setTitle(event.message);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_bus_sticky_skip_to);
    }
}
package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

import com.malugy.EventBusStickySkipToActivity;
import com.malugy.activitydemo.Event.StickyMsgEvent;

import org.greenrobot.eventbus.EventBus;

public class EventbusStickyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_eventbus_stiky);
    }

    // 创建OptionMenu
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // 加载菜单资源
        getMenuInflater().inflate(R.menu.sticky, menu);
        // 让菜单显示
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        // 获取被点击的item的id
        switch (item.getItemId()) {
            case R.id.action_sticky:
                // 直接发布sticky事件
                EventBus.getDefault().postSticky(new StickyMsgEvent("sticky-msg-content"));
                startActivity(new Intent(this, EventBusStickySkipToActivity.class));
                break;
        }
        return true;
    }
}
package com.malugy.activitydemo.Event;

public class StickyMsgEvent {
    public final String message;

    public StickyMsgEvent(String message) {
        this.message = message;
    }
}
<!-- sticky.xml -->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
            android:id="@+id/action_sticky"
            android:orderInCategory="100"
            android:title="sticky"
            app:showAsAction="never"/>
</menu>
.5-2 配置-慕课网就业班 2019-07-25 15_36(更多 IT 教程 1x)

eventbus 订阅事件的函数没有被使用,因为底层是通过反射来执行的
如果 minifyEnabled 为 true,则会在打包时尽量压缩包大小,而我们没有使用的函数会被删除(比如订阅事件的函数)

需要配置混淆规则

// proguard-rules.pro
-keepattributes *Annotation*
-keepclasseswithmembers class * {
    @org.greenrobot.eventbus.Subscribe ;
}
-keep enum org.greenrobot.eventbus.ThreadMode {*;}

# only required if you use AsyncExecutor
-keepclasseswithmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent{
     (java.lang.Throwable);
}

03 RecyclerView 列表流行控件

第 1 章 课程介绍

第 2 章 RecyclerView 简介

.2-1 RecyclerView 工作流程-慕课网就业班



有3种默认管理器,对应不同效果

.2-2 配合相关类-慕课网就业班






第 3 章 RecyclerView 数据显示(重点)

.3-1 项目案例介绍-慕课网就业班 2019-07-25 15_45(更多 IT 教程 1x)

3-2 引用 RecyclerView-慕课网就业班 2019-07-25 15_44(更多 IT 教程 1x)

以前需要引入

package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.view.View;

public class RecycleViewMainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycle_view_main);

        recyclerView = this.findViewById(R.id.recycle_view);
        // 设置布局
        LinearLayoutManager manager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(manager);
        // 设置适配器

    }

    public void onAddDataClick(View v) {

    }
}
<!-- activity_recycle_view_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".RecycleViewMainActivity">

    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onAddDataClick"
            android:text="添加数据"/>

    <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycle_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

    </androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
.3-4 线性布局-慕课网就业班 2019-07-25 15_45(更多 IT 教程 1x)
<!-- recycle_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:layout_margin="4dp"
              android:background="#a4d3ee"
              android:orientation="horizontal">

    <ImageView
            android:id="@+id/cycle_iv"
            android:layout_width="88dp"
            android:layout_height="88dp"
            android:scaleType="fitXY"/>

    <TextView
            android:id="@+id/cycle_tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="8dp"
            android:gravity="center_vertical"
            android:textColor="#fff"
            android:textSize="22sp"/>
</LinearLayout>
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.view.View;

import com.malugy.activitydemo.adapter.MyRecycleViewAdapter;

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

public class RecycleViewMainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private MyRecycleViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycle_view_main);

        recyclerView = this.findViewById(R.id.recycle_view);
        // 设置布局
        LinearLayoutManager manager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(manager);
        // 设置适配器
        adapter=new MyRecycleViewAdapter(this);
        recyclerView.setAdapter(adapter);
    }

    public void onAddDataClick(View v) {
        List<String> data = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            String s = "第" + i + "条数据";
            data.add(s);
        }
        adapter.setDatas(data);
    }
}
package com.malugy.activitydemo.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.malugy.activitydemo.R;

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

public class MyRecycleViewAdapter
        extends RecyclerView.Adapter<MyRecycleViewAdapter.MyViewHolder> {

    private List<String> datas;
    private Context context;

    public MyRecycleViewAdapter(Context context) {
        this.context = context;
        datas = new ArrayList<>();
    }

    public List<String> getDatas() {
        return datas;
    }

    public void setDatas(List<String> datas) {
        this.datas = datas;
        // 通知视图更新
        notifyDataSetChanged();
    }

    // 创建并返回viewholder
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
        return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.recycle_item, parent, false));
    }

    // viewholder绑定数据
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.iv.setImageResource(getIcon(position));
        holder.tv.setText(datas.get(position));
    }

    // 返回数据个数
    @Override
    public int getItemCount() {
        return datas.size();
    }

    private int getIcon(int position) {
        switch (position % 5) {
            case 0:
                return R.mipmap.a;
            case 1:
                return R.mipmap.b;
            case 2:
                return R.mipmap.c;
            case 3:
                return R.mipmap.d;
            case 4:
                return R.mipmap.e;
        }
        return 0;
    }

    class MyViewHolder extends RecyclerView.ViewHolder {
        ImageView iv;
        TextView tv;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            iv = itemView.findViewById(R.id.cycle_iv);
            tv = itemView.findViewById(R.id.cycle_tv);
        }
    }
}

如果false没加会报错

.3-5 反向数据-慕课网就业班 2019-07-25 15_50(更多 IT 教程 1x)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycle_view_main);

        recyclerView = this.findViewById(R.id.recycle_view);
        // 设置布局
        LinearLayoutManager manager = new LinearLayoutManager(this);
        // 横向排列item view
        manager.setOrientation(LinearLayoutManager.HORIZONTAL);
        // 数据反向
        manager.setReverseLayout(true);
        recyclerView.setLayoutManager(manager);
        // 设置适配器
        adapter = new MyRecycleViewAdapter(this);
        recyclerView.setAdapter(adapter);
    }
.3-6 网格布局-慕课网就业班 2019-07-25 15_46(更多 IT 教程 1x)
<!-- activity_recycle_view_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".RecycleViewMainActivity">

    ...

    <Button
            android:id="@+id/cycle_change"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="切换布局"/>

    ...
</LinearLayout>
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.cycle_add: {
                List<String> data = new ArrayList<>();
                for (int i = 0; i < 20; i++) {
                    String s = "第" + i + "条数据";
                    data.add(s);
                }
                adapter.setDatas(data);
                break;
            }
            case R.id.cycle_change: {
                if (recyclerView.getLayoutManager().getClass() == LinearLayoutManager.class) {
                    // 转为网格布局
                    // 参数2: 每行有几列
                    GridLayoutManager manager = new GridLayoutManager(this, 2);
                    recyclerView.setLayoutManager(manager);
                }
                break;
            }
        }
    }
.3-7 瀑布流布局-慕课网就业班 2019-07-25 15_45(更多 IT 教程 1x)
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.cycle_add: {
                List<String> data = new ArrayList<>();
                for (int i = 0; i < 20; i++) {
                    String s = "第" + i + "条数据";
                    data.add(s);
                }
                adapter.setDatas(data);
                break;
            }
            case R.id.cycle_change: {
                if (recyclerView.getLayoutManager().getClass() == LinearLayoutManager.class) {
                    // 转为网格布局
                    // 参数2: 每行有几列
                    GridLayoutManager manager = new GridLayoutManager(this, 2);
                    recyclerView.setLayoutManager(manager);
                }else if(recyclerView.getLayoutManager().getClass() == GridLayoutManager.class){
                    // 转为瀑布流布局
                    // 参数: 1. 每行有几列; 2. 横向或竖向展示
                    StaggeredGridLayoutManager manager=new StaggeredGridLayoutManager(
                            2,StaggeredGridLayoutManager.VERTICAL);
                    recyclerView.setLayoutManager(manager);
                }else {
                    LinearLayoutManager manager=new LinearLayoutManager(this);
                    recyclerView.setLayoutManager(manager);
                }
                break;
            }
        }
package com.malugy.activitydemo.adapter;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;

import com.malugy.activitydemo.R;

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

public class MyRecycleViewAdapter
        extends RecyclerView.Adapter<MyRecycleViewAdapter.MyViewHolder> {

    private List<String> datas;
    private Context context;
    RecyclerView recyclerView;

    public MyRecycleViewAdapter(Context context, RecyclerView recyclerView) {
        this.context = context;
        datas = new ArrayList<>();
        this.recyclerView = recyclerView;
    }

    // ...

    // viewholder绑定数据
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.iv.setImageResource(getIcon(position));
        holder.tv.setText(datas.get(position));
        // 只在瀑布流布局中设置随机高度
        if (recyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, getRandomHeight());
            holder.tv.setLayoutParams(params);
        }else {
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            holder.tv.setLayoutParams(params);
        }
    }

    // 返回不同的item高度
    private int getRandomHeight() {
        return (int) (Math.random() * 1000);
    }

    // ...
}

04 Glide 图片流行框架

第 1 章 课程介绍


第 2 章 原生代码加载图片

.2-1 原生代码加载网络图片-慕课网就业班 2019-07-25 15_53(更多 IT 教程 1x)
package com.malugy.activitydemo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ImageView;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;

public class GlideNativeLoadImgActivity extends AppCompatActivity implements View.OnClickListener {
    ImageView iv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_glide_native_load_img);
        iv = findViewById(R.id.native_loadimg);
    }

    @Override
    public void onClick(View view) {
        loadUrlImg("http://192.168.87.137:8888/out.png");
    }

    // 加载网络图片
    private void loadUrlImg(String path) {
        iv.setImageResource(R.mipmap.loading);
        // 1. 地址
        // 2. 根据地址把图片转换为可加载对象
        // 3. 展示到imageview
        new Thread() {
            @Override
            public void run() {
                super.run();
                Message message = new Message();
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        // 获取数据流
                        InputStream in = conn.getInputStream();
                        // 可被imageview展示的对象
                        Bitmap bitmap = BitmapFactory.decodeStream(in);
                        message.obj = bitmap;
                        message.what = 200;
                    } else {
                        message.what = code;
                    }
                } catch (MalformedURLException e) {
                    message.what = -1;
                    throw new RuntimeException(e);
                } catch (ProtocolException e) {
                    message.what = -1;
                    throw new RuntimeException(e);
                } catch (IOException e) {
                    message.what = -1;
                    throw new RuntimeException(e);
                } finally {
                    handler.sendMessage(message);
                }
            }
        }.start();
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
                case 200:
                    Bitmap bitmap = (Bitmap) msg.obj;
                    iv.setImageBitmap(bitmap);
                    break;
                default:
                    iv.setImageResource(R.mipmap.loader_error);
                    break;
            }
        }
    };
}
<!-- activity_glide_native_load_img.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".GlideNativeLoadImgActivity">

    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="加载图片"/>

    <ImageView
            android:id="@+id/native_loadimg"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
</LinearLayout>
.2-2 注意事项-慕课网就业班 2019-07-25 15_52(更多 IT 教程 1x)

如果加载的图片太大,会崩溃(内存溢出)

加载 http 的图片(我加载 http 没遇到问题)


第 3 章 Glide 加载图片(重点)

.3-1 Glide 基本用法-慕课网就业班 2019-07-25 15_52(更多 IT 教程 1x)


    implementation("com.github.bumptech.glide:glide:4.8.0")
    annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1'

    private void loadUrlImgGlide(String path) {
        Glide.with(this)
                .load(path)
                .into(iv);
    }
.3-2 Glide 配置的使用-慕课网就业班 2019-07-25 15_52(更多 IT 教程 1x)

    private void loadUrlImgGlide(String path) {
        // 配置Glide
        RequestOptions options = new RequestOptions()
                // 图片加载中
                .placeholder(R.mipmap.loading)
                // 图片加载失败
                .error(R.mipmap.loader_error)
                // 图片圆角
                .circleCrop();

        Glide.with(this)
                .load(path)
                // 应用配置
                .apply(options)
                .into(iv);
    }
.3-4 Glide 高级用法-慕课网就业班 2019-07-25 15_54(更多 IT 教程 1x)
package com.malugy.activitydemo.common;

import com.bumptech.glide.request.RequestOptions;
import com.malugy.activitydemo.R;

public class GlideBaseOptions {
    public static RequestOptions baseOptions() {
        return new RequestOptions()
                // 图片加载中
                .placeholder(R.mipmap.loading)
                // 图片加载失败
                .error(R.mipmap.loader_error);
    }

    public static RequestOptions circleCropOptions() {
        return baseOptions()
                // 图片圆角
                .circleCrop();
    }
}



失败了,没有生成 glideapp

package com.malugy.activitydemo.common;

import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;

// 生成glideApp对象
@GlideModule
public class MyAppGlideModule extends AppGlideModule {

}
package com.malugy.activitydemo.common;

import com.bumptech.glide.annotation.GlideExtension;
import com.bumptech.glide.annotation.GlideOption;
import com.bumptech.glide.request.RequestOptions;
import com.malugy.activitydemo.R;

@GlideExtension
public class MyGlideExtension {
    private MyGlideExtension() {

    }

    /**
     * 全局统一配置
     *
     * @param options
     */
    @GlideOption
    public static void injectOptions(RequestOptions options) {
        options.placeholder(R.mipmap.loading)
                // 图片加载失败
                .error(R.mipmap.loader_error)
                .circleCrop();
    }
}
    private void glideAppLoadUrlImage(String img) {
        GlideApp.with(this)
                .load(img)
                .into(iv);
    }

05 GreenDao 数据库框架

第 1 章 课程介绍




第 2 章 使用前的准备

.2-1 GreenDao 的引入-慕课网就业班 2019-07-25 15_57(更多 IT 教程 1x)
implementation 'org.greenrobot:greendao:3.3.0'
.2-2 创建数据库会话-慕课网就业班 2019-07-25 15_57(更多 IT 教程 1x)


依赖有问题,放弃

05 步骤五:流行框架(下)

01 极光推送

第 1 章 课程介绍

02 WebView 浏览器组件

第 1 章 课程介绍与学前扫盲

.1-1 WebView 简介-慕课网就业班 2019-07-25 16_19(更多 IT 教程 1x)



1-2 学前扫盲_网页的组成-慕课网就业班 2019-07-25 16_20(更多 IT 教程 1 )

第 2 章 WebView 常用方法(重点)

.2-1 加载网页的四种方式-慕课网就业班 2019-07-25 16_20(更多 IT 教程 1x)


chrome://inspect/#devices

点击inspect

package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class WebviewActivity extends AppCompatActivity {
    public static final String iURL = "https://www.bing.com/";
    private WebView wv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);
        wv = findViewById(R.id.wv);

        // 可以在浏览器上调试
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            WebView.setWebContentsDebuggingEnabled(true);
        }

        // 打开网络上的html
//        wv.loadUrl(iURL);
        // 打开手机上的html
//        loadFile();
        // 打开项目assets里的html
//        wv.loadUrl("file:///android_asset/a.html");
        // 加载时传递header
//        loadWithHeader();
        // 加载富文本
//        wv.loadData("<h1>hello world</h1>", "text/html", "utf-8");
        // baseurl -> src=baseurl+src
        // historyurl -> 跳转到新页面,点击后退会到达historyurl
        // 这里使用historyurl无效,原因不明
        wv.loadDataWithBaseURL(
                "http://192.168.226.137:8888",
                "<img src=\"/out.png\" /><a href=\"https://www.bing.com\">history</a>",
                "text/html", "utf-8", "https://www.baidu.com");
        wv.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                // 在这里写没生效
//                loadWithHeader();
                return super.shouldOverrideUrlLoading(view, url);
            }
        });
    }

    public void onBack(View v) {
        // 网页回退
        wv.goBack();
    }

    private void loadWithHeader() {
        Map<String, String> req_headers = new HashMap<>();
        req_headers.put("android-webview-demo", "testReqHeaders");
        wv.loadUrl("http://192.168.226.137:8888/a.html", req_headers);
    }

    private void loadFile() {
        // 允许访问文件
        wv.getSettings().setAllowFileAccess(true);
        //我们可以通过Context的getExternalFilesDir(null)方法获取APP专属目录
        String filePath = getExternalFilesDir(null) + File.separator + "a.html";
//        String filePath = Environment.getExternalStorageDirectory().getPath() + "/a.html";
        Log.d("TAG", "onCreate: " + filePath);
        wv.loadUrl(filePath);
    }
}
<!-- activity_webview.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".WebviewActivity">

    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="onBack"
            android:text="back"/>

    <WebView
            android:id="@+id/wv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
</LinearLayout>
.2-2 控制网页的前进后退-慕课网就业班 2019-07-25 20_25(更多 IT 教程 1x)

package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.text.Editable;
import android.util.Log;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.Toast;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class WebviewActivity extends AppCompatActivity {
    public static final String iURL = "https://www.bing.com/";
    private WebView wv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);
        wv = findViewById(R.id.wv);

        wv.loadUrl(iURL);

        wv.setWebViewClient(new WebViewClient() );
    }

    public void onCanGoBack(View v) {
        Toast.makeText(this, String.valueOf(wv.canGoBack()), Toast.LENGTH_SHORT).show();
    }

    public void onGoBack(View v) {
        wv.goBack();
    }

    public void onGoForward(View v) {
        wv.goForward();
    }

    public void onCanGoForward(View v) {
        Toast.makeText(this, String.valueOf(wv.canGoForward()), Toast.LENGTH_SHORT).show();
    }

    public void onCanGoBackOrForward(View v) {
        EditText et = findViewById(R.id.steps);
        int steps = Integer.valueOf(et.getText().toString());
        Toast.makeText(this, String.valueOf(wv.canGoBackOrForward(steps)), Toast.LENGTH_SHORT).show();
    }

    public void onGoBackOrForward(View v) {
        EditText et = findViewById(R.id.steps);
        int steps = Integer.valueOf(et.getText().toString());
        wv.goBackOrForward(steps);
    }

    public void onClearHistory(View v) {
        wv.clearHistory();
    }
}
<!-- activity_webview.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
              android:orientation="vertical"
              tools:context=".WebviewActivity">

    <!--    <Button-->
    <!--        android:layout_width="match_parent"-->
    <!--        android:layout_height="wrap_content"-->
    <!--        android:onClick="onBack"-->
    <!--        android:text="back" />-->
    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

        <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="onCanGoBack"
                android:text="CanGoBack"/>

        <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="onGoBack"
                android:text="GoBack"/>
    </LinearLayout>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

        <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="onCanGoForward"
                android:text="CanGoForward"/>

        <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="onGoForward"
                android:text="GoForward"/>

    </LinearLayout>

    <EditText
            android:id="@+id/steps"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

        <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="onCanGoBackOrForward"
                android:text="CanGoBackOrForward"/>

        <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="onGoBackOrForward"
                android:text="GoBackOrForward"/>
    </LinearLayout>

    <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onClearHistory"
            android:text="ClearHistory"/>

    <WebView
            android:id="@+id/wv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
</LinearLayout>
.2-3 WebView 状态管理-慕课网就业班 2019-07-25 16_20(更多 IT 教程 1x)

为什么需要状态管理?设想一下,当网络播放音乐,但是当手机返回后却无法停止音乐播放

package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.text.Editable;
import android.util.Log;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.Toast;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class WebviewActivity extends AppCompatActivity {
    public static final String iURL = "https://www.bing.com/";
    private WebView wv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);
        wv = findViewById(R.id.wv);
        wv.loadUrl("file:///android_asset/a.html");

        wv.setWebViewClient(new WebViewClient()  );
    }

    // ...

    @Override
    protected void onPause() {
        super.onPause();
        wv.onPause();
        // 暂停所有webview操作
//        wv.pauseTimers();
    }

    @Override
    protected void onResume() {
        super.onResume();
        wv.onResume();
//        wv.resumeTimers();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        wv.destroy();
    }
}
<!-- assets/a.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <meta
            name="viewport"
            content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
    />
    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
    <title>Document</title>
</head>
<body>
assets下的html
<audio
        autoplay="autoplay"
        controls="controls"
        loop="loop"
        preload="auto"
        src="a.flac"
>
    你的浏览器版本太低,不支持audio标签
</audio>
</body>
</html>

第 3 章 WebSettings

.3-1 WebView 相关常用类简介-慕课网就业班 2019-07-25 16_20(更多 IT 教程 1x)

.3-2 控制 JS 运行-慕课网就业班 2019-07-25 16_24(更多 IT 教程 1x)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <meta
            name="viewport"
            content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
    />
    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
    <title>Document</title>
</head>
<body>
assets下的html
<audio
        autoplay="autoplay"
        controls="controls"
        loop="loop"
        preload="auto"
        src="a.flac"
>
    你的浏览器版本太低,不支持audio标签
</audio>
<script>
    // 跳转bing
    location.href = 'https://www.bing.com'
</script>
</body>
</html>
package com.malugy.activitydemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.text.Editable;
import android.util.Log;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.Toast;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class WebviewActivity extends AppCompatActivity {
    public static final String iURL = "https://www.bing.com/";
    private WebView wv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_webview);
        wv = findViewById(R.id.wv);

        WebSettings settings = wv.getSettings();
        settings.setJavaScriptEnabled(true);

        wv.loadUrl("file:///android_asset/a.html");

        wv.setWebViewClient(new WebViewClient());

    }
    // ...
}

06 步骤六:项目实战

02 仿QQ阅读的小慕书苑阅读器

第2章 项目分析

.2-2 流程分析-慕课网就业班

implementation 'com.loopj.android:android-async-http:1.4.11'

第3章 引导页的实现

.3-1 闪屏页实现-慕课网就业班

03 Android 高级应用与Kotlin综合实战

01 步骤一:Android高级应用

01 Service基础

第1章 课程介绍

第2章 两种服务的简单使用(重点)

.2-1 启动服务-慕课网就业班 2019-07-26 09_36(更多IT教程 1x)


  目录