参考书籍:基于 Android 的物联网应用开发 (北京新大陆时代教育科技有限公司)

# 简识

# Gradle

Gradle 是一个项目自动 化构建工具,帮我们做了依赖、打包、部署、发布、各种渠道的差异管理等

# AndroidManifest.xml - 清单文件

  • 这个文件中包含了 APP 的配置信息系统,需要根据里面的内容运行 APP 的代码,显示界面。

# 布局

# 布局通用属性

# 常用属性

属性 作用
gravity 控制组件所包含的子元素的对齐方式
layout_gravity 控制组件在父容器的对齐方式
layout_width 布局宽度
layout_height 布局高度
wrap_content 大小由包裹的内容大小自动伸缩
match_parent (大小填充父容器)(竖向排列则竖向填充,横向则横向填充)

# 边距的使用

  • 在 Android 的布局中,经常需要给控件和容器设置一些边距.
  • 控件与控件之间的边距称为外边距 (margin),控件与其内容的边距称为内边距 (padding)。
控件属性 功能描述 补充
android:layout_margin 设置四个方向相同的外边距
android:layout_marginLeft 设置左边的外边距
android:layout_marginTop 设置上边的外边距
android:layout_marginRight 设置右边的外边距
android:layout_marginBottom 设置下边的外边距

同理,可以使用属性 android:padding 设置 4 个方向相同的内边距 (padding),也可以加上方向分别设置某个方向的内边距

# LinearLayOut (线性布局)

属性 方法 作用
orientation vertical/horizontal (布局的排列方式) 垂直 / 水平
weight 在容器中按比例划分

# Divider 分割线

  • 为线性布局设置分割线图片
属性 (showDividers) 位置
none
beginning 开始
end 结束
middle 每两个组件间

使用方法

android:divider="@drawable/ktv_line_div" <!-- 设置分割线图片 -->
android:showDividers="middle"  <!-- 设置分割线位置 -->
android:dividerPadding="10dp"  <!-- 设置分割线边距 -->

# RelativeLayout (相对布局)

相对布局是通过相对定位的方式指定控件的位置:以其他控件或父容器为参照物,摆放控件位置。

在设计相对布局时要遵循控件之间的依赖关系,后放入控件的位置依赖于先放入的控件

# 设置控件相对于父容器的位置

控件属性 功能描述
android:layout_centerHorizontal 设置当前控件位于父容器的水平居中位置
android:layout_centerVertical 设置当前控件位于父容器的垂直居中位置
android:layout_centerInParent 设置当前控件位于父容器的中央位置
android:layout_alignParentTop 设置当前控件与父容器顶部对齐
android:layout_alignParentLeft 设置当前控件与父容器左对齐
android:layout_alignParentRight 设置当前控件与父容器右对齐
android:layout_alignParentBottom 设置当前控件与父容器底部对齐

# 设置控件相对于某个控件的位置

控件属性 功能描述
android:Layout_above 设置当前控件位于某个控件的上方
android:Layout_below 设置当前控件位于某个控件的下方
android:Layout_toLeftof 设置当前控件位于某个控件的左侧
android:Layout_toRightof 设置当前控件位于某个控件的右侧
android:Layout_alignTop 设置当前控件与某个控件的顶部对齐
android:Layout_alignBottom 设置当前控件与某个控件的底部对齐
android:Layout_alignLeft 设置当前控件与某个控件左对齐
android:Layout_alignRight 设置当前控件与某个控件右对齐

# ConstraintLayout (约束布局)

ConstraintLayout 是 Google 在 2016 年的 Google I/O 大会上提出的一个可以灵活控制子控件位置和大小的新布局。它已是目前 Android Studio 中项目的默认布局


# Relative positioning (相对定位)

控件属性 功能描述
app:layout_constraint Left_toLeftOf 控件与某控件或父容器的左侧对齐 设置居中
app:layout_constraint Left_toRightOf 控件在某控件的右侧
app:layout_constraint Right_toLeftOf 控件在某控件的左侧
app:layout_constraint Right_toRightOf 控件与某控件或父容器的右侧对齐 设置居中
app:layout_constraint Top_toTopOf 控件与某控件或父容器的顶部对齐 设置居中
app:layout_constraint Top_toBottomOf 控件在某控件的下方
app:layout_constraint Bottom_toTopOf 控件在某控件的上方
app:layout_constraint Bottom_toBottomOf 控件与某控件或父容器的底部对齐 设置居中
app:layout_constraint Baseline_toBaselineOf 控件与某控件文本基线对齐

# Circular positioning (圆形定位)

圆形定位指的是可以用 一个角度 和 一个半径 来约束两个控件的中心

控件属性 功能描述
app:Layout_constraintCircle 设置约束的圆心位置
app:Layout_constraintCircleAngle 设置约束角度
app:Layout_constraintCircleRadius 设置约束半径

# Chains (链式约束)

链式约束,即一组控件通过一个双向的约束关系链接起来。链式约束能够对一组在水平或竖直方向互相关联控件的属性进行统一管理

属性 作用
chain_spread 展开元素
chain_spread_inisde 展开,但是链的两端贴近 parent
chain_packed 链的元素将被打包在一起

# Centering positioning and bias (居中以及设置偏差)

在约束布局中,把控件放在布局中央,需要设置四个方向的约束

同理,如果要单独设置某个方向的居中,只需要将对应两边对齐即可

# 约束布局中的偏移属性

控件属性 功能描述 取值范围 (左~右)
app:layout_constraintHorizontal_bias 水平偏移 0~1
app:layout_constraintVertical_bias 垂直偏移 0~1

# 控件

# 通用控件方法

控件属性 功能描述 单位
android:text 设置显示文本
android:textColor 设置文本的颜色
android:textSize 设置文字大小 推荐 sp
android:textStyle 设置文字样式,如 bold (粗体)、italic (斜体)、bolditalic (粗斜体)
android:height 设置文本区域的高度
android:width 设置文本区域的宽度
android:Visibility 设置控件的显示模式 visible/gone/invisible

# TextView (文本框)

主要作用于在界面上显示一段文本信息

控件属性 功能描述 单位
android:maxlength 设置文本长度,超出不显示
android:shadowColor 设置阴影颜色,需要与 shadowRadius 一起使用
android:shadowRadius 设置阴影的模糊程度
android:shadowDx 设置阴影在水平方向的偏移
android:shadowDy 设置阴影在竖直方向的偏移
drawblexxx 设置图片在文字的方向 top/buttom/left/right
autoLink 识别链接类型
singleLine 自动换行 默认 false

drawblexxx 设置图片大小

text.setBounds(100,0,200,200)// 设置坐标点,从离文字最左边开始 100dp 处到 200dp 处 宽是:从文字上方 0dp 处往上延伸 200dp!

设置字符

可以使用 id.setText 往控件中输入字符


# EditText (输入框)

  • 允许用户在控件里输入和编辑内容
  • EditText 继承自 TextView , 所有 EditText 可以使用 TextView 的属性

特有属性

控件属性 功能描述
android:hint 设置 EditText 没有输入内容时显示的提示文本
android:inputType 设置输入文本的类型,如 textPassword 表示输入的文本为密码类型(文本将以 “.” 显示),phone 表示电话号码类型,date 表示日期类型等
singleline 单行输入不换行
minlines 设置最小行的行数
maxlines 设置最大行行数

Tips: 禁止软键盘弹出

原理:设置 EditText 失去焦点,从而阻止软键盘的弹出

XML 法 (要放在其上一层布局)

android:focusable="false"
android:focusableInTouchMode="false"

提取文字

String name =  ed_name.getText().toString().trim();

检测是否为空

TextUtils.isEmpty(...)

密码可见度

// 密码不可见
setTransformationMethod(HideReturnsTransformationMethod.getInstance());
// 密码可见
setTransformationMethod(PasswordTransformationMethod.getInstance());

# Button (按钮)

是程序与用户交互的一个控件

三种注册点击的监听器常用方式

  1. 使用 android:onClick
  2. 使用匿名内部类的方式
  3. 在当前 Activity 中实现 onClickListener 接口

使用 android:onClick

创建一个方法,放入 View view 参数,即可在 xml 文件中调用

# ToggleButton (开关按钮)

  • 独立切换按钮
属性 作用
android:disabledAlpha 设置按钮在禁用时的透明度
android:textOff 按钮没有被选中时显示的文字
android:textOn 按钮被选中时显示的文字

# Switch (开关)

属性 作用
showText 设置 on/off 的时候是否显示文字,boolean
splitTrack 是否设置一个间隙,让滑块与底部图片分隔,boolean
switchMinWidth 设置开关的最小宽度
switchPadding 设置滑块内文字的间隔
track 底部的图片
thumb 滑块的图片

# RadioButton (单选按钮)

  • RadioButton (单选按钮) 需要与 RadioGroup (单选组合框)配合使用,才能实现多个单选按钮间的互斥效果。
  • 单选按钮控件需要设置 id 属性,否则有可能不会出现互斥效果

如何使用

  1. 在 xml 中使用 RadioGroup, 在其中加入 RadioButton 来显示;
  2. 使用 setOnCheckedChangeListener 方法获取单选框改变时的值
setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                id.getText()
            }
        });

在 xml 中可以用 checked 来设置默认状态是否会被选中


# CheckBox (复选框)

  • CheckBox (复选框)又称为多选按钮,它允许用户一次选择多个选项.

使用

  • 设置监听即可
checkBox1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {  
    @Override  
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {  
        // 处理选中事件  
    }  
});

# ImageView (图片视图)

三种开头

  1. FIT 开头
  2. CENTER 开头
  3. ScaleType.MATRIX 开头
控件属性 功能描述
android:src 把图片当内容显示 (以原图大小显示)
android:background 把图片当背景显示 (根据控件大小进行伸缩)

当使用 SRC 属性来显示图片时,可以配合使用 android:scaleType 来设置显示的图片怎么缩放

使用 java 设置背景时需要使用

setbackgroundresoure(R.id.图片)

FIT_开头

共同点:都会对图片进行缩放

控件属性 功能描述 补充
ScaleType.FIT_CENTER 图片会被 **** 等比缩放到能够填充控件大小,并居中展示 默认模式
ScaleType.FIT_START 图片等比缩放到控件大小,并放置在控件的上边或左边展示
ScaleType.FIT_END 图片等比缩放到控件大小,并放置在控件的下边或右边展示
ScaleType.FIT_XY 图片缩放到控件大小,完全填充控件大小展示

CENTER_ 开头

共同点:居中显示,图片的中心点会与 Imageview 的中心点重叠

控件属性 功能描述 补充
ScaleType.CENTER 不使用缩放, ImageView 会展示图片的中心部分 如果图片的大小小于控件的宽高,那么图片会被居中显示
ScaleType.CENTER_CROP 图片会被等比缩放直到完全填充整个 ImageView ,并居中显示
ScaleType.CENTER_INSIDE 图片将被等比缩放到能够完整展示在 ImageView 中并居中 如果图片大小小于控件大小,那么直接居中展示该图片

ScaleType.MATRIX 开头

  • 在这八种 ScaleType 中,这个模式就是重点了。该模式需要与 ImageView.setImageMatrix (Matrix matrix) 配合使用,因为该模式需要用于指定一个变换矩阵用于指定图片如何展示.
  • 前面的 7 种模式都是通过 ImageView 在内部生成了相应的变换矩阵,等于是提供了该模式的一种特定值
  • 使用这个模式只要传入相应矩阵,也就能实现上述七种显示效果。

使用方法

  1. 在使用时,需要先调用
imageView.setScaleType(ImageView.ScaleType.MATRIX);
  1. 再调用
imageView.setImageMatrix(matrix);
  1. 注意顺序不要搞错,否则会出现问题的
imageView.setScaleType(ImageView.ScaleType.MATRIX);  // 设置为矩阵模式
 Matrix matrix = new Matrix();           // 创建一个单位矩阵
 matrix.setTranslate(100, 100);          // 平移 x 和 y 各 100 单位
 matrix.preRotate(30);                   // 顺时针旋转 30 度
 imageView.setImageMatrix(matrix);       // 设置并应用矩阵

设置图片转动

ObjectAnimator rotationAnimator = ObjectAnimator.ofFloat(fanImage, "rotation", 0f, 360f);// 设置旋转角度
        rotationAnimator.setDuration(500); // 设置动画时长为 2 秒
        rotationAnimator.setInterpolator(new LinearInterpolator()); // 设置线性插值器
        rotationAnimator.setRepeatCount(ValueAnimator.INFINITE); // 设置动画无限循环
        rotationAnimator.setRepeatMode(ValueAnimator.RESTART); // 设置动画循环模式为立即重新开始
        rotationAnimator.start(); // 开始动画

# ProgressBar (进度条)

  • 进度条的两种种类

    1. 圆环形进度条 (默认)
    2. 水平显示进度
  • 如果需要改变默认的样子则在里面加入 style:@style/Widget.AppCompat.ProgressBar.Horizontal

android:max:进度条的最大值
android:progress:进度条已完成进度值
android:progressDrawable:设置轨道对应的Drawable对象
android:indeterminate:如果设置成true,则进度条不精确显示进度
android:indeterminateDrawable:设置不显示进度的进度条的Drawable对象
android:indeterminateDuration:设置不精确显示进度的持续时间
android:secondaryProgress:二级进度条,类似于视频播放的一条是当前播放进度,一条是缓冲进度,前者通过progress属性进行设置!
getMax():返回这个进度条的范围的上限
getProgress():返回进度
getSecondaryProgress():返回次要进度
incrementProgressBy(int diff):指定增加的进度
isIndeterminate():指示进度条是否在不确定模式下
setIndeterminate(boolean indeterminate):设置不确定模式下

# SeekBar (拖动条)

  • 属性

    • SeekBar 是 ProgressBar 的子类,ProgressBar 主要用来显进度,但是不能和用户进行交互.

    • 而 SeekBar 可以供用户行拖动改变进度值。

  • 一般步骤

    1. 初始化控件

    2. 注册拖动条监听器 setOnseekBarChangeListener () 方法为 SeekBar 注册进度值变化的监听器

  • 三个回调方法

    OnProgressChanged(拖动条,进度值,是否人为触发);// 进度发生改变时会触发
    onStartTrackingTouch();// 按住 SeekBar 时会触发
    onStopTrackingTouch();// 放开 SeekBar 时触发
    属性 作用
    max 滑动条最大值
    progress 滑动条当前进度
    secondaryProgress 二级滑动条进度
    thumb 滑块图片

# Toolbar (标题栏)

作用

代替原本的标题栏,扩展性更高

步骤

  1. 在 Android.xml 中将原来的标题栏关闭
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
  1. 在 XML 里加上 Toolbar
  2. 在 java 中给 Toolbar 进行设置
// 设置 menu
        toolbar.inflateMenu(R.menu.menu);
        // 设置 logo
        toolbar.setLogo(R.drawable.logo);
        // 监测 menu
        toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem menuItem) {
                return false;
            }
        });
// 以下是实现标题栏返回键的步骤
setSupportActionBar(toolbar);// 设置为活动 //setSupportActionBar 是 AppCompatActivity 的方法
getSupportActionBar().setDisplayHomeAsUpEnabled(true);// 启用左上角返回键
getSupportActionBar().setHomeButtonEnabled(true);// 控制左上角图标的显示和点击事件
// 然后重写 onOptionsItemSelected 方法,处理返回键的点击事件
@Override  
public boolean onOptionsItemSelected(MenuItem item) {  
    switch (item.getItemId()) {  
        case android.R.id.home:  
            // 处理返回键点击事件  
            onBackPressed();  
            return true;  
        default:  
            return super.onOptionsItemSelected(item);  
    }  
}
// 当用户点击返回键时,会调用 onBackPressed () 方法,该方法会模拟用户按下物理返回键的行为,返回到上一级页面。

如何创建 menu 文件:

​ res 右键,选择 Android Resource File, 选择类型为 menu


# 对话框

此章节含有以下三种对话框

  1. 提示信息对话框
  2. 单选对话框
  3. 多选对话框

步骤

  1. 修改 xml 文件的布局代码
  2. 在 MainActivity 中分别对 4 个按钮注册点击监听器

# 提示信息对话框

提示信息对话框主要是用来显示提示信息的,并通常具有 “确定” 和 “取消” 按钮,

  1. 创建 AlertDialog 构造器 Builder 对象,AlertDialog 建议使用 android.support.v7.app 包下的
private void showMsgDialog(){
         AlertDialog.Builder builder = new AlertDialog.Builder(this);
     }
  1. 设置对话框标题,提示信息,图标
  2. 添加确定按钮 (第一个参数为按钮显示的信息,第二个参数为按钮的监听器,如无需作用,可以填 null)
  3. 添加取消按钮
  4. 创建并显示对话框
AlertDialog alert = new builder 
	.setTitle("提示信息对话框")
	.setMessage("是否确定退出")
	.setIcon(R.mipmap.ic_launcher)
    .setNegativeButton("取消", new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog, int which) {
       Toast.makeText(mContext, "你点击了取消按钮~", Toast.LENGTH_SHORT).show();
          }
       }).setPositiveButton("确定", new DialogInterface.OnClickListener() {
        @Override
          public void onClick(DialogInterface dialog, int which) {
            Toast.makeText(mContext, "你点击了确定按钮~", Toast.LENGTH_SHORT).show();
             }
          }).setNeutralButton("中立", new DialogInterface.OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {
                    Toast.makeText(mContext, "你点击了中立按钮~", Toast.LENGTH_SHORT).show();
                 }
            }).create();     // 创建 AlertDialog 对象
     	alert.show();        // 显示对话框

# 单选对话框

属性

  • 一般用于从选项中选择一项
  • 是使用 AlertDialog 的 构造器 Builder 对象调用 setSingleChoiceItems () 方法 设置的
  1. 基本使用流程
  • 创建 Builder 对象
  • 设置对话框标题
  • 设置对话框图标
  • 设置单选选项

setSingleChoiceItems () 方法的三个参数

  1. 选项数组,用于显示选项内容
  2. 设置默认选中的选项,0 表示选中第一个选项
  3. 选项点击的监听器。运行程序,并点击单选对话框按钮

# 多选对话框

属性

  • 用作从选项中选择多项
  • 它是通过 AlertDialog 的 构造器 Builder 对象调用 setMultiChoiceItems () 方法 设置的

一般步骤 (详情见书 P65 页)

  1. 设置对话框标题
  2. 设置对话框图标
  3. 设置多选选项
  4. 添加确定按钮
  5. 创建并显示对话框

setMultiChoiceItems () 方法的三个参数

  1. 选项数组,用于显示选项内容;
  2. boolean 数组,用于设置默认选中的选项,new boolean, 表示默认选中第二个与第三个选项,如果没有默认选中的选项,则参数可填 null
  3. 选项点击监听器

# 自定义对话框

为什么存在

  • 为了提高用户体验,当系统提供的的对话框不能满足需求时,可以根据项目需求自定义对话框

步骤 (详情见书 p66)

  1. 创建一个自定义对话框的布局
  2. 在 Java 目录的包名下创建一个 MyDialog 类继承 android:app.Dialog (主要用于初始化自定义对话框中的控件以及注册按钮的监听器)
  3. 在 MainActicity 中对 showCustomDialog () 方法进行实现,显示一个自定义对话框

# progressDialog (进度条对话框)

// 普通的圆形进度条对话框
ProgressDialog.show(MainActivity.this, "资源加载中", "资源加载中,请稍后...",false,true);
// 参数依次为,上下文,标题,内容,是否显示进度,是否可以用取消按钮关闭
// 定义非动态的条形进度条对话框
ProgressDialog pd1 = new ProgressDialog(Context);
// 依次设置标题,内容,是否用取消按钮关闭,是否显示进度
pd1.setTitle("软件更新中");
pd1.setMessage("软件正在更新中,请稍后...");
pd1.setCancelable(true);
// 这里是设置进度条的风格,HORIZONTAL 是水平进度条,SPINNER 是圆形进度条
pd1.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd1.setIndeterminate(true);
// 调用 show () 方法将 ProgressDialog 显示出来
pd1.show();
// 定义动态的条形进度条对话框
// 初始化属性
progressStart = 0;
add = 0;
// 依次设置一些属性
ProgressDialog pd2 = new ProgressDialog(context);
pd2.setMax(MAXVALUE);
pd2.setTitle("文件读取中");
pd2.setMessage("文件加载中,请稍后...");
// 这里设置为不可以通过按取消按钮关闭进度条
pd2.setCancelable(false);
pd2.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
// 这里设置的是是否显示进度,设为 false 才是显示的哦!
pd2.setIndeterminate(false);
pd2.show();
// 这里的话新建一个线程,重写 run () 方法,
new Thread()
{
    public void run()
    {
        while(progressStart < MAXVALUE)
        {
            // 这里的算法是决定进度条变化的,可以按需要写
            progressStart = 2 * usetime() ;
            // 把信息码发送给 handle 让更新界面
            hand.sendEmptyMessage(123);
        }
    }
}.start();

# DrawerLayout (官方侧滑菜单)

  • 最外层必须是 DrawerLayout
  • 必须指定侧滑试图的 android:layout_gravity 属性 (Start 从左向右滑出,end 从右向左滑出)
  • 设置监听方法: DrawerLayout.setDrawerListener (new DrawerLayout.DrawerListener);

xml 示例

<android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <!-- 内容区 -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="20dp"
                android:text="内容区"
                android:textSize="20sp"/>
            <Button
                android:id="@+id/btn_open_left"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="打开左边"/>
            <Button
                android:id="@+id/btn_open_right"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="打开右边"/>
        </LinearLayout>
        <!-- 左边菜单 -->
        <android.support.design.widget.NavigationView
            android:id="@+id/navigation_view"
            android:layout_width="260dp"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:headerLayout="@layout/drawer_header"
            app:menu="@menu/menu_drawer_left"/>
        <!-- 右边菜单 -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="end"
            android:background="@color/black"
            android:gravity="center"
            android:orientation="vertical">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="20dp"
                android:text="右边菜单"
                android:textColor="@color/white"
                android:textSize="20sp"
                android:textStyle="bold"/>
            <Button
                android:id="@+id/btn_close_right"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="关闭"/>
        </LinearLayout>
    </android.support.v4.widget.DrawerLayout>

关联 Toolbar

//mDrawerLayout 与 mToolbar 关联起来
ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.open, R.string.close);
// 初始化状态
actionBarDrawerToggle.syncState();

# 吐司弹窗

Toast.makeText(this, "按钮一被点击了", Toast.LENGTH_LONG).show();

# 四大组件

# Activity

# Activity 的生命周期

# Oncreate()

它会在系统首次创建 Activity 时触发。Activity 会在创建后进入 “已创建” 状态,Activity 进入 “已开始” 状态,系统会相继调用 onStart()onResume() 方法。

# OnStart()

  1. 当 Activity 进入 “已开始” 状态时,系统会调用此回调

  2. onStart() 调用使 Activity 对用户可见,应用会为 Activity 进入前台并支持互动做准备

  3. 此方法会非常快速完成,此回调结束后,进入 已恢复 状态,系统调用 onResume () 方法

# Onresume()

  1. 这是一种应用与用户交互的状态,会保持到这种状态直到,该 Activity 不是此焦点 (eg : 接到来电、用户导航到另一个 Activity,或设备屏幕关闭。)

  2. 当 Activity 进入已恢复状态时,与 Activity 生命周期相关联的所有生命周期感知型组件都将收到 ON_RESUME 事件。这时,生命周期组件可以启用在组件可见且位于前台时需要运行的任何功能

  3. 当发生中断事件时,Activity 进入 “已暂停” 状态,系统调用 onPause() 回调。

  4. 如果 Activity 从 “已暂停” 状态返回 “已恢复” 状态,系统将再次调用 onResume() 方法.

# OnPause()

此方法表示 Activity 不再位于前台(尽管在用户处于多窗口模式时 Activity 仍然可见)

# OnStop()

  1. 如果您的 Activity 不再对用户可见,说明其已进入 “已停止” 状态,因此系统将调用 onStop() 回调。例如,当新启动的 Activity 覆盖整个屏幕时,可能会发生这种情况。

  2. 当 Activity 进入已停止状态时,生命周期组件可以停止在组件未显示在屏幕上时无需运行的任何资源.

# OnDestroy()

销毁 Activity 之前,系统会先调用 onDestroy() 。调用原因有以下两种情况

  1. Activity 即将结束(由于用户彻底关闭 Activity 或由于系统为 Activity 调用 finish()
  2. 由于配置变更(例如设备旋转或多窗口模式),系统暂时销毁 Activity

# OnRestart()

重启活动。重新加载内存中的页面数据

# OnNewlntent()

重用已有的活动实例


# Activity 的跳转

两种跳转模式

  1. 显式 Intent (精准)
  2. 隐式 Intent (模糊)

# 显式 Intent

按名称指定要启动的组件,创建显式启动 Activity 或服务时,系统将立即启动 Intent 对象中指定的应用组件

组件名称 方法名称 方法作用
Activity startActivity() 跳转到新的 (另一个) Activity
Service startService() 启动服务
bindService() 绑定服务
BroadcastReceiver sendBroadcasts() 发送无序广播
sendOrderdBroadcasts() 发送有序广播

三种构建方式

  1. 在 intent 的构造函数中指定
Intent intent = new Intent(FirstActivity.this, ThirdActivity.class);
  • 调用意图对象的 setclass 方法指定

  • 调用意图对象的 setComponent 方法指定


# Activity 的启动模式 (Task 栈)

栈:

后进先出 (LIFO),常用操作入栈 (push),出栈 (pop),处于最顶部的叫栈顶,最底部叫栈底

# 默认启动模式 standard

  • 按启动顺序压入 Task 栈 中,然后按顺序退出

# 栈顶复用模式 signleTop

  • 如果栈顶的 Activity 为我们要新建的,就不会重复创建新的 Activity

# 栈内复用模式 singleTask

  • 在创建了多个不同的 Activity 之后,如果再创建一个主界面,则直接弹出其它界面,回到主界面

# 全局唯一模式 singleInstance

  • 会为每一个目标 Activity 创建一个新的 Task 栈,并获得焦点
  • 如果已经创建过目标 Activity, 则之间将创建过的唤醒使用

# Service

  • Service 是 android 系统的四大组件之一,是一种长生命周期的,没有可视化界面,运行于后台的一种服务程序
  • Service 组件通常用于为其他组件提供后台服务或监控其他组件的运行状态
  • 即使应用被切换到后台或用户关闭了应用,Service 也可以继续运行

# Started Service

​ 被开启的 service 通过其他组件调用 startService () 被创建,这种 service 可以无限地运行下去,必须调用 stopSelf () 方法或者其他组件调用 stopService () 方法来停止它,当 service 被停止时,系统会销毁它

  • 启动方式:当你调用 startService() 时,Service 会立即启动(或如果它已经在运行,则不会重新创建),并且会执行其 onStartCommand() 方法。
  • 生命周期:Service 的生命周期与启动它的组件(如 Activity)的生命周期不直接相关。即使启动 Service 的 Activity 被销毁,Service 也会继续运行,直到它被明确地停止(通过调用 stopService() 或 Service 内部的 stopSelf() )。
  • 并发性:Service 可以在其自己的进程中运行(如果它在 AndroidManifest.xml 中声明为在单独的进程中),并且它可以同时处理来自多个客户端的请求。
  • 用途:常用于执行后台任务,如文件下载、位置更新等,这些任务不依赖于启动它的组件。

# Bounded Service

​ 被绑定的 service 是当其他组件(一个客户)调用 bindService () 来创建的,客户可以通过一个 IBinder 接口和 service 进行通信,客户可以通过 unbindService () 方法来关闭这种连接,一个 service 可以同时和多个客户绑定,当多个客户都解除绑定之后,系统会销毁 service

  • 启动方式:当你调用 bindService() 时,Service 会被启动(如果它尚未运行),并且客户端(如 Activity)会与 Service 建立连接。一旦连接建立,客户端可以访问 Service 中定义的接口,并与之进行交互。
  • 生命周期:Service 的生命周期与绑定到它的客户端组件的生命周期紧密相关。当最后一个客户端与 Service 断开连接时(通过调用 unbindService() ),如果 Service 中没有其他正在运行的回调(如 onStartCommand() 中的工作),则 Service 将被销毁。
  • 并发性:与 startService() 不同, bindService() 通常用于单个客户端与 Service 之间的交互,而不是处理来自多个客户端的请求。
  • 用途:常用于需要客户端与 Service 之间进行交互的场景,如音乐播放器中的媒体控制、与远程服务的通信等。

进程

​ Service 还有一个作用就是提升进程 (每个应用都是一个进程) 的优先级,进程的优先级指的是在 Android 系统中,会把正在运行的应用确定一个优先级,当内存空间不足时,系统会根据进程的优先级清理掉一部分进程占用的内存空间,以获得足够的内存空间以供新启用的应用运行。

  • 前台进程:应用程序存在 Activity 正位于前台,可见并可控
  • 可见进程:应用程序存在 Activity 处于局部可见状态,即局部可见却不可控
  • 服务进程:应用程序存在正在运行的 Service
  • 后台进程:应用程序的所有 Activity 均被置于后台,没有任何 Activity 可见
  • 空进程:已经退出的应用程序

# BroadcastReceiver(广播接收器)

  • BroadcastReceiver 用于接收来自其他应用或系统发出的广播(Broadcast)
  • 广播可以是系统广播(如网络状态变化、电池电量低等)或应用自定义广播
  • BroadcastReceiver 没有用户界面,通常用于在接收到广播时执行一些后台操作,如更新 UI、启动 Service 等

# ContentProvider(内容提供者)

  • ContentProvider 是 Android 系统中不同应用之间共享数据的标准接口。
  • 它允许一个应用访问另一个应用的数据(如联系人、照片等),而无需知道数据的具体存储方式。
  • ContentProvider 通常用于实现跨应用数据共享,如将联系人信息提供给第三方应用。
  • 开发者可以通过实现 ContentProvider 类来定义自己的数据共享接口,并通过 URI 来访问这些数据

使用方法:

  1. 自定义 ContentProvider 类:开发者需要继承 Android 提供的 ContentProvider 基类,并实现其中的方法。这些方法包括 onCreate()、query()、insert()、update()、delete()等,用于处理数据的创建、查询、插入、更新和删除操作。
  2. 定义 URI:每个 ContentProvider 都需要定义一个唯一的 URI,用于指定到它的数据集。URI 的格式通常为 content://<authority>/<path> ,其中 <authority> 是 ContentProvider 的唯一标识符, <path> 是数据集的路径。
  3. 使用 ContentResolver:ContentResolver 是数据调用者,它结合 URI 对 ContentProvider 进行增删改查操作。开发者可以通过调用 ContentResolver 的 query()、insert()、update()、delete()等方法来访问 ContentProvider 中的数据

# 数据传递

# Intent

两种传递数据

  1. 向下传递数据
  2. 向上传递数据

向下传递数据

通过 Intent 提供的 putExtra() 方法,将要传递的数据暂存在 Intent 中

// 放入数据
// 跳转页面
 Intent intent = new Intent(FirstActivity.this, ThirdActivity.class);
// 放入 String 数据
intent.putExtra("name","张三");
// 放入 int 数据
intent.putExtra("age",20);

第二页面获取数据

// 获取实例			
Intent intent = getIntent();
    // 获取字符串 
String name = intent.getStringExtra("name");
    // 获取 int
int age = intent.getIntExtra("age",defalult value);
    // 初始化    
tv_date = findViewById(R.id.tv_date);
    // 输入文字  
tv_date.setText("接收到数据:\n姓名:"+name+"\n年龄:"+age);

向上传递数据

// 父 activity
Intent intent = new Intent(this, secondActivity.class);
startActivityForResult(intent, REQUEST_CODE);// 启动需要监听返回值的 Activity,并设置请求码:requestCode
// 重写响应
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    // 当 otherActivity 中返回数据的时候,会响应此方法
    //requestCode 和 resultCode 必须与请求 startActivityForResult () 和返回 setResult () 的时候传入的值一致。
    if(requestCode==1&&resultCode==2)
    {
        int three=data.getIntExtra("key", 0);
        result.setText(String.valueOf(three));
    }
}
// 在子 Activity 中  
Intent returnIntent = new Intent();
returnIntent.putExtra("key", value);
setResult(RESULT_OK, returnIntent);// 设置结果吗,让上一响应
finish();// 使用完成后结束当前 Activity 的生命周期

# Bundle

Bundle 是一种用于存储和传递数据的数据结构,通常用于在不同组件(如 Activity、Fragment、Service)之间传递数据

// 写数据
Bundle args = new Bundle();
args.putString("MainActivity","Hello");
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
// 把 Bundle 容器中的数据放到 Intent 中
intent.putExtras(bundle);
// 启动该 Intent,实现 Activity 的跳转
startActivity(intent);

Bundle 所保存的数据是以 key-value (键值对) 的形式保存在 ArrayMap 中

// 读数据
Intent intent = getIntent();
// 从 Intent 中取出 Bundle
Bundle bundle = intent.getExtras();
// 获取 FunPerson 里的数据数据
String name = bundle.getString("MainActivity");

# 数据存储

# 安卓文件的操作模式

模式 内容
MODE_PRIVATE 默认操作模式,代表该文件是私有数据,只能够被本应用本身访问,写入的内容会覆盖原文件的内容
MODE_APPEND MODE_APPEND 会检验文件是否存在,存在的话往文件中追加内容,否则建立新文件
MODE_WORLD_READABLE 当前文件可以被其他应用读取
MODE_WORLD_WRITEABLE 当前文件可以被其他应用写入

# SharedPreferences

适用于保存一些简单数据和键值对

# 存数据

使用步骤

public void write()
{
    //1. 通过 getSharedPreferences () 方法获取 SharedPreferences 对象
    SharedPreferences sp = getSharedPreferences("包名",MODE_PRIVATE);
    //2. 通过该对象的 edit () 方法获取一个 Editor 对象,通过这个对象可以保存数据
    SharedPreferences.Editor editor = sp.edit();
    //3. 往 editor 对象中添加数据 (键值对数据)
    editor.putString("name","newland");// 存字符串类型的数据。键是 name, 值是 newland
    editor.putBoolean("rememberpass",true);// 存布尔类型的数据。键是 rememberpass, 值是 true
    //4. 提交数据,完成存储
    editor.commit();
}

关于 getSharedPreferences ("包名",MODE_PRIVATE) 方法解析

getSharedPreferences("1.包名",2. MODE_PRIVATE);方法的两个参数
  1. 文件名,第一次使用时文件名不存在则会创建一个
  2. 操作模式,MODE_PRIVATE 表示只有当前应用程序才可以读写这个文件

# 取数据

SharedPreferences 对象提供了一系列的 get 方法来取数据 (取数据时需要指明取什么类型的数据)

public void read()
{
    //1. 通过 getSharedPreferences () 方法获取 SharedPreferences 对象
    SharedPreferences sp = getSharedPreferences("包名",MODE_PRIVATE);
    //2. 从中取出数据
    String sname = sp.getString("name","")// 取字符串类型的数据。键值是 name, 取不到值,默认返回 ""
    Boolean flag = sp.getBoolean("rememberpass","false")// 取布尔类型的数据。键值是 rememberpass, 取不到值,默认返回 0
        Sysrem.out.println("sname="+sname+",flag="+flag);
}

# I/O 流数据存储

# 存入

FileOutputStream data = openFileOutput("file.txt", Context.MODE_PRIVATE);
//MODE_PRIVATE 表示私有模式,文件只能被应用本身访问;MODE_APPEND 表示追加模式,如果文件已存在,则写入的数据将被追加到文件末尾
String inputFileContext = "hello android";
// 写入数据,字符串转换为字节数组
data.write(inputFileContext.getBytes());
// 释放系统资源
data.close();

# 取出

FileInputStream fis = openFileInput("data.txt");
	byte[] buffer = new byte[1024];  // 定义字节数组存入数据
    int bytesRead;  
    StringBuilder text = new StringBuilder(); //StringBuilder 是一个可变字符序列,用于高效地构建字符串
    while ((bytesRead = fis.read(buffer)) != -1) {  // 从文件输入流(FileInputStream)中读取数据到字节数组(buffer)中
        // 从一个输入流中读取的字节数据转换为字符串,并追加到一个 StringBuilder 对象 text 的末尾
        text.append(new String(buffer, 0, bytesRead));  
    }  
    String readData = text.toString();
方法
getDir(name,mode) 在 app 的 data 目录下获取或创建 name 对应的子目录
getFileDir() 获得 app 的 data 目录的 file 目录的绝对路径
String[] fileList() 返回 app 的 data 目录下的全部文件
deleteFile(filename) 删除 app 的 data 目录下的指定文件

# SQL Lite

  • Step 1:自定义一个类继承 SQLiteOpenHelper 类
  • Step 2:在该类的构造方法的 super 中设置好要创建的数据库名,版本号
  • Step 3:重写 onCreate ( ) 方法创建表结构
  • Step 4:重写 onUpgrade ( ) 方法定义版本号发生改变后执行的操作

示例

public class MySQLopenHelper extends SQLiteOpenHelper {
    public MySQLopenHelper(Context context, String name, CursorFactory factory,int version) {
        super(context, "my.db", null, 1); 
    }
    @Override
    // 数据库第一次创建时被调用
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE Student(_id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20))");
    }
    // 软件版本号发生改变时调用
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("ALTER TABLE person ADD phone VARCHAR(12) NULL");
    }
}

使用 API

MySQLopenHelper mySQLopenHelper = new MyDBOpenHelper(mContext, "my.db", null, 1);// 建表
SQLiteDatabase db = myDBHelper.getWritableDatabase();// 设置数据库以读写方式打开数据库
// 插入
ContentValues values = new ContentValues();
values.put("name","小明");
db.insert("Student",null,values);// 参数分别为表名,列名,数据
// 改
ContentValues values1 = new ContentValues();
values1.put("name","大明");
db.updata("Student",values1,"name = ?",new String[]{""});// 参数分别为表名,修改后的值,where 条件,约束的值 (要修改的 id 名)
// 删
db.delete("Student", "_id = ?", new String[]{""});// 参数依次是表名,以及 where 条件与约束
// 查
StringBuilder sb = new StringBuilder();
// 参数依次是:表名,列名,where 约束条件,where 中占位符提供具体的值,指定 group by 的列,进一步约束
Cursor cursor = db.query("Student", null, null, null, null, null, null);
// 遍历查询结果
if (cursor != null) {
    while (cursor.moveToNext()) {
        String name = cursor.getString(cursor.getColumnIndex("_id"));// 获取列名为_id 的所有数据
    };
    cursor.close();

# StringBuilder 概述和 Cursor 概述

StringBuilder

  1. 查阅 java.lang.StringBuilder 的 API,StringBuilder 又称为可变字符序列,它是一个类似于 String 的字符串缓冲区,通过某些方法调用可以改变该序列的长度和内容。
  2. 它的内部拥有一个数组用来存放字符串内容,进行字符串拼接时,直接在数组中加入新内容。StringBuilder 会自动维护数组的扩容

Cursor

  1. Cursor 是 Android 数据库查询的核心组件,它提供了对查询结果集的访问。你可以把它想象成一个指针,指向查询结果集中的一行数据。通过 Cursor ,你可以遍历查询结果集,获取每一行数据的列值。
  2. 在 Android 中, Cursor 通常用于访问 SQLite 数据库,但也可以用于访问其他类型的数据源,如文件、网络等。使用 Cursor 时,需要注意其生命周期管理,避免内存泄漏。

# 物联网开发

前提步骤

将 libs 中的文件复制到安卓项目中

# 4017&4150 的控制

4017 和 4150 的初始化

// 利用 Tcp 进行控制
MD4017 md4017 = new MD4017(DataBusFactory.newSocketDataBus("串口服务器地址",端口号),监听(可以选择null));
Modbus4150 modbus4150 = new Modbus4150(DataBusFactory.newSocketDataBus("串口服务器地址",端口号),监听(null));
// 用串口直连
Modbus4150 modbus4150 = new Modbus4150(DataBusFactory.newSerialDataBus(串口号,波特率));
MD4017 md4017 = new MD4017(DataBusFactory.newSerialDataBus(串口号,波特率));

4017 获得传感器数据

md4017.getVin(new MD4017ValListener() {
                @Override
                public void onVal(int[] ints) {
                    ed_co2.setText(int["串口号"]+"");
                }
// 转换为实际的传感值:
MD4017ValConvert.getRealValByType(Md4017VIN.TEM, val["串口号"])

4150 获取数据

modbus4150.getVal("DI口",new MdBus4150RelayListener(){
                @Override
                public void onVal(int val) {
                    ed_co2.setText(val+"");
                }
});

4150 控制继电器

modbus4150.ctrlRelay("端子号", true()/false(), new MdBus4150RelayListener() {
                @Override
                public void onCtrl(boolean b) {
                }
                @Override
                public void onFail(Exception e) {
                }
            });

# RFID 的读取

RFID 的初始化

RFID rfid = new RFID(DataBusFactory.newSocketDataBus("串口服务器地址",端口号),监听(可以选择null));
RFID rfid  = new RFID(DataBusFactory.newSerialDataBus(串口号,波特率));

读取单张 RFID 号

读到卡号后执行完操作后自动停止

rfid.readSingleEpc(new SingleEpcListener(){
    @Override
    public void onVal(String val){
        tvSingleEpc.setText(val);
    }
    @Override
    public void onFail(Exception e) {
     	Toast.makeText(getApplicationContext(),e.toString(),Toast.LENGTH_SHORT).show()
    });

读取数据

rfid.readData(new RFIDReadListener(){
    @Override
    public void onResult(String str){
        //str 为读到的数据
        tvSingleEpc.setText(str);
    }
    @Override
    public void onFail(Exception e) {
     	Toast.makeText(getApplicationContext(),e.toString(),Toast.LENGTH_SHORT).show()
    });

# LED 显示屏

初始化

LedScreen ledScreen = new LedScreen(DataBusFactory.newSerialDataBus(串口号,波特率));
LedScreen ledScreen = new LedScreen(DataBusFactory.newSocketDataBus("串口服务器地址",端口号),监听(可选null));

发送文本

PlayType:支持上下左右等,对应枚举为 UP,DOWN,RIGHT;

ShowSpeed: 支持从 1 级到 8 级,对应枚举类为 ShowSpeed1 到 8

stopTime:表示停止 * 秒

validTime:小于 99 表示有效时间,大于 99 表示永久有效。

ledScreen.sendTxt("要发送的内容",PlayType, ShowSpeed, stopTime, validTime, new LedListener(){
    @override
    public void onsuccess(boolean val){
        Toast mokeText(getApplicationContext(),val+"", Toast LENGTH SHORT).show();
    }
    @Overcide
    public void onFail(Exception e){
        Toast.makeText(getApplicationContext(), e.tostring(), Toast.LENGTH_SHORT).show();
    }
});

# SO 文件的导入

到使用新大陆库文件的时候偶尔会出现找不到 so 文件的情况,需要在 build.gradle (app) 中添加

sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }

# 云平台 SDK 的使用

创建 netWorkBusiness 的实例

netWorkBusiness = new NetWorkBusiness(""," http://api.nlecloud.com");

用得到的实例调用 signIn 登录云平台获取 accessToken

netWorkBusiness.signIn(new SignIn("account","password"), new NCallBack<BaseResponseEntity<User>>(this) {
            @Override
            protected void onResponse(BaseResponseEntity<User> response) {
                // 将得到的 token 重新登录云平台
                netWorkBusiness = new NetWorkBusiness(response.getResultObj().getAccessToken()," http://api.nlecloud.com");
                // 放入需要操作的代码
            }
};

获取数据或者操作

netWorkBusiness.getSensor("设备号", "Tag号", new NCallBack<BaseResponseEntity<SensorInfo>>(this) {
        @Override
        protected void onResponse(BaseResponseEntity<SensorInfo> response) {
            if (!(response.getResultObj().getValue()).equals(null)){
                // 把数值转换成 int 型
                double doubleValue = Double.parseDouble(response.getResultObj().getValue());
                int intValue = (int) doubleValue;
                // 进行其它操作
            }
        }
    });

# 其它

# 设置屏幕保持显示不息屏

  • 在 xml 顶层文件中设置 android:keepScreenOn 属性为 "true" 即可

# 隐藏顶部状态栏

  • 活动的主题中添加以下代码: <item name="android:windowFullscreen">true</item>
  • 在代码中使用以下方法: getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);

请注意,隐藏状态栏可能会影响用户体验,因此应该在适当的时候使用。同时,不同版本的 Android 系统可能会有不同的兼容性问题,需要针对具体版本做适配处理


# ListView 与 Adapter 的结合使用

Adapter 关键代码

public class MyAdapter extends BaseAdapter{
    private Context mcontext;
    private String[] mtext;
    private String[] medit;
    private SparseArray<String> etc = new SparseArray<>();
    public MyAdapter(String[] text,String[] edit,Context context){
        this.mtext = text;
        this.medit = edit;
        this.mcontext = context;
    }
    @Override
    public int getCount() {
        return mtext.length;
    }
    @Override
    public Object getItem(int i) {
        return mtext[i];
    }
    @Override
    public long getItemId(int i) {
        return i;
    }
    @SuppressLint("ViewHolder")
    @Override
    public View getView(final int i, View view, ViewGroup viewGroup) {
        ViewHolder viewHolder;
        if (view == null){
            view = LayoutInflater.from(mcontext).inflate(R.layout.setting,viewGroup,false);
            viewHolder = new ViewHolder();
            viewHolder.editText = view.findViewById(R.id.set_edit);
            view.setTag(viewHolder);
        }else {
            viewHolder = (ViewHolder)view.getTag();
        }
        String content = etc.get(i);
        if (content != null){
            viewHolder.editText.setText(content);
        }else {
            viewHolder.editText.setText(medit[i]);
        }
        viewHolder.editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }
            @Override
            public void afterTextChanged(Editable editable) {
                etc.put(i,editable.toString());
            }
        });
        return view;
    }
    public class ViewHolder {
        EditText editText;
    }
    public String getdata(){
        return etc.get(0);
    }
}