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"><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_prefs
以xml
的文件形式保存起来。它有一个特点,内容保存都是是键值对的方式进行保存。(如下图)
获取数据回显:
//拿到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开发学习(基础)相关推荐
- Android开发学习——基础学习
在微信公众号上,发现一个自学android的一个文章,觉得不错.对其进行小小总结,整理给大家. 1. 基础UI学习 Button/TextView/EditText/CheckBox/ImageVie ...
- 《Java和Android开发学习指南(第2版)》——第2章,第2.10节本章小结
本节书摘来自异步社区<Java和Android开发学习指南(第2版)>一书中的第2章,第2.10节本章小结,作者 [加]Budi Kurniawan,更多章节内容可以访问云栖社区" ...
- 如何自学?Android开发学习路线指南,最详资料解析
现状 后端转 Android 我该从何处下手,现在学习 android 晚吗? 不晚,一点也不晚.因为Android系统在全球范围内仍然是最受欢迎的移动操作系统之一,而且随着智能手机和移动应用的普及, ...
- 《Android Studio应用开发实战详解》——第1章,第1.5节Android开发学习路线图
本节书摘来自异步社区<Android Studio应用开发实战详解>一书中的第1章,第1.5节Android开发学习路线图,作者 王翠萍,更多章节内容可以访问云栖社区"异步社区& ...
- 【嵌入式Android开发学习攻略】手把手教你循序渐进的学习
[嵌入式Android开发学习攻略]手把手教你循序渐进的学习 学习嵌入式主要有以下几个方面: C语言:C是必须学的,它是最基础的 操作系统:Linux.Android 目前Linux是主流,这个一 ...
- android开发学习大体思路
android开发学习: android学习的前提是java基础.如果你没有好的java基础,那就赶紧补充,我在这里不做介绍. android是基于linux的,如果你要做底层的东西,可以买一些关于l ...
- Android开发学习Part3
Android开发学习Part3 可点击的图片 输入控件 菜单和选择器 用户导航 选项卡导航 RecyclerView使用 可点击的图片 心得: 现在的FloatingActionButton写法如下 ...
- android培训内容明细,记录Android开发学习
记录Android开发学习 Menu菜单学习 1.掌握Android中菜单的创建. 2.掌握Intent信使组件. 创建菜单Menu 我们模仿微信菜单栏学习,创建一个于微信菜单栏相似的菜单 那么我们应 ...
- android开发学习之路——连连看之游戏逻辑(五)
GameService组件则是整个游戏逻辑实现的核心,而且GameService是一个可以复用的业务逻辑类. (一)定义GameService组件接口 根据前面程序对GameService组件的依赖, ...
- Android开发学习---使用Intelij idea 13.1 进行android 开发
Android开发学习---使用Intelij idea 13.1 进行android 开发 原文:Android开发学习---使用Intelij idea 13.1 进行android 开发 1.为 ...
最新文章
- python下载教程win10-win10系统下如何安装Python软件
- python整理excel数据-Python 自动整理 Excel 表格
- 创建 .m2 文件夹
- 前端模版引擎选择指南
- WINDOWS 逻辑坐标 设备坐标 屏幕坐标 客户区坐标
- MySQL--更新自增列的潜在风险
- 创建一个显示所有预定义系统颜色的ListBox
- 第六篇:汇编基础指令讲解
- 期货品种产业链图表_农业
- python开发的代码如何加密_python 代码加密
- 微型计算机原理控制,微机原理与控制技术(试题).doc
- LARS算法---十折交叉验证
- web系统整体性能测试
- 三星手机使用应用沙盒动态修改sdk数据
- 基于TCP的简单服务器
- 设计模式笔记--访问者模式
- c语言文件尾没有newline字符,关于C++:”文件末尾无新行”编译器警告“No newline at end of file”...
- day06 Elasticsearch搜索引擎2
- python3 实现自动生成入账记录表
- 焱融科技与趋动科技携手解决一站式存算难
热门文章
- Matlab:控制绘图函数如何选择颜色和线型
- while(!x)的含义
- HCIA-Datacom题库2023最新放送,能答对60%就拿下证书
- linux用c创建数据库,使用c语言实现linux数据库的操作
- 什么是训练集、验证集和测试集?
- 370、合格弱电工程师必备的120条基础常识
- linux apache php loadmodule,解决windows下安装Apache+PHP出现LoadModule takes two
- 2023Matlab初级教程- 第一章 初识Matlab与界面介绍
- 关于 speex 和 ogg 的一个没事找事儿的回答
- 三、WEB漏洞-逻辑越权