Android开发学习(基础)

Android开发环境的搭建

1、安装jdk开发环境

甲骨文公司jdk1.8下载地址
1.下载安装即可,建议安装到指定文件目录下
2.安装完成之后,配置java的JAVA_HOME环境变量,Android开发工具需要使用。
3.java -version 可以查看有没有配置成功。

2、安装android studio开发工具

android studio开发工具下载地址
1.傻瓜式安装,下一步,下一步。

3、第一次使用下载SDK

1.自定义设置
在这里插入图片描述

2.定义安卓SDK的存放地址
在这里插入图片描述
3.安装安卓的SDK
在这里插入图片描述

4、设置AndroidStudio的参数

观看视频教学

5、配置ADB的路径/创建一个AndroidStudio自带的模拟器

1.找到AndroidSDK的路径,找到ADB的目录配置进环境变量里。(path)
在这里插入图片描述

2.打开androidstudio,创建一个手机模拟器。
在这里插入图片描述
3.新建
在这里插入图片描述
4.等待安装完成!!
5.如果提示不能使用的话,进入bios系统开启设备支持虚拟化技术。
6.可以直接下载demo进虚拟手机里运行,查看状况。
在这里插入图片描述

安卓开发UI布局

1、新建项目

1.创建项目
在这里插入图片描述
2.目录介绍
在这里插入图片描述
在这里插入图片描述

  • 新建页面

    • 完整的页面创建过程包括三个步骤:
    • 在 layout 目录下创建 XML 文件
    • 创建与 XML 文件对应的 Java 代码
    • 在 AndroidManifest.xml 中注册页面配
  • 快速生成页面源码

    • 依次选择右键菜单New→Activity→Empty Activity,弹出图示的页面创建窗口。
    • 输入各项信息后,单击窗口右下角的 Finish 按钮,即可完成新页面的创建动作
      在这里插入图片描述

2、线性布局(LinearLayout)

1.什么是线性布局?其实呢,线性布局就是把孩子都摆在同一条线上!

1.设置视图的宽高

视图宽度通过属性android:layout_width表达,
视图高度通过属性android:layout_height表达,
宽高的取值主要有下列三种:

  • match_parent:表示与上级视图保持一致。
  • wrap_content:表示与内容自适应。
  • 以dp为单位的具体尺寸

2.线性布局内部两种排列方式

线性布局内部的各视图有两种排列方式:

  • orientation属性值为horizontal时,内部视图在水平方向从左往右排列。
  • orientation属性值为vertical时,内部视图在垂直方向从上往下排列。
  • 如果不指定orientation属性,则LinearLayout默认水平方向排列。
//垂直
android:orientation="vertical"

//水平
android:orientation="horizontal"

在这里插入图片描述

3.线性布局的权重

  • 线性布局的权重概念,指的是线性布局的下级视图各自拥有多大比例的宽高
  • 权重属性名叫layout_weight,但该属性不在LinearLayout节点设置,而在线性布局的直接下级视图设置,表示该下级视图占据的宽高比例
    • layout_width填0dp时,layout_weight表示水平方向的宽度比例
    • layout_height填0dp时,layout_weight表示垂直方向的高度比例
      在这里插入图片描述
//权重
android:layout_weight="1"

android:layout_weight="1"

在这里插入图片描述

3、相对布局(RelativeLayout)

相对布局的下级视图位置由其他视图决定。用于确定下级视图位置的参照物分两种:

  • 与该视图自身平级的视图;
  • 该视图的上级视图(也就是它归属的RelativeLayout)

如果不设定下级视图的参照物,那么下级视图默认显示在RelativeLayout内部的左上角。

相对位置的取值

相对位置的属性取值相对位置说明
layout_toLeftOf当前视图在指定视图的左边
layout_toRightOf当前视图在指定视图的右边
layout_above当前视图在指定视图的上方
layout_below当前视图在指定视图的下方
layout_alignLeft当前视图与指定视图的左侧对齐
layout_alignRight当前视图与指定视图的右侧对齐
layout_alignTop当前视图与指定视图的顶部对齐
layout_alignBottom当前视图与指定视图的底部对齐
layout_centerInParent当前视图在上级视图中间
layout_centerHorizontal当前视图在上级视图的水平方向居中
layout_centerVertical当前视图在上级视图的垂直方向居中
layout_alignParentLeft当前视图与上级视图的左侧对齐
layout_alignParentRight当前视图与上级视图的右侧对齐
layout_alignParentTop当前视图与上级视图的顶部对齐
layout_alignParentBottom当前视图与上级视图的底部对齐

用true来控制,例如:

//当前视图在上级视图中间
android:layout_centerInParent="true"

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

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="中间"
    />
</RelativeLayout>

在这里插入图片描述

相对位置的属性取值相对位置说明
layout_centerInParent当前视图在上级视图中间
layout_centerHorizontal当前视图在上级视图的水平方向居中
layout_centerVertical当前视图在上级视图的垂直方向居中
layout_alignParentLeft当前视图与上级视图的左侧对齐
layout_alignParentRight当前视图与上级视图的右侧对齐
layout_alignParentTop当前视图与上级视图的顶部对齐
layout_alignParentBottom当前视图与上级视图的底部对齐

2.相对布局相对于同级控件

相对位置的属性取值相对位置说明
layout_toLeftOf当前视图在指定视图的左边
layout_toRightOf当前视图在指定视图的右边
layout_above当前视图在指定视图的上方
layout_below当前视图在指定视图的下方
layout_alignLeft当前视图与指定视图的左侧对齐
layout_alignRight当前视图与指定视图的右侧对齐
layout_alignTop当前视图与指定视图的顶部对齐
layout_alignBottom当前视图与指定视图的底部对齐

在这里插入图片描述

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

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="中间"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/button"
        android:layout_centerHorizontal="true"
        android:text="我在中间的上面"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toLeftOf="@id/button"
        android:text="我在中间的左边"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@id/button"
        android:text="我在中间的右边"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_below="@id/button"
        android:text="我在中间的下边"
        android:layout_centerHorizontal="true"/>
</RelativeLayout>

注意:组件的id要书写正确

<Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="中间"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/button"
        android:layout_centerHorizontal="true"
        android:text="我在中间的上面"/>

在这里插入图片描述

4、绝对布局(AbsoluteLayout)

AbsoluteLayout是靠xy来控制自己的位置

<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_x="156dp"
        android:layout_y="324dp"
        android:text="Button" />
</AbsoluteLayout>

5、表格布局(TableLayout)

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TableRow>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="按钮1"
            android:layout_weight="1"
            >
        </Button>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="按钮2"
            android:layout_weight="1"
            >
        </Button>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="按钮3"
            android:layout_weight="1"
            >
        </Button>
    </TableRow>
    <TableRow>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="按钮4"
            android:layout_weight="1"
            >
        </Button>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="按钮5"
            android:layout_weight="1"
            >
        </Button>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="按钮6"
            android:layout_weight="1"
            >
        </Button>
    </TableRow>
    <TableRow>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="按钮7"
            android:layout_weight="1"
            >
        </Button>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="按钮8"
            android:layout_weight="1"
            >
        </Button>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="按钮9"
            android:layout_weight="1"
            >
        </Button>
    </TableRow>
</TableLayout>

6、帧布局(FrameLayout)

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--通常用作播放器暂停的界面-->
    <View
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#ff00"
        android:layout_gravity="center"
        />
</FrameLayout>

7、网格布局(GridLayout)

  • 网格布局支持多行多列的表格排列
  • 网格布局默认从左往右、从上到下排列,它新增了两个属性
    • columnCount属性,它指定了网格的列数,即每行能放多少个视图
    • rowCount属性,它指定了网格的行数,即每列能放多少个视图
      在这里插入图片描述

8、滚动视图(ScrollView)

滚动视图有两种:

  • ScrollView,它是垂直方向的滚动视图;垂直方向滚动时,layout_width属性值设置为match_parent,layout_height属性值设置为wrap_content。
  • HorizontalScrollView,它是水平方向的滚动视图;水平方向滚动时,layout_width属性值设置为wrap_content,layout_height属性值设置为match_parent。

9、按钮控件(Button)

按钮控件Button由TextView派生而来,它们之间的区别有:

  • Button拥有默认的按钮背景,而TextView默认无背景;
  • Button的内部文本默认居中对齐,而TextView的内部文本默认靠左对齐;
  • Button会默认将英文字母转为大写,而TextView保持原始的英文大小写;

按钮控件的新增属性:
与TextView相比,Button增加了两个新属性:

  • textAllCaps属性,它指定了是否将英文字母转为大写,为true是表示自动转为大写,为false表示不做大写转换。
  • onClick属性,它用来接管用户的点击动作,指定了点击按钮时要触发哪个方法。

10、安卓开发中常用的单位

布局中常用的单位

  • 像素单位px
    像素单位不建议使用,除非是手表,或者机顶盒。
  • 适配的单位dp
    这适配屏幕的单位,推荐使用,在实际开发中,U设计师会给你标好的。(推荐使用,需要技术相应的dp值来使用!!!)
  • 字体单位sp
    sp:全名 scaled pixels-best for text size,放大像素(比例像素),与刻度无关,可以根据用户的字体大小首选项进行缩放,主要用来处理字体的大小;

简单控件

1、设置文本内容

设置文本内容有两种方式:

  • 在 XML 文件中通过属性 android:text 设置文本
  • 在 Java 代码中调用文本视图对象的 setText 方法设置文本

2、引用字符串资源

  • 在XML文件中引用(@string/***)
  • 在Java代码中引用(R.string.***)

3、设置文本的大小

  • 在 Java 代码中调用 setTextSize 方法,即可指定文本大小。
  • 在 XML 文件中则通过属性 android:textSize 指定文本大小,此时需要指定字号单位。
    • px:它是手机屏幕的最小显示单位,与设备的显示屏有关。
    • dp:它是与设备无关的显示单位,只与屏幕的尺寸有关。
    • sp:它专门用来设置字体大小,在系统设置中可以调整字体大小。

4、设置文本的颜色

在 Java 代码中调用 setTextColor 方法即可设置文本颜色,具体色值可从 Color 类取。

Color类中的颜色类型说明Color类中的颜色类型说明
BLACK黑色GREEN绿色
DKGRAY深灰BLUE蓝色
GRAY灰色YELLOW黄色
LTGRAY浅灰CYAN青色
WHITE白色MAGENTA玫红
RED红色TRANSPARENT

5、RGB颜色定义

  • 在XML文件中则通过属性android:textColor指定文本颜色,色值由透明度alpha和RGB三原色(红色red、绿色green、蓝色blue)联合定义。
  • 色值有八位十六进制数与六位十六进制数两种表达方式,例如八位编码FFEEDDCC中,FF表示透明度,EE表示红色的浓度,DD表示绿色的浓度,CC表示蓝色的浓度。
  • 透明度为FF表示完全不透明,为00表示完全透明。RGB三色的数值越大,表示颜色越浓,
    也就越亮;数值越小,表示颜色越淡,也就越暗。

使用色值定义文字颜色:

  • 在Java代码中设置色值需要添加0x前缀表示十六进制数。
  • 在XML文件中设置色值需要添加“#”前缀

6、引用颜色资源

  • 在XML文件中引用(@color/***)
  • 在Java代码中引用(R.color.***)

7、在代码中设置视图宽高

首先确保XML中的宽高属性值为wrap_content,接着打开该页面对应的Java代码,依序执行以下三个步骤:

  • 调用控件对象的getLayoutParams方法,获取该控件的布局参数。
  • 布局参数的width属性表示宽度,height属性表示高度,修改这两个属性值。
  • 调用控件对象的setLayoutParams方法,填入修改后的布局参数使之生效

8、设置视图的间距

设置视图的间距有两种方式:

  • 采用layout_margin属性,它指定了当前视图与周围平级视图之间的距离。包括layout_margin、layout_marginLeft、layout_marginTop、layout_marginRight、layout_marginBottom
  • 采用padding属性,它指定了当前视图与内部下级视图之间的距离。包括padding、paddingLeft、paddingTop、paddingRight、paddingBottom

9、设置视图的对齐方式

  • 设置视图的对齐方式有两种途径:
    • 采用layout_gravity属性,它指定了当前视图相对于上级视图的对齐方式。
    • 采用gravity属性,它指定了下级视图相对于当前视图的对齐方式。
  • layout_gravity与gravity的取值包括:left、top、right、bottom,还可以用竖线连接各取值,例如“left|top”表示即靠左又靠上,也就是朝左上角对齐

10、点击事件的处理(重要)

(第一种方式)
在要被点击的控件里添加onClick属性
格式:android:onClick="XXXX"

<Button
            android:id="@+id/button01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/shape"
            android:text="按钮1"
            android:color="#ffffff"
            android:layout_weight="1"
            android:onClick="onOne01">

接下来,我们就在对应使用这个布局的Activity上面写一个方法,这个方法的格式为:

public void 方法名(View view){//view触发事件的视图控件
        Log.d(TAG,"编写要执行的方法");
}
public void onOne01(View view){
        if(view instanceof Button){
            Button view1 = (Button) view;
            String s = view1.getText().toString();
            Log.d(TAG,"s="+s);
        }
        Log.d(TAG,"One be click ...");
    }

(第二种方式)
第二种方式呢,就是通过ld声明的方式来找到控件,然后呢,对这个控件设置点击事件。
1.给对应的控件添加id
2.在对应的activity里头找到控件

 	private Button button01;
    private Button button02;
    private Button button03;

/**
 * 在这个方法里面找到全部控件
 */
    private void initViev() {
        //找到全部控件并定义成内部私有属性
        button01 = (Button)this.findViewById(R.id.button01);
        button02 = (Button)this.findViewById(R.id.button02);
        button03 = (Button)this.findViewById(R.id.button03);
    }

3.设置点击事件
完整代码

package com.tian;

import static android.util.Log.d;

import androidx.appcompat.app.AppCompatActivity;

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


public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MainActivity";

    private Button button01;
    private Button button02;
    private Button button03;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.table_layout);
        //找控件
        initViev();
        //设置点击事件
        initClickEvent();
    }
    /**
     * 设置点击事件
     */
    private void initClickEvent() {
        //第一种设置方式
        button01.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (v instanceof Button){
                    Button v1 = (Button) v;
                    String s = v1.getText().toString();
                    d(TAG, "onClick: 111"+s);
                }
            }
        });
        //第二种设置方式,可以设置统一处理的方法(需要做好控件类型控制和判断)
        button02.setOnClickListener(this);
        button03.setOnClickListener(this);

    }

    /**
     * 在这个方法里面找到全部控件
     */
    private void initViev() {
        //找到全部控件并定义成内部私有属性
        button01 = (Button)this.findViewById(R.id.button01);
        button02 = (Button)this.findViewById(R.id.button02);
        button03 = (Button)this.findViewById(R.id.button03);
    }
    /**
     * 重写该类的点击处理函数,处理所有按键的点击事件
     */
    @Override
    public void onClick(View v) {
        //如果有多个控件设置点击事件,那么我们这里面统一处理的话,需要判断是那个控件。
        if(v == button02){
            d(TAG, "onClick:2 "+button02.getText().toString());
        }else if(v == button03 ){
            d(TAG, "onClick:3 "+button03.getText().toString());
            //....用同样的方法去判断
        }

        //另外一种方法就是用switch来判断id
        //先拿到id
        int id = v.getId();
        d(TAG, "onClick: " + id);
        switch (id){
            case R.id.button01:
                // one 这个内容被点击了
                // 就是在这里处理就Ok了
                break;
            case R.id.button02:
                // one 这个内容被点击了
                // 就是在这里处理就Ok了
                break;
            case R.id.button03:
                // one 这个内容被点击了
                // 就是在这里处理就Ok了
                break;
        }
    }
}

使用以下代码来隐藏输入的密码:

android:inputType=“textPassword”

安卓数据持久化存储

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"
    android:background="#00aaff"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="登录即代表阅读内容并同意阅读条款"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom = "true"
        android:layout_marginBottom="30dp"
        android:textSize="18dp"
        android:textColor="@color/white" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:orientation="vertical"
        android:padding="30dp"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:drawableLeft="@mipmap/ic_launcher_round"
            android:text="QQ"
            android:textColor="@color/white"
            android:textSize="50sp"
            />
        <EditText
            android:id="@+id/user"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:hint="QQ号码/手机号码/邮箱"
            android:textColorHint="@color/white"
            android:textColor="@color/white"
            />
        <EditText
            android:id="@+id/password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="密码"
            android:textColorHint="@color/white"
            android:layout_marginTop="10dp"
            android:inputType="textPassword"/>
        <Button
            android:id="@+id/loginButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="登录"
            android:textSize="20dp"
            />

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

            <TextView
                android:id="@+id/passwordIsNull"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="忘记密码?"
                android:textSize="15dp"
                android:textColor="@color/white"
                />
            <TextView
                android:id="@+id/newUser"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="新用户注册"
                android:textColor="@color/white"
                android:layout_alignParentRight = "true"
                />

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

逻辑代码:

package com.example.qqlogindemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.File;
import java.io.FileOutputStream;



/**
 *  为什么我们直接写一个文件名的时候,去写文件,报出的异常是read-only.
 *   其实呢,在Android系统中,每一个应用呢就是一个用户,每个用户它的权限是特定的。不可操作其他应用的内容。
 *   以"/“为根自录的,它不是跟windows一样的
 *
 *   当我们看到我们当前应用的数据保存目录下创建了这个文件info.text,就说明我们可以保存数据了
 */



public class MainActivity extends AppCompatActivity implements View.OnClickListener {

	//用于日志过滤的(log)
    private static final String TAG = "MainActivity";
    private EditText user;
    private EditText password;
    private Button button;
    private TextView passwordIsNull;
    private TextView newUser;

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

        //第一步,找到控件
        initView();

        //第二步,给控件绑定上点击事件
        initViewEvent();
    }

    private void initViewEvent() {
        user.setOnClickListener(this);
        password.setOnClickListener(this);
        button.setOnClickListener(this);
        passwordIsNull.setOnClickListener(this);
        newUser.setOnClickListener(this);
    }

    /**
     * 这个方法是用来找到控件并定义成私有成员变量
     */
    private void initView() {
       user = (EditText)this.findViewById(R.id.user);
       password = (EditText)this.findViewById(R.id.password);
       button = (Button)this.findViewById(R.id.loginButton);
       passwordIsNull = (TextView) this.findViewById(R.id.passwordIsNull);
       newUser = (TextView) this.findViewById(R.id.newUser);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id){
            case R.id.loginButton:
                    handlerLoginEvent(v);//处理登录逻辑!!
                break;
        }
    }

    /**
     * 处理登录逻辑事件
     * 拿到界面中输入的账号和密码
     */
    private void handlerLoginEvent(View view) {
        //第三步,我们要拿到界面上的内容,包括:账号和密码
        String userText = user.getText().toString();
        String passwordText = password.getText().toString();
        Log.d(TAG, "账号:"+userText+";密码:"+passwordText);

        //把账号密码保存起来
        saveUserInfo(userText,passwordText);
    }
	/**
	*保存用户信息的文件存储操作
	*/
    private void saveUserInfo(String userText, String passwordText) {
        File file = new File("/data/data/com.example.qqlogindemo/info.text");//找到对应的位置存储路径
        try {
        if(!file.exists()){//文件不存在就创建
            file.createNewFile();
        }
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            //以我们特定的格式来存储
            fileOutputStream.write((userText+"***"+passwordText).getBytes());
            fileOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

截图:
在这里插入图片描述

2、查看保存的数据

第一种方法:
1.用命令
adb devices 查看手机设备
adb shell 进入安卓linux系统
2.找到/data/data/包名(com.example.qqlogindemo)路径下查找有没有info.text文件。
cat info.text 命令查看

generic_x86_arm:/data/data/com.example.qqlogindemo # cat info.text
dsadsad***dsadasdasdsageneric_x86_arm:/data/data/com.example.qqlogindemo #

第二种方法:
在这里插入图片描述

3、通过系统的方法获取到保存的路径

this.getFilesDir()方法

  • 怎么获取到文件保存的路径呢?/data/data/com.example.qqlogindemo/files
  • 输出的结果为:/data/user/0/com.example.qqlogindemo/files(里面的文件内容与上面的一致应该是属于映射路径)
  • 也就是说,这个getFilesDir()这个方法它拿到的路径是/data/user/0/包名/files这个路径
/**
     * 处理登录逻辑事件
     * 拿到界面中输入的账号和密码
     */
    private void handlerLoginEvent(View view) {
        //第三步,我们要拿到界面上的内容,包括:账号和密码
        String userText = user.getText().toString();
        String passwordText = password.getText().toString();
        Log.d(TAG, "账号:"+userText+";密码:"+passwordText);

        //把账号密码保存起来
        saveUserInfo(userText,passwordText);
    }

    private void saveUserInfo(String userText, String passwordText) {

        //怎么获取到文件保存的路径呢?/data/data/com.example.qqlogindemo/files
        //输出的结果为:/data/user/0/com.example.qqlogindemo/files(里面的文件内容与上面的一致应该是属于映射路径)
        //也就是说,这个getFilesDir()这个方法它拿到的路径是/data/user/0/包名/files这个路径


        File filesDir = this.getFilesDir();
        Log.d(TAG, "文件的路径是: "+filesDir.toString());
        File file = new File(filesDir,"info.text");//找到对应的位置存储路径
        try {
        if(!file.exists()){//文件不存在就创建
            file.createNewFile();
        }
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            //以我们特定的格式来存储
            fileOutputStream.write((userText+"***"+passwordText).getBytes());
            fileOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
//获取到缓存文件存储的路径
File cacheDir = this.getCacheDir();
Log.d(TAG, "缓存文件存储的路径: "+cacheDir);
//获取文件保存的路径
File filesDir = this.getFilesDir();
Log.d(TAG, "文件的路径是: "+filesDir.toString());
File file = new File(filesDir,"info.text");//找到对应的位置存储路径

4、账号密码判空提示

弹出提示窗口代码:

Toast.makeText(this,"密码不能为空..",Toast.LENGTH_SHORT).show();

主要是利用:TextUtils类的isEmpty方法来实现

		String userText = user.getText().toString();
        String passwordText = password.getText().toString();
//账号判空操作
        if (userText.length() == 0){
            //长度为空
            Toast.makeText(this,"账号不能为空..",Toast.LENGTH_SHORT).show();
            return;
        }
        //密码判空操作
        if (passwordText.length() == 0){
            Toast.makeText(this,"密码不能为空..",Toast.LENGTH_SHORT).show();
            return;
        }
        //第二种方法:(常用的以下)
        if(TextUtils.isEmpty(userText)){
            //长度为空
            Toast.makeText(this,"账号不能为空..",Toast.LENGTH_SHORT).show();
            return;
        }
        if(TextUtils.isEmpty(passwordText)){
            //长度为空
            Toast.makeText(this,"密码不能为空..",Toast.LENGTH_SHORT).show();
            return;
        }

5、读取数据回显

1.利用activity的生命周期函数来实现
onRestart()函数

@Override
    protected void onRestart() {
        super.onRestart();
//        File filesDir = this.getFilesDir();
//        File file = new File(filesDir,"info.text");//找到对应的位置存储路径
        try {
            FileInputStream fileInputStream = this.openFileInput("info.text");
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
            String info = bufferedReader.readLine();
            bufferedReader.close();
            //fileOutputStream.write((userText+"***"+passwordText).getBytes());是我们之前保存的形式
            //我们拿到数据之后要进行切割
            String[] split = info.split("\\*\\*\\*");//两个反斜杆进行转义的操作
            for (int  i = 0;i<split.length;i++){
                Log.d(TAG, "分割之后的元素: ["+i+"]="+split[i]);
            }
            //回显数据
            user.setText(split[0]);
            password.setText(split[1]);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

6、数据存储到SD卡上

1.不常用的方式

adb shell进入手机linux系统查看内存挂载地址,文件夹mnt
在这里插入图片描述
进入挂载地址/storage/self/primary
在这里插入图片描述
前面我们把数据保存到应用的内部:/data/data/com.sunofbeaches.aalogindemo/files 那么现在我们就开始学习怎么把这个数据保存到SD 上!

新建Activity流程

1.新建一个新的Activity,继承Activity类,重写onCreate方法
在这里插入图片描述

2.注册Activity,四大组件都需要注册,然后修改程序的主入口
在这里插入图片描述

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.QQloginDemo">
        <activity
            android:name=".MainActivity"
            android:exported="true">
<!--            <intent-filter>-->
<!--                <action android:name="android.intent.action.MAIN" />-->
<!--                <category android:name="android.intent.category.LAUNCHER" />-->
<!--            </intent-filter>-->
        </activity>
<!--        都设置了主入口就从上往下找,第一个作为主入口-->
        <activity android:name=".SdkActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

3.设置布局setContentView(R.layout.sdk_activity);

public class SdkActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //设置布局
        setContentView(R.layout.sdk_activity);

    }
}

3.找到控件用sd_button = (Button)this.findViewById(R.id.sd_button);
4.设置点击事件用 sd_button.setOnClickListener(this);方法
5.实现监听代码

@Override
    public void onClick(View v) {
        if(v == sd_button){
            //写数据到sd卡上
            File filePath = new File("/storage/self/primary");//之前获取的sd卡挂载的路径。
            File file = new File(filePath, "info.txt");
            try {
                FileOutputStream fileOutputStream = new FileOutputStream(file);
                //写入东西
                fileOutputStream.write("我是练习sd卡存储的程序!!".getBytes());
                //关闭输出流
                fileOutputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

6.读写需要添加权限,在AndroidManifest.xml里添加。

<!--添加sd卡的读取权限,-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

在这里插入图片描述

要打开权限空间:
在这里插入图片描述
然后就可以看到存储路径下的info.txt文件了
在这里插入图片描述

2.常用的方式

1.获取sd卡的存储路径,有专用的api获取。

  • Environment.getExternalStorageDirectory();
//获取sd卡的路径
File externalStorageDirectory = Environment.getExternalStorageDirectory();
Log.d(TAG, "sd卡的存储路径: "+externalStorageDirectory.toString());

为什么要这样子获取呢?就是因为不同的手机厂商,它们的扩展卡的名字不一样,通过这个API,就可以获取到它们的扩展卡的路径。

2.我们在实际开发中,会遇到这样的问题,怎么样知道这个手机有没有SD卡?
我们通过一个api来判断这个SD卡是否已经挂载了

  • Environment.getExternalStorageState()
//判断设备有没有挂载SD卡
String externalStorageState = Environment.getExternalStorageState();
if(externalStorageState.equals(Environment.MEDIA_MOUNTED)){
       Log.d(TAG, "该手机挂载了SD卡");
       return;
       }
if (externalStorageState.equals(Environment.MEDIA_UNMOUNTED)){
       Log.d(TAG, "该手机SD卡已经移除");
       return;
       }

3.计算SD卡的可用空间

获取SDK卡相关的信息,比如:可用空间
使用getFreeSpace()方法

 //显示内存容量是多少
            File exFile = Environment.getExternalStorageDirectory();
            long freeSpace = exFile.getFreeSpace();
            //把long类型转成我们直观的空间大小,比如说:多少M,多少KB,多少MB
            String s = Formatter.formatFileSize(this, freeSpace);//this是上下文。
            Log.d(TAG, "显示内存容量: "+s);

7、SharePreference

一般用于保存这个偏好设置,比如说我们设置里面的条目。
SharePreference使用步骤

  • 第一步:拿到这个SharePreference
settings_info = this.getSharedPreferences("settings_info", MODE_PRIVATE);

这里面这个this指的是上下文Context,在视频中我们是在Activity里面所以直接使用this。因为这Activity间接地继承了Context。

  • 第二步:进入编辑模式
SharedPreferences.Editor edit = settings_info.edit();
  • 第三步:保存数据
edit.putBoolean("state",isChecked);
  • 第四步:提交编辑器
edit.commit();
  • 进过这四个步骤,我们就可以把数据保存到SharePreference里了!!

完整代码:

package com.example.qqlogindemo;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.Switch;

public class PreferenceActivity extends Activity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {

    private static final String TAG = "PreferenceActivity";
    Switch is_switch;
    SharedPreferences settings_info;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_preference);

        //找控件
       is_switch = (Switch)this.findViewById(R.id.is_switch);
       is_switch.setOnCheckedChangeListener(this);
       //拿到SharedPreferences
       settings_info = this.getSharedPreferences("settings_info", MODE_PRIVATE);
       //获取数据回显
        boolean state = settings_info.getBoolean("state", false);
        is_switch.setChecked(state);
    }

    @Override
    public void onClick(View v) {
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        //我们在这里需要对数据进行保存
        Log.d(TAG, "onCheckedChanged: "+isChecked);
        SharedPreferences.Editor edit = settings_info.edit();
        edit.putBoolean("state",isChecked);
        edit.commit();
    }
}

switch控件编写:
在这里插入图片描述
shareprefernece存储也是属于这个内部存储,它跟files/cache也是一样的,在/data/data/包名下/shared_prefsxml的文件形式保存起来。它有一个特点,内容保存都是是键值对的方式进行保存。(如下图)
在这里插入图片描述
获取数据回显:

//拿到SharedPreferences
settings_info = this.getSharedPreferences("settings_info", MODE_PRIVATE);
//获取数据回显
boolean state = settings_info.getBoolean("state", false);
is_switch.setChecked(state);

sqlite创建与使用

1、创建数据库并创建表

1.首先新建一个DatabaseHelper类继承SQLiteOpenHelper类实现重写两个方法和编写构造方法:

package com.example.qqlogindemo;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DatabaseHelper extends SQLiteOpenHelper {

    private static final String TAG = "DatabaseHelper";

    /**
     *
     * @ context   上下文
     * @ name      数据库名称
     * @ factory   游标工厂
     * @ version   版本号
     * 通过构造方法创建了我们的数据库
     */
    public DatabaseHelper(Context context) {
        super(context, Constants.DATABASE_NAME, null, Constants.VERSION_CODE);
    }


    /**
     *
     * 当第一次创建数据库的时候被调用
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        //创建时的回调
        Log.d(TAG, "创建数据库... ");
        //创建数据表的操作写在这里面
        String sql = "CREATE TABLE "+Constants.TABLE_NAME+"(id integer,name varchar,age integer,salary integer)";
        Log.d(TAG, "sql: "+sql);
        //db.execSQL(sql);
        db.execSQL("CREATE TABLE user(id integer,name varchar,age integer,salary integer)");
        Log.d(TAG, "数据库版本: "+db.getVersion());
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //升级数据库时的回调
        Log.d(TAG, "升级数据库... ");

        String sql;
        switch (oldVersion){
            case 1:
                //编写版本1需要到指定版本添加的字段值
                sql = "alter table "+Constants.TABLE_NAME+" add phone integer,address varchar";
                db.execSQL(sql);
                break;
            case 2:
                sql = "alter table "+Constants.TABLE_NAME+" add address varchar";
                db.execSQL(sql);
                break;
            case 3:
                //当前版本

                break;

        }
    }
}
  • onCreate:当第一次创建数据库的时候被调用
  • onUpgrade:当数据库版本改变时执行这个回调方法
  • public DatabaseHelper(Context context) {
    super(context, Constants.DATABASE_NAME, null, Constants.VERSION_CODE);大撒大撒
    }

构造方法里的参数是

/**
     *
     * @ context   上下文
     * @ name      数据库名称
     * @ factory   游标工厂
     * @ version   版本号
     * 通过构造方法创建了我们的数据库
     */
    public DatabaseHelper(Context context) {
        super(context, Constants.DATABASE_NAME, null, Constants.VERSION_CODE);
    }

2.为了方便管理某些配置参数新建一个Constants配置类

package com.example.qqlogindemo;

public class Constants {
    //定义数据库名称
    public static final String DATABASE_NAME = "atian.db";
    //定义版本
    public static final int VERSION_CODE = 1;
    //定义表名
    public static final String TABLE_NAME = "user";

}

3.在Activity里new DatabaseHelper调用getWritableDatabase()方法

package com.example.qqlogindemo;

import android.app.Activity;
import android.os.Bundle;

public class DatabaseActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_database);

        DatabaseHelper databaseHelper = new DatabaseHelper(this);
        databaseHelper.getWritableDatabase();

    }
}

创建表的工作一般在onCreate这个回调函数里编写。

2、编写Dao类

package com.example.qqlogindemo;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

public class Dao {
    private static final String TAG = "Dao";
    DatabaseHelper mHelper;
    public Dao(Context context) {
        //创建数据库
        mHelper = new DatabaseHelper(context);
    }

    //增
    public void insert(){
        SQLiteDatabase db = mHelper.getWritableDatabase();
        String sql = "insert into "+Constants.TABLE_NAME+"(id,name,age,salary) values(?,?,?,?)";
        db.execSQL(sql,new Object[]{1,"张三",45,5000});
        db.close();
    }
    //删
    public void delete(){
        SQLiteDatabase db = mHelper.getWritableDatabase();
        String sql = "delete from "+Constants.TABLE_NAME+" where age = 45";
        db.execSQL(sql);
        db.close();
    }
    //改
    public void update(){
        SQLiteDatabase db = mHelper.getWritableDatabase();
        String sql = "update "+Constants.TABLE_NAME+" set salary = 2 where age = 45";
        db.execSQL(sql);
        db.close();
    }
    //查
    public void query(){
        SQLiteDatabase db = mHelper.getWritableDatabase();
        String sql = "select * from "+Constants.TABLE_NAME;
        Cursor cursor = db.rawQuery(sql, null);
        //Cursor是游标,指向某一行某一列

        while (cursor.moveToNext()){
            //获取到age年龄
            int index = cursor.getColumnIndex("age");
            String age = cursor.getString(index);
            Log.d(TAG, "查询到的年龄是: "+age);

        }

        cursor.close();

        db.close();
    }

}

3、编写测试类

package com.example.qqlogindemo;

import android.content.Context;
import android.util.Log;

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

import java.util.List;

/**
 * Instrumented test, which will execute on an Android device.
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
    private static final String TAG = "ExampleInstrumentedTest";

    @Test
    public void useAppContext() {
        // Context of the app under test.
        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
        assertEquals("com.example.qqlogindemo", appContext.getPackageName());
    }

    @Test//测试插入
    public void testInsert(){
        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
        Dao dao = new Dao(appContext);
        dao.insert();
    }

    @Test//测试删除
    public void testDelete(){
        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
        Dao dao = new Dao(appContext);
        dao.delete();
    }

    @Test//测试修改
    public void testUpdate(){
        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
        Dao dao = new Dao(appContext);
        dao.update();
    }

    @Test//测试查询
    public void testQuery(){
        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
        Dao dao = new Dao(appContext);
        dao.query();
    }

}

4、使用android的api实现增删改查操作

先获取SQLiteDatabase db = mHelper.getWritableDatabase();
然后调用相应的方法。

package com.example.qqlogindemo;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

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

public class Dao {
    private static final String TAG = "Dao";
    DatabaseHelper mHelper;
    public Dao(Context context) {
        //创建数据库
        mHelper = new DatabaseHelper(context);
    }

    //增
    public void insert(){
        SQLiteDatabase db = mHelper.getWritableDatabase();
//        String sql = "insert into "+Constants.TABLE_NAME+"(id,name,age,salary) values(?,?,?,?)";
//        db.execSQL(sql,new Object[]{1,"张三",45,5000});

        ContentValues values = new ContentValues();
        values.put("id",2);
        values.put("name","xqt");
        values.put("age",25);
        values.put("salary",5000);
        db.insert(Constants.TABLE_NAME, null, values);
        db.close();
    }
    //删
    public void delete(){
        SQLiteDatabase db = mHelper.getWritableDatabase();
//        String sql = "delete from "+Constants.TABLE_NAME+" where age = 45";
//        db.execSQL(sql);
        int delete = db.delete(Constants.TABLE_NAME, null, null);
        //等于1就是删除成功了
        Log.d(TAG, "delete: "+delete);
        db.close();
    }
    //改
    public void update(){
        SQLiteDatabase db = mHelper.getWritableDatabase();
//        String sql = "update "+Constants.TABLE_NAME+" set salary = 2 where age = 45";
//        db.execSQL(sql);
        ContentValues values = new ContentValues();
        values.put("salary",2000);
        db.update(Constants.TABLE_NAME,values,null,null);
        db.close();
    }
    //查
    public void query(){
        SQLiteDatabase db = mHelper.getWritableDatabase();
//        String sql = "select * from "+Constants.TABLE_NAME;
//        Cursor cursor = db.rawQuery(sql, null);
//        //Cursor是游标,指向某一行某一列
//        while (cursor.moveToNext()){
//            //获取到age年龄
//            int index = cursor.getColumnIndex("age");
//            String age = cursor.getString(index);
//            Log.d(TAG, "查询到的年龄是: "+age);
//        }
//
//        cursor.close();
        Cursor cursor = db.query(Constants.TABLE_NAME, null, null, null, null, null, null);
        while (cursor.moveToNext()){
            int id = cursor.getInt(0);
            String name = cursor.getString(1);
            Log.d(TAG, "id: "+id+";name: "+name);
        }
        db.close();


    }

}

Cursor:相当于是游标,确定行的位置。
ContentValues :相当于是kv的键值对,相当于是设置传参。

5、数据库事务

有两个特点:
1.安全性
情景:
每个月15号,你公司都会给你发工作。
操作流程:

公司的财务账号有10000000,那么它要-12000
那你的账号就要增加12000

可能在这两个过程中,出现了问题,比如说,停电,那么公司,账号上减了钱,但是你的账号没钱进入。
这个时候,就可以使用数据库事务来解决这个问题。

2.高效性

使用普通的形势向数据添加3000条数据
time = 14790

再使用开启事务的形势插入3000条数据
time = 210

对比耗时多少:

原理呢:这个没开始事务的是打开数据库,插入数据,关闭数据库。(耗时很多)
开启事务的:把数据存到内存里,然后一次写入到数据库里。

 //开启事务
db.beginTransaction();
try {
     //这里里面编写多条sql并执行
     db.execSQL("insert into user(id,name,age,salary) values(1,\"张三\",45,5000)");

     db.execSQL("insert into user(id,name,age,salary) values(1,\"张三\",45,5000)");

	//标记数据库操作成功
     db.setTransactionSuccessful();
}catch (Exception e){
      throw  new RuntimeException("出错了!!!");
}finally {
      //关闭事务
      db.endTransaction();
      db.close();
}

四大组件之Activity

1、了解AndroidManifest.xml

包名:package="com.example.qqlogindemo
添加权限:

<!--添加sd卡的读取权限,-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

在这里插入图片描述
详细地址:https://notes.sunofbeach.net/pages/dbc5a7/

2、新建Activity类

1.新建一个XXXXActivity类,继承Activity。重写onCreate。

package com.example.qqlogindemo;

import android.app.Activity;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;

public class DatabaseActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //设置视图
        setContentView(R.layout.activity_database);
        
    }
}

2.新建布局xml

activity_database.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:gravity="center">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="数据库界面"
        android:textSize="20dp"

        />
</RelativeLayout>

3.进行Activity的静态注册到AndroidManifest.xml里

<activity android:name=".DatabaseActivity">
      <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
</activity>

android:label="@string/app_name"可以设置app的名称
一个安卓应用可以有多个入口的。

3、界面跳转(显式意图)

显式Intent:按名称(完全限定类名)指定要启动的组件。
1.首先要创建一个意图对象,然后通过startActivity方法来跳转

String userText = user.getText().toString().trim();
String passwordText = password.getText().toString().trim();
if(userText.equals("")){	
	//提示框
    Toast.makeText(this,"账号为空!",Toast.LENGTH_SHORT).show();
    return;
}
if(passwordText.equals("")){
    Toast.makeText(this,"密码为空!",Toast.LENGTH_SHORT).show();
    return;
}
//这里进行页面跳转
//我们先要创建一个意图对象,然后通过startActivity方法来跳转
Intent intent = new Intent(this,TwoActivity.class);
//中间添加跳转页面需要的参数
intent.putExtra("userText",userText);
intent.putExtra("passwordText",passwordText);
startActivity(intent);

2.跳转后利用getIntent()方法获取参数

//获取跳转的参数
Intent intent = getIntent();
String userText = intent.getStringExtra("userText");
String passwordText = intent.getStringExtra("passwordText");

//获取输出控件,并输出
TextView textView = this.findViewById(R.id.text_two);
textView.setText("账号为:"+userText+",密码为:"+passwordText+"");

4、界面跳转(隐式意图)

隐式意图是相对于显式意图来说的,显式意图可以看得到对应的类,而隐式意图看不到。

接下来,我们就是通过隐式意图的方式来实现界面的跳转:

步骤:

这里面我们针对的是应用内的跳转

第一步:创建一个Intent

Intent intent = new Intent();

第二步:在AndroidManifest.xml里配置目标跳转Activity的意图过滤器(是通过意图过滤来启动的)

…….

<activity android:name”.SecondActivity”>

<intent-filter>

<action name=”com.sunofbeaches.LOGIN_INFO”>

</intent-filter>

</activity>

第三步:给意图设置action

intent.setAction(“com.sunofbeaches.LOGIN_INFO);
//中间添加跳转页面需要的参数
intent.putExtra("userText",userText);
intent.putExtra("passwordText",passwordText);

第四步:然后

startActivity(intent);

其实,我们的显式意图是用来应用内跳转。 而隐式意图用于第三方应用的跳转。

1.过滤logcat运行程序的cmp

命令:
在这里插入图片描述
然后手机上点击谷歌浏览器
获取到:
cmp=com.android.chrome/com.google.android.apps.chrome.Main其实组件的名称,也就是ComponentName = 包名/类的路径名称。前缀包名相同可以省略,也就是所在的包跟包名是一样的。
就可以省略。

cmp=com.android.chrome/com.google.android.apps.chrome.Main

2.跳转到谷歌浏览器(通过显式意图)

//新建一个Intent对象
Intent intent = new Intent();
//第一种写法(对应抓取到的包名和类名)
intent.setClassName("com.android.chrome","com.google.android.apps.chrome.Main");

//第二种写法
ComponentName componentName = new ComponentName("com.android.chrome","com.google.android.apps.chrome.Main");
intent.setComponent(componentName);

//开始跳转
startActivity(intent);

其实,我们的显式意图是用来应用内跳转。 而隐式意图用于第三方应用的跳转。

3.跳转到谷歌浏览器(通过隐式意图)

步骤:
第一步:创建Intent对象

Intent intent = new Intent();

第二步:给这个Intent对象设置Action,设置它的category值,如果5.1以上系统需要设置包名。
这些信息可以从安卓的源码里看到

intent.setAction("android.intent.action.SEARCH");
intent.addCategory("android.intent.category.DEFAULT");
intent.setPackage("com.android.browser");

第三步:startActivity(intent)进行跳转。

 //开始跳转
startActivity(intent);

5、组件之间的数据传输(Intent)

之前我们已经学习了组件之间的跳转,其实我们也尝试过数据之间的传输对吧!

在第一节课的时候,我们学习了怎么跳转,并且把数据传递到下一个界面,也就是我们登录那个例子,把登录的信息传到下一个界面.

接下来我们则系统进学习一下如何把数据传到下一个界面。大家也不要局限于Activity之间的数据传递,也就是说,这是组件与组件之间的数据传递,也适用于后面我们学到的服务,广播接收者…

四大组件,对吧

1.基本数据类型的传输

Intent的组成部分:

元素名称设置方法说明与用途
ComponentsetComponent组件,它指定意图的来源与目标
ActionsetAction动作,它指定意图的动作行为
DatasetData即Uri,它指定动作要操纵的数据路径
CategoryaddCategory类别,它指定意图的操作类别
TypesetType数据类型,它指定消息的数据类型
ExtrasputExtras扩展信息,它指定装载的包裹信息
FlagssetFlags标志位,它指定活动的启动标志

前面要跳转的话需要创建一个意图对象,也就是Intent。这个Intent其实就是数据的载体,把数据扔intent里面。
所以就有了:
在这里插入图片描述
前面的String name是key,也就是这里put,另外一边则是get了。get的时候需要传入key,这样才能获取到对应的值。

一般来说,这个key定义为一个常量,并且两个组件都能访问到。

另外一边则是获取数据是吧!

首先我们要拿到意图对象,也就是Intent

在这里插入图片描述
通过get类型(key)的方法来获取到对应的内容,这样子就完成了数据内容的传输了。

例子:

package com.sunofbeaches.componentdatadeliver;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

/**
 * 虽然说我们这节课是组件之间的数据传递
 * 我们常说的组件有Activity,BroadcastReceiver,Service,ContentProvider
 * <p/>
 * 这里的话我们只学习Activity之间的数据传输,其实其他组件之间的数据传输也是一样的。
 */
public class MainActivity extends AppCompatActivity {

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


    /**
     * 第一个按钮被点击了
     *
     * @param view
     */
    public void firstClick(View view) {
        //这样子写也是可以的哦!
        Intent intent = new Intent(this, SecondActivity.class);
        intent.putExtra("booleanKey", true);
        intent.putExtra("charKey", 'a');
        intent.putExtra("byteKey", (byte) 1);
        intent.putExtra("shortKey", (short) 2);
        intent.putExtra("intKey", 3);
        intent.putExtra("longKey", 4l);
        intent.putExtra("floatKey", 0.5f);
        intent.putExtra("doubleKey", 0.6d);
        startActivity(intent);
    }


    /**
     * 第二个按钮被点击了
     */
    public void secondClick(View view) {

    }
}


第二个Activity

package com.sunofbeaches.componentdatadeliver;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

/**
 * Create by TrillGates 2017/11/23
 * 这是第二个界面,我们就在这个界面获取一个内容吧:
 */
public class SecondActivity extends Activity {

    private static final String TAG = "SecondActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        /**
         * 拿到启动这个Activity的意图对象
         */
        Intent intent = getIntent();
        if (intent != null) {
            //前面的是key,后面的是默认值,假设获取不到的时候,就会返回默认值,也就是后面的那个值。
            //比如说我们把key写错了,这样子就获取不到值了。
            boolean booleanValue = intent.getBooleanExtra("booleanKey", false);
            //
            char charValue = intent.getCharExtra("charKey", '*');
            //
            byte byteValue = intent.getByteExtra("byteKey", (byte) 0);
            //
            short shortValue = intent.getShortExtra("shortKey", (short) 0);
            //
            int intValue = intent.getIntExtra("intKey", -1);
            //
            long longValue = intent.getLongExtra("longKey", 0l);
            //
            float floatValue = intent.getFloatExtra("floatKey", 0.0f);
            //
            double doubleValue = intent.getDoubleExtra("doubleKey", 0.0d);

            Log.d(TAG, "booleanValue = " + booleanValue);
            Log.d(TAG, "charValue = " + charValue);
            Log.d(TAG, "byteValue = " + byteValue);
            Log.d(TAG, "shortValue = " + shortValue);
            Log.d(TAG, "intValue = " + intValue);
            Log.d(TAG, "longValue = " + longValue);
            Log.d(TAG, "floatValue = " + floatValue);
            Log.d(TAG, "doubleValue = " + doubleValue);

        }
    }
}

执行结果是怎么样的呢?我们点击一下按钮如下:
在这里插入图片描述

2.引用数据类型的数据传输

前面已经说了如何传输基本数据类型,那么后面的话我们说一说怎么传递对象。

前面我们看到可以传String呢?String呢不是基本数据类型,它是引用数据类型。

String是已经实现了序列化的接口的:
在这里插入图片描述
Bitmap也就是位图,位图对象也是实现了序列化的接口的。(传图片)

所以我们可以传位图对象,但是要注意的是它的大小 ,后面我们会讲到意图对象的传值的大小限制。

接下来,我们使用Intent来传一个对象:User

首先,我们要把User序列化。

package com.sunofbeaches.componentdatadeliver;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by TrillGates on 17/11/24.
 * God bless my code!
 */
public class User implements Parcelable {
    private String name;
    private int age;

    public User(String name, int age) {
        this.age = age;
        this.name = name;
    }

    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }
}

实现Parcelable也行,Serializable也行。后者是Java的,前者是google写的类。两者的不同是前者比较高效,它是写到内存里的,后者是写到持久化存储单元里的。

然后呢?看代码吧:

   /**
     * 第二个按钮被点击了
     */
    public void secondClick(View view) {
        User user = new User("TrillGates", 25);
        Intent intent = new Intent(this, SecondActivity.class);
        intent.putExtra("user", user);
        startActivity(intent);
    }

第二个Activity里的代码:

   User user = intent.getParcelableExtra("user");
   Log.d(TAG, "usr Name == " + user.getName());
   Log.d(TAG, "usr age == " + user.getAge());

运行起来,结果如下:
在这里插入图片描述
除了这样传以外,还可以怎么传呢?其实我们可以以协议的形式来传数据的呢:

实际的列子有那些呢?比如说我们第三应用发短信,第三方应用要调用电话拨号器。这个时候就需要去看它的意图过滤器了:

<intent-filter>
      <action android:name="android.intent.action.CALL" />
      <category android:name="android.intent.category.DEFAULT" />
      <data android:scheme="tel" />
</intent-filter>

我们可以看到,这其实就是约束,数据的约束,也就是说,我们要拨打电话的时候,是这样子的:

Intent intent = new Intent():

intent.addAction(“android.intent.action.CALL”);

intent.setCategory(“android.intent.category.DEFAULT”);

intent.setData(Uri.parse(“tel://10086”));

startActivity(intent);

这样能看明白吗?以上是纯手写的代码,没有开发工具也不知道单词有没写错。
OK,知道这个以后,我们就明白了,还可以通过setData来传数据,并且,在另外一个界面,通过getData来获取到。

获取到的是全部内容哦,包括约束:tel://10086

3.Intent封装数据的大小限制

有的时候,我们传的数据是挺大的。比如说,我们要传一张图片的时候,就很大了,是吧。那么它的现实是多大呢?我们可以看看官方的文档。
在这里插入图片描述
那怎么办呢?可以写到SD卡上,把路径传过去就可以。对于IPC来说,有好多种方式。IPC就是跨进程通迅啦,后面我们学习到服务的时候 ,我们也会学到AIDL,这也是IPC的一种方式。

从上面的文章我们可以知道,它限制的大小为1M,这块其实是共享内存来的。也就是Blundle.详细请看我们的视频课程吧!

4.结束活动

从当前页面回到上一个页面,相当于关闭当前页面,返回代码如下:
finish(); // 结束当前的活动页面

5.实现拨打电话的功能

本质是隐式跳转:
阅读安卓源码得到隐式跳转信息

//新建一个Intent对象
Intent intent = new Intent();
//以下信息阅读源码获得
intent.setAction("android.intent.action.CALL");
intent.addCategory("android.intent.category.DEFAULT");
//要根据它的格式来编写uri。
Uri parse = Uri.parse("tel:10086");
//设置数据
intent.setData(parse);

//开始跳转
startActivity(intent);

AndroidManifest.xml添加权限:

<uses-permission android:name="android.permission.CALL_PHONE"/>

第二种写法:
ACTION_DIAL:跳转到拨打电话界面
ACTION_CALL:直接拨打电话

Intent intent = new Intent(Intent.ACTION_CALL,Uri.parse("tel:10086"));

6.Activity的数据回传

数据回传的基础流程:
在这里插入图片描述
直接上代码:

OneActivity

package com.example.qqlogindemo.activitys;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;

import com.example.qqlogindemo.R;

public class OneActivity extends Activity implements View.OnClickListener {
    private static final String TAG = "OneActivity";
    EditText user ;
    EditText password ;
    Button button;
    private Button egg_button;
    Button phone_button;
    Button sms_button;
    Button recharge_button;
    TextView text_view;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_one);
        //初始化
        initView();
        //设置点击事件
        initViewEvent();

    }

    private void initViewEvent() {
        button.setOnClickListener(this);
        egg_button.setOnClickListener(this);
        phone_button.setOnClickListener(this);
        sms_button.setOnClickListener(this);
        recharge_button.setOnClickListener(this);
    }

    private void initView() {
         user = this.findViewById(R.id.login_user);
         password = this.findViewById(R.id.login_password);
            button = this.findViewById(R.id.login_button);
        egg_button = this.findViewById(R.id.egg_button);
        phone_button = this.findViewById(R.id.phone_button);
        sms_button = this.findViewById(R.id.sms_button);
        recharge_button = this.findViewById(R.id.recharge_button);
        text_view = this.findViewById(R.id.text_view);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id){
            case R.id.login_button:
                handlerLoginEvent(v);
                break;
            case R.id.egg_button:
                handlerEgg(v);
                break;
            case R.id.phone_button:
                handlerPhone(v);
                break;
            case R.id.sms_button:
                handlerSms(v);
                break;
            case R.id.recharge_button:
                handlerRecharge(v);
                break;
        }
    }

    private void handlerRecharge(View v) {
        //跳转到充值界面
        //新建一个Intent对象
        Intent intent = new Intent(this,PayActivity.class);

        //调用充值页面
        startActivityForResult(intent,1);

    }

    /**
     * 返回的结果会在这里面
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode==1){
            if (resultCode == 2){
                String resultContent = data.getStringExtra("resultContent");
                //显示文字内容
                text_view.setText(resultContent);
                //改变文字大小
                text_view.setTextSize(20);
            }
            if (resultCode == 3){
                String resultContent = data.getStringExtra("resultContent");
                //显示文字内容
                text_view.setText(resultContent);
                //改变文字大小
                text_view.setTextSize(20);
            }
        }

    }

    private void handlerSms(View v) {
        //新建一个Intent对象
        Intent intent = new Intent();



    }

    private void handlerPhone(View v) {
        //新建一个Intent对象
        Intent intent = new Intent(Intent.ACTION_DIAL,Uri.parse("tel:10086"));

//        intent.setAction("android.intent.action.CALL");
//        intent.addCategory("android.intent.category.DEFAULT");
//        Uri parse = Uri.parse("tel:10086");
//        intent.setData(parse);

        //开始跳转
        startActivity(intent);
    }

    private void handlerEgg(View v) {
        //新建一个Intent对象
        Intent intent = new Intent();
//        //第一种写法
//        intent.setClassName("com.android.chrome","com.google.android.apps.chrome.Main");
//
//        //第二种写法
//        ComponentName componentName = new ComponentName("com.android.chrome","com.google.android.apps.chrome.Main");
//        intent.setComponent(componentName);

        intent.setAction("android.intent.action.SEARCH");
        intent.addCategory("android.intent.category.DEFAULT");
        intent.setPackage("com.android.browser");


        //开始跳转
        startActivity(intent);
    }

    private void handlerLoginEvent(View v) {
        String userText = user.getText().toString().trim();
        String passwordText = password.getText().toString().trim();
        if(userText.equals("")){
            Toast.makeText(this,"账号为空!",Toast.LENGTH_SHORT).show();
            return;
        }
        if(passwordText.equals("")){
            Toast.makeText(this,"密码为空!",Toast.LENGTH_SHORT).show();
            return;
        }
        //这里进行页面跳转
        //我们先要创建一个意图对象,然后通过startActivity方法来跳转
        Intent intent = new Intent(this,TwoActivity.class);
        //中间添加跳转页面需要的参数
        intent.putExtra("userText",userText);
        intent.putExtra("passwordText",passwordText);
        startActivity(intent);

        Log.d(TAG, "userText: "+userText);
        Log.d(TAG, "passwordText: "+passwordText);
    }
}

PayActivity

package com.example.qqlogindemo.activitys;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;


import androidx.annotation.Nullable;

import com.example.qqlogindemo.R;

public class PayActivity extends Activity {

    Button recharge;
    Button unRecharge;
    EditText money;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pay);
        //初始化
        initView();
        //设置点击事件
        initViewEvent();

    }

    private void initViewEvent() {
        recharge.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String trim = money.getText().toString().trim();
                if (trim.equals("")){
                    Toast.makeText(PayActivity.this,"请输入金额..",Toast.LENGTH_SHORT).show();
                    return;
                }


                //进行网络访问,进行充值


                Intent intent = new Intent();
                intent.putExtra("resultContent","充值成功!");

                //setResult有两个重载的方法,一个是只有resultCode的,一个是有resultCode和Intent的。
                setResult(2,intent);
                //结束活动
                finish();


            }
        });

        unRecharge.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.putExtra("resultContent","取消充值!");

                //setResult有两个重载的方法,一个是只有resultCode的,一个是有resultCode和Intent的。
                setResult(3,intent);

                //结束活动
                finish();
            }
        });
    }

    private void initView() {
        recharge = (Button)this.findViewById(R.id.recharge);
        unRecharge = (Button)this.findViewById(R.id.unRecharge);
        money = (EditText)this.findViewById(R.id.money);
    }
}

activity_pay.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/money"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:hint="请输入充值金额"
        android:inputType="number" />

    <Button
        android:id="@+id/recharge"
        android:layout_width="match_parent"
        android:text="充值"
        android:textSize="20dp"
        android:layout_height="wrap_content"/>

    <Button
        android:id="@+id/unRecharge"
        android:layout_width="match_parent"
        android:text="取消充值"
        android:textSize="20dp"
        android:layout_height="wrap_content"/>

</LinearLayout>

实现相机的回传,需要查看安卓上层应用源码进行开发
setResult里的返回码源码里面固定的。

6、Activity的生命周期

https://notes.sunofbeach.net/pages/1ffec7/

在这里插入图片描述
Activity的生命周期:

  • onCreate:创建活动。把页面布局加载进内存,进入了初始状态。(初始)
  • onStart:开始活动。把活动页面显示在屏幕上,进入了就绪状态。(可见)
  • onResume:恢复活动。活动页面进入活跃状态,能够与用户正常交互,例如允许响应用户的点击动作、允许用户输入文字等等。(获取到焦点)
  • onPause:暂停活动。页面进入暂停状态,无法与用户正常交互。(失去焦点)
  • onStop:停止活动。页面将不在屏幕上显示。(不可见)
  • onDestroy:销毁活动。回收活动占用的系统资源,把页面从内存中清除。(结束)
  • onRestart:重启活动。重新加载内存中的页面数据。(重启——>onStart)
  • onNewIntent:重用已有的活动实例

1.各状态之间的切换过程

打开新页面的方法调用顺序为:
onCreate→onStart→onResume
关闭旧页面的方法调用顺序为:
onPause→onStop→onDestroy
在这里插入图片描述
onCreate和onDestroy主要保存信息的代码

package com.sunofbeaches.activitylifecircledemo;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

    private EditText mInputBox;
    private SharedPreferences mMsgConfig;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //前面我们学习数据存储的时候学习过了sp的存储,这里我们就使用sp来存储这些简单的数据即可
        mMsgConfig = this.getSharedPreferences("MsgConfig", MODE_PRIVATE);
        //输入框控件
        mInputBox = (EditText) this.findViewById(R.id.message_content);
        //在sp里拿到内容
        String content = mMsgConfig.getString("content", null);
        //如果内容不为空的话,再设置到输入框里去,显示出来。
        if (content != null) {
            mInputBox.setText(content);
        }
    }

    @Override
    protected void onDestroy() {
        //销毁之前,拿到输入框框的内容,然后判断是否为空,不为空的话保存起来,为下一次进入的时候显示出来。
        String content = mInputBox.getText().toString().trim();
        if (!TextUtils.isEmpty(content)) {
            mMsgConfig.edit().putString("content", content).commit();
        }
        super.onDestroy();
    }
}

设置主题为透明:
android:theme=“@android:style/Animation.Translucent”

2.横竖屏切换Activity生命周期的变化

切换的话,Activity执行了onPause,再执行onStop和onDestroy方法。 也就是说,它先是走完了自己的生命周期,再重新开始。

对于横竖屏生命周期的总结是:先销毁掉原来的生命周期,然后再重新跑一次。
但是,这样子是不是会有问题呢?有些场景下: 比如说,做游戏开发 。横竖屏的切换,生命周期重新加载,那么当前页面的数据也会重新开始了。

那怎么样处理横竖屏的生命周期呢?

第一种方法,写它横竖屏,也就是说,指定该Activity是横屏或者是竖屏,在那里修改呢? 在配置文件里修改:
在这里插入图片描述
假设说,我们修改为横屏,一般来说,游戏横屏的比较多嘛,比较方便操作。

    <activity android:name=".LandscapeActivity"
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

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

修改成横屏了,我们再跑一次。发现它直接就横屏显示了。
在这里插入图片描述
接着我们点击切换横竖屏的按钮:
在这里插入图片描述
生命周期没有发生变化,但是,屏幕却竖起来了。

那有没有一种方式,能让屏幕随着屏幕的旋转而旋转,但是并不硬性生命周期的变化呢?

是可以的呢,我们需要设置一下忽略的配置变化就可以啦!

怎么设置呢?
在这里插入图片描述

<activity android:name=".LandscapeActivity"
            android:configChanges="orientation|screenSize|keyboardHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

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

可以知道生命周期并没有发生改变,但是我们UI已经切换过来了,对吧!

7、Activity的启动模式

1.创建基本的Activity

在这里插入图片描述

2.Activity 的启动模式

在这里插入图片描述

3.在配置文件中指定启动模式

最基本最直接的方式就是直接修改AndroidManifest.xml里的属性配置即可,比如说:
在这里插入图片描述

launchMode属性的取值说明见下表:

launchMode属性值说明
standard标准模式,无论何时启动哪个活动,都是重新创建该页面的实例并放入栈顶。如果不指定launchMode属性,则默认为标准模式
singleTop启动新活动时,判断如果栈顶正好就是该活动的实例,则重用该实例;否则创建新的实例并放入栈顶,也就是按照standard模式处理
singleTask启动新活动时,判断如果栈中存在该活动的实例,则重用该实例,并清除位于该实例上面的所有实例;否则按照standard模式处理
singleInstance启动新活动时,将该活动的实例放入一个新栈中,原栈的实例列表保持不变

新建线程任务:new Handler()

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private int time = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Handler handler = new Handler();
        handler.post(new Runnable() {

            @Override
            public void run() {
                if (time < 5) {
                    startActivity(new Intent(MainActivity.this, NewActivity.class));
                    Log.d(TAG, "start new activity...");
                    time++;
                    handler.postDelayed(this, 1000);
                }
            }
        });

    }
}

  • 14
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值