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"><Buttonandroid: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"><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="中间"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@id/button"android:layout_centerHorizontal="true"android:text="我在中间的上面"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_toLeftOf="@id/button"android:text="我在中间的左边"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_toRightOf="@id/button"android:text="我在中间的右边"/><Buttonandroid: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要书写正确

<Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="中间"/><Buttonandroid: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"?>
<AbsoluteLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid: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><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="按钮1"android:layout_weight="1"></Button><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="按钮2"android:layout_weight="1"></Button><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="按钮3"android:layout_weight="1"></Button></TableRow><TableRow><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="按钮4"android:layout_weight="1"></Button><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="按钮5"android:layout_weight="1"></Button><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="按钮6"android:layout_weight="1"></Button></TableRow><TableRow><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="按钮7"android:layout_weight="1"></Button><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="按钮8"android:layout_weight="1"></Button><Buttonandroid: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"><!--通常用作播放器暂停的界面--><Viewandroid: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"

<Buttonandroid: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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.table_layout);//找控件initViev();//设置点击事件initClickEvent();}/*** 设置点击事件*/private void initClickEvent() {//第一种设置方式button01.setOnClickListener(new View.OnClickListener() {@Overridepublic 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);}/*** 重写该类的点击处理函数,处理所有按键的点击事件*/@Overridepublic 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//先拿到idint 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"><TextViewandroid: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" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="50dp"android:orientation="vertical"android:padding="30dp"><TextViewandroid: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"/><EditTextandroid: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"/><EditTextandroid: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"/><Buttonandroid:id="@+id/loginButton"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="登录"android:textSize="20dp"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:id="@+id/passwordIsNull"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="忘记密码?"android:textSize="15dp"android:textColor="@color/white"/><TextViewandroid: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;@Overrideprotected 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);}@Overridepublic 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()函数

@Overrideprotected 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"><applicationandroid: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"><activityandroid: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 {@Overrideprotected 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.实现监听代码

@Overridepublic 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,多少MBString 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;@Overrideprotected 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);//拿到SharedPreferencessettings_info = this.getSharedPreferences("settings_info", MODE_PRIVATE);//获取数据回显boolean state = settings_info.getBoolean("state", false);is_switch.setChecked(state);}@Overridepublic void onClick(View v) {}@Overridepublic 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);}/**** 当第一次创建数据库的时候被调用*/@Overridepublic 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());}@Overridepublic 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 {@Overrideprotected 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";@Testpublic 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 {@Overrideprotected 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"><TextViewandroid: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的组成部分:

元素名称 设置方法 说明与用途
Component setComponent 组件,它指定意图的来源与目标
Action setAction 动作,它指定意图的动作行为
Data setData 即Uri,它指定动作要操纵的数据路径
Category addCategory 类别,它指定意图的操作类别
Type setType 数据类型,它指定消息的数据类型
Extras putExtras 扩展信息,它指定装载的包裹信息
Flags setFlags 标志位,它指定活动的启动标志

前面要跳转的话需要创建一个意图对象,也就是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 {@Overrideprotected 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";@Overrideprotected 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>() {@Overridepublic User createFromParcel(Parcel in) {return new User(in);}@Overridepublic 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;}@Overridepublic int describeContents() {return 0;}@Overridepublic 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;@Overrideprotected 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);}@Overridepublic 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*/@Overrideprotected 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;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_pay);//初始化initView();//设置点击事件initViewEvent();}private void initViewEvent() {recharge.setOnClickListener(new View.OnClickListener() {@Overridepublic 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() {@Overridepublic 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"><EditTextandroid:id="@+id/money"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:hint="请输入充值金额"android:inputType="number" /><Buttonandroid:id="@+id/recharge"android:layout_width="match_parent"android:text="充值"android:textSize="20dp"android:layout_height="wrap_content"/><Buttonandroid: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;@Overrideprotected 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);}}@Overrideprotected 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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final Handler handler = new Handler();handler.post(new Runnable() {@Overridepublic void run() {if (time < 5) {startActivity(new Intent(MainActivity.this, NewActivity.class));Log.d(TAG, "start new activity...");time++;handler.postDelayed(this, 1000);}}});}
}

Android开发学习(基础)相关推荐

  1. Android开发学习——基础学习

    在微信公众号上,发现一个自学android的一个文章,觉得不错.对其进行小小总结,整理给大家. 1. 基础UI学习 Button/TextView/EditText/CheckBox/ImageVie ...

  2. 《Java和Android开发学习指南(第2版)》——第2章,第2.10节本章小结

    本节书摘来自异步社区<Java和Android开发学习指南(第2版)>一书中的第2章,第2.10节本章小结,作者 [加]Budi Kurniawan,更多章节内容可以访问云栖社区" ...

  3. 如何自学?Android开发学习路线指南,最详资料解析

    现状 后端转 Android 我该从何处下手,现在学习 android 晚吗? 不晚,一点也不晚.因为Android系统在全球范围内仍然是最受欢迎的移动操作系统之一,而且随着智能手机和移动应用的普及, ...

  4. 《Android Studio应用开发实战详解》——第1章,第1.5节Android开发学习路线图

    本节书摘来自异步社区<Android Studio应用开发实战详解>一书中的第1章,第1.5节Android开发学习路线图,作者 王翠萍,更多章节内容可以访问云栖社区"异步社区& ...

  5. 【嵌入式Android开发学习攻略】手把手教你循序渐进的学习

    [嵌入式Android开发学习攻略]手把手教你循序渐进的学习 学习嵌入式主要有以下几个方面: C语言:C是必须学的,它是最基础的 操作系统:Linux.Android   目前Linux是主流,这个一 ...

  6. android开发学习大体思路

    android开发学习: android学习的前提是java基础.如果你没有好的java基础,那就赶紧补充,我在这里不做介绍. android是基于linux的,如果你要做底层的东西,可以买一些关于l ...

  7. Android开发学习Part3

    Android开发学习Part3 可点击的图片 输入控件 菜单和选择器 用户导航 选项卡导航 RecyclerView使用 可点击的图片 心得: 现在的FloatingActionButton写法如下 ...

  8. android培训内容明细,记录Android开发学习

    记录Android开发学习 Menu菜单学习 1.掌握Android中菜单的创建. 2.掌握Intent信使组件. 创建菜单Menu 我们模仿微信菜单栏学习,创建一个于微信菜单栏相似的菜单 那么我们应 ...

  9. android开发学习之路——连连看之游戏逻辑(五)

    GameService组件则是整个游戏逻辑实现的核心,而且GameService是一个可以复用的业务逻辑类. (一)定义GameService组件接口 根据前面程序对GameService组件的依赖, ...

  10. Android开发学习---使用Intelij idea 13.1 进行android 开发

    Android开发学习---使用Intelij idea 13.1 进行android 开发 原文:Android开发学习---使用Intelij idea 13.1 进行android 开发 1.为 ...

最新文章

  1. python下载教程win10-win10系统下如何安装Python软件
  2. python整理excel数据-Python 自动整理 Excel 表格
  3. 创建 .m2 文件夹
  4. 前端模版引擎选择指南
  5. WINDOWS 逻辑坐标 设备坐标 屏幕坐标 客户区坐标
  6. MySQL--更新自增列的潜在风险
  7. 创建一个显示所有预定义系统颜色的ListBox
  8. 第六篇:汇编基础指令讲解
  9. 期货品种产业链图表_农业
  10. python开发的代码如何加密_python 代码加密
  11. 微型计算机原理控制,微机原理与控制技术(试题).doc
  12. LARS算法---十折交叉验证
  13. web系统整体性能测试
  14. 三星手机使用应用沙盒动态修改sdk数据
  15. 基于TCP的简单服务器
  16. 设计模式笔记--访问者模式
  17. c语言文件尾没有newline字符,关于C++:”文件末尾无新行”编译器警告“No newline at end of file”...
  18. day06 Elasticsearch搜索引擎2
  19. python3 实现自动生成入账记录表
  20. 焱融科技与趋动科技携手解决一站式存算难

热门文章

  1. Matlab:控制绘图函数如何选择颜色和线型
  2. while(!x)的含义
  3. HCIA-Datacom题库2023最新放送,能答对60%就拿下证书
  4. linux用c创建数据库,使用c语言实现linux数据库的操作
  5. 什么是训练集、验证集和测试集?
  6. 370、合格弱电工程师必备的120条基础常识
  7. linux apache php loadmodule,解决windows下安装Apache+PHP出现LoadModule takes two
  8. 2023Matlab初级教程- 第一章 初识Matlab与界面介绍
  9. 关于 speex 和 ogg 的一个没事找事儿的回答
  10. 三、WEB漏洞-逻辑越权