跳到主要内容

Android开发学习

学习资源

项目结构

快速上手

安卓应用的生命周期

应用的生命周期

回调描述
onCreate()这是第一个回调,在活动第一次创建时调用
onStart()这个回调在活动为用户可见时被调用
onResume()这个回调在应用程序与用户开始可交互的时候调用
onPause()被暂停的活动无法接受用户输入,不能执行任何代码。当前活动将要被暂停,上一个活动将要被恢复时调用
onStop()当活动不在可见时调用
onDestroy()当活动被系统销毁之前调用
onRestart()当活动被停止以后重新打开时调用

20200502011959

前置知识

assets中的文件无法直接访问,可以使用AssetManager访问。 android.content.Context

Android Studio也有类似javaWeb的那个Maven的,默认使用的是gradle 添加依赖的方法类似Maven

gradle 是目前非常流行的一个项目构建工具。 它并不局限于一种平台。

Gradle和Maven都是项目自动构建工具 虽然两者都是项目工具,但是maven现在已经是行业标准,Gradle是后起之秀,很多人对他的了解都是从android studio中得到的,Gradle抛弃了Maven的基于XML的繁琐配置,众所周知XML的阅读体验比较差,对于机器来说虽然容易识别,但毕竟是由人去维护的。取而代之的是Gradle采用了领域特定语言Groovy的配置,大大简化了构建代码的行数

核心功能构建项目管理jar包

Gradle使用一种基于Groovy的特定领域语言(DSL)来声明项目配置,抛弃了基于XML的各种繁琐配置。面向java应用为主。当前其支持的语言限于java,Groovy和Scala,计划未来将支持更多的语言

安卓开发中,不直接使用 System.out.print 来调试信息,而是使用 Log.d 来显示日志 使用例

Log.d("myMsg","hello");

前面的就是Tag,通过这个tag就能找到出现的Log

gradle-wrapper.properties设置为如下的

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=file:///F:/D:/JavaTools/gradle-6.3-all.zip

build.gradle设置为如下

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {

repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/' }

}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'


// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/' }

}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

Demo

public class LifeCycleActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_life_cycle);
Log.d("LifeCycle","----onCreate----");
}

@Override
protected void onStart() {
super.onStart();
Log.d("LifeCycle","----onStart----");
}

@Override
protected void onResume() {
super.onResume();
Log.d("LifeCycle","----onResume----");
}

@Override
protected void onPause() {
super.onPause();
Log.d("LifeCycle","----onPause----");
}

@Override
protected void onStop() {
super.onStop();
Log.d("LifeCycle","----onStop----");
}

@Override
protected void onRestart() {
super.onRestart();
Log.d("LifeCycle","----onRestart----");
}

@Override
protected void onDestroy() {
super.onDestroy();
Log.d("LifeCycle","----onDestroy----");
}
}

Activity的创建三部曲

  1. 新建一个类去继承Activity或其子类(现在是继承AppCompatActivity
  2. AndroidManifest中声明
  3. 创建一个layout并在Activity的onCreate中设置

AndroidManifest

每个Activity都需要在这里注册(一般会自动生成),如果需要就在AndroidManifest里设置属性就好了

例如

<activity android:name=".ProgressActivity" android:label="这是自定义的标题"/>
  • 设置标题名称
android:label="这是自定义的标题"

  • 去除顶部导航(如果要全局的话在application里加上这个属性就好了)
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"

  • 竖屏显示 把手机横过来也不会转
android:screenOrientation="portrait"

  • 设置默认启动的Activity(在元素里面包着这个就可以了,但是一个项目只能有一个)
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

Groovy编程语言

print("Hello Groovy");
// 可以省略分号,和括号
print "Hello Groovy"

Groovy中定义变量

//def 是弱类型的,groovy会自动根据情况给变量赋予对应的类型
def i = 18
print i


// 定义一个集合类型
def list = ['a','b']
// 往list中添加元素
list << 'c'
// 取出元素
print list.get(2)


//定义一个map
def map = ['key1':'value1','key2':'value2']
//向map添加元素
map.key3='value3'
// 取出元素
print map.get('key3')

Groovy 中的闭包

闭包就是一段代码块。在gradle中,我们主要是把闭包当参数来使用。

def b1 = {
print "hello b1"
}

Groovy带参闭包

def b2 = {
v ->
print "hello ${v}"
}

Groovy定义方法

// 这里导入一个带参闭包
def b2 = {
v ->
print "hello ${v}"
}

// 这个Closure就是闭包的参数类型
def method(int a,Closure closure){
// 然后在调用处传参
closure("xiao ming")
}

method(1,b2)

gradle学习

创建gradle构建 先到项目目录下 gradle init 初始化

项目的目录结构 src/main/java 放置正式代码目录 src/main/resources 放置正式配置文件目录 src/test/java 放置单元测试代码目录 src/test/resources 放置测试配置文件目录 src/main/webapp 放置页面元素 js,css,html等

安卓两个build.gradle

一个Project的,一个Module的

  • Project中的gradle是声明的资源包括依赖项、第三方插件、maven仓库地址的,是用来加载gradle脚本自身需要使用的资源
  • 而Module中的gradle是添加的使应用程序所需要的依赖包,也就是项目运行所需要的东西。

添加坐标

每一个jar包的坐标都有三个基本元素组成 group,name,version testCompile 表示该jar包在测试时起作用(在gradle里面添加坐标的时候都要带上jar包的作用域)

// 标明开发用的语言
plugins {
id 'java'
}

// 本项目的坐标
group 'com.alsritter'
version '1.0-SNAPSHOT'

// 使用的java版本
sourceCompatibility = 1.8

// 指定仓库的路径,mavenCentral()表示使用的是中央仓库,此时项目中所需的jar包都会默认从中央仓库下载到本地指定目录
repositories {
mavenCentral()
}

// gradle工程所有jar包的坐标都在dependencies属性内放置
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}

然后导入外部jar包还是向Maven那样用Maven中央仓库找到坐标,然后在坐标那栏选择gradle

把中央仓库改成本地仓库

在环境变量里添加这个变量(这个不是找启动器的那种,所以变量名是固定的) CRADLE_USER_HOME 然后选择自己本地的Maven路径:D:\JavaTools\mavenRepo

添加本地仓库的路径

// 注意顺序,这里mavenLocal()放前面,表示先从本地找
repositories {
mavenLocal()
mavenCentral()
}

android四大组件

1、activity : 活动 2、service : 服务 3、content provider : 广播接收器 4、broadcast receiver : 内容提供者

Context数量

参考

Context一共有三种类型,分别是ApplicationActivityService

那么一个应用程序中到底有多少个Context呢?其实根据上面的Context类型我们就已经可以得出答案了。Context一共有Application、Activity和Service三种类型,因此一个应用程序中Context数量的计算公式就可以这样写:

Context数量 = Activity数量 + Service数量 + 1

上面的1代表着Application的数量,因为一个应用程序中可以有多个Activity和多个Service,但是只能有一个Application。

View

每个控件都需要配置一个id(一般是自动) android:id="@+id/button" 添加id之后就能从android.R 资源类里引用ID

注意@+id@id的区别 @+id是添加一个id名称,如果存在同id的就会覆盖掉原先的id @id则是直接引用R.java文件存在的id资源,如果不存在会编译报错

ID字符串在同一布局里是唯一的,不能重名,不同布局可以重名

布局

布局的xml语法

控件

细节

绘图兼容性

在项目的build.gradle添加

android.defaultConfig.vectorDrawables.useSupportLibrary = true

px和dp的区别

px代表像素,dp代表像素密度,dpi:每英寸包含的像素数

内边距和外边距

20200425015948

20200425020205

常用布局

LinearLayout 线性布局

TableLayout 表格布局

AbsoluteLayou 绝对布局

RelativeLayout 相对布局

ConstraintLayout 约束布局

ViewModel

官方说明

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.

就是当状态改变(旋转屏幕之类的)时保存view数据(参见application life,就是把临时UI数据保存到内存中)

这样的话最直接的好处就是开发者不再需要关心 Activity / Fragment 的相关状态数据持久化(saveInstanceStatus)的问题了,能够专注于产品业务的开发,避免缓存恢复数据这种重复模板化操作。

实际上就是自己创建一个类,去继承这个ViewModel类,在这个类里封装你需要保存的数据(例如篮球等分)

public class MyViewModel extends ViewModel {
public int number = 0;
}
public class MainActivity extends AppCompatActivity {

private MyViewModel myViewModel;
TextView textView;
Button button1,button2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myViewModel = new ViewModelProvider(this,new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);

textView = findViewById(R.id.textView);
button1 = findViewById(R.id.button2);
button2 = findViewById(R.id.button3);

textView.setText(String.valueOf(myViewModel.number));

button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
myViewModel.number++;
textView.setText(String.valueOf(myViewModel.number));
}
});

button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
myViewModel.number--;
textView.setText(String.valueOf(myViewModel.number));
}
});
}
}

LiveData

LiveData 的使用分三步:

  • 创建一个 LiveData 的实例,让它持有一种特定的数据类型,比如 String 或者 User .通常是将 LiveData 放在ViewModel中使用的(这里我们先单独使用)。

  • 创建一个 Observer 对象,并实现其 onChanged(…) 方法,在这里定义当 LiveData 持有的数据发生改变的时候,应该做何操作。可以在这进行UI的更新,一般 Observer 是在 UI controller 中创建,比如 Activity 或者 Fragment 。

  • 通过创建的 LiveData 实例的 observe(…)方法,将 Observer 对象添加进 LiveData 中。方法的原型为observe( LifecycleOwner owner, Observer observer),第一个参数是 LifecycleOwner对象,这也是 LiveData 能监听生命周期的能力来源。第二个参数就是我们的监听器对象 Observer 。

temp

  • AndroidManifest.xml 文件就是类似于Unity的那种项目打包设置那样的东西,配置一些应用信息 例如图标,请求的权限(网络)等
  • MainActivity是主线程,在这里的所有操作都是同一个线程在执行,所以如果要网络请求时需要开新的线程来执行,不然会造成主线程阻塞

  • 用到的每个activity都需要在AndroidManifest.xml里注册

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.alsritter.learnandroidexploit">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
  • 一般只需要添加一个<activity android:name=".MainActivity"></activity>就可以了,但是这里添加了<intent-filter>表示设置这个activity是默认启动activity
  • 这个MainActivity前面那个.表示默认包名(因为实际上就是把这个activity这个类导进来)
<activity android:name=".TextViewActivity"></activity>
<activity android:name=".MainActivity">

跳转界面

textView_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 跳转到TextView演示界面
Intent intent = new Intent(MainActivity.this,TextViewActivity.class);
startActivity(intent);
}
});

注意:这里的MainActivity.this 在内部类的方法中,要指定某个嵌套层次的外围类的“this”引用时,使用“外围类名.this”语法。

//例如这里的内部类使用外围的类Foo(匿名内部类同理)
class Foo {
class Bar {
Foo getFoo() {
// 如果在这里使用:Bar.this 与直接用this效果一样
return Foo.this;
}
}
}

UI管理器

布局管理器

match_parent: 父控件的大小 wrap_content: 按照内容大小自动分配大小

线性布局(LinearLayout)

设置方向的作用就是默认怎么排列(位置不够了向右还是向下,默认是水平排列)

常用属性
属性作用
android:id设置id
android:layout_width设置宽度
android:layout_height设置高度
android:layout_margin外边距
android:layout_padding设置内边距
android:background设置背景
android:orientation设置方向
android:gravity内部元素排序方式
android:layout_weight设置权重

android:layout_weight:把剩余内容按权重分配 例如:

A的权重是1
B的权重是2
C的权重是1

则A:1/4 B:2/4 C:1/4

当两个元素权重相同时且宽度都是0,则它们会互相平分(剩下的空间平分)

相对布局(RelativeLayout)

常用属性

相对布局特有的属性

属性作用
android:layout_toLeftOf在控件的左边
android:layout_toRightOf在控件的右边
android:layout_below在控件下面
android:layout_above在控件的上面
android:layout_alignBottom底部对齐
android:layout_alignParentBottom与父空间底部对齐

帧布局(FrameLayout)

FrameLayout是最简单的布局了。所有放在布局里的控件,都按照层次堆叠在屏幕的左上角。后加进来的控件覆盖前面的控件。

在FrameLayout布局里,定义任何空间的位置相关的属性都毫无意义。控件自动的堆放在左上角,根本不听你的控制。最先添加的控件放在最底层,后添加的控件在先添加的控件上面。下面通过一个实例看一下这个FrameLayout的基础用法,如下:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/FrameLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#FF6143" />

<TextView
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#7BFE00" />

<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#FFFF00" />

</FrameLayout>

v2-da68a3f1ec42b6cbf4d3eeafffb8becc_720w

基础控件

TextView

  • 文字大小、颜色
  • 显示不下时(让显示不下的文字最后面是....)
  • 文字 + icon
  • 中划线(删除线)、下划线

字体大小单位一般是sp

  • 文字大小、颜色
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这里是一段话,测试文字大小和颜色"
android:textColor="#000000"
android:textSize="24sp"/>
  • 显示不下时(让显示不下的文字最后面是....)
    <TextView
android:id="@+id/text2"
android:layout_width="100dp"
android:maxLines="1"
android:ellipsize="end"
android:layout_height="wrap_content"
android:text="这里是一段话,测试文字太长的情况"
android:textColor="#000000"
android:textSize="24sp"/>
  • 文字 + icon
<TextView
android:id="@+id/text3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="测试图标"
android:textColor="#000000"
android:textSize="24sp"
android:layout_marginTop="15dp"
android:drawableRight="@drawable/ic_keyboard_arrow_down_black_24dp"
android:drawablePadding="5dp"/>
  • 中划线(删除线)、下划线 这个就不能用xml属性来做了,需要通过修改Java代码的方式
public class TextViewActivity extends AppCompatActivity {

TextView textView1;
TextView textView2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_text_view);
// 删除线
textView1 = findViewById(R.id.text4);
textView1.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);

// 下划线
textView2 = findViewById(R.id.text3);
textView2.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG);
}
}
  • TextView也可以添加点击事件

  • 文字居中

<TextView  
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/**yourtextstring**"
/>

Button

  • 设置颜色

    <Button
    android:id="@+id/btn1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="按钮1"
    android:textColor="#000"
    android:background="#3F51B5"/>
  • 圆角按钮 先添加一个drawable

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid
    android:color="#FF9900"/>
    <corners
    android:radius="5dp"/>
    </shape>

    然后把Button里的background修改为上面自定义的drawable

    <Button
    android:id="@+id/btn2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="按钮1"
    android:textColor="#000"
    android:layout_below="@id/btn1"
    android:layout_marginTop="10dp"
    android:background="@drawable/btn_round"/>
  • 描边效果

如果不加solid则里面是中空的

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke
android:width="4dp"
android:color="#F44336"/>

<solid android:color="#52CA57"/>

<corners
android:radius="15dp"/>
</shape>
  • 按下特效drawable改成selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 正常状态 -->
<item android:state_pressed="false">
<shape>
<solid
android:color="#FF9900"/>
<corners
android:radius="15dp"/>
</shape>
</item>
<!-- 按下时 -->
<item android:state_pressed="true">
<shape>
<solid
android:color="#674F2C"/>
<corners
android:radius="15dp"/>
</shape>
</item>
</selector>
  • 点击调用方法

先在按钮属性上加上

android:onClick="showToast"
//注意传入参数View,不然会报错
public void showToast(View view){
// 手机弹出一个提示消息
Toast.makeText(this,"提示一条消息",Toast.LENGTH_LONG).show();
}

EditText

输入框样式edit_contour

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="2dp"
android:color="#AAAA"/>
<corners android:radius="15dp"/>
</shape>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp">

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/edit1"
android:textColor="#3F51B5"
android:textSize="25sp"
android:maxLines="1"
android:maxLength="15"
android:hint="用户名"
android:drawableLeft="@drawable/ic_account_circle_black_24dp"
android:drawablePadding="10dp"
android:paddingLeft="15dp"
android:padding="5dp"
android:background="@drawable/edit_contour"/>

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/edit2"
android:textColor="#3F51B5"
android:textSize="25sp"
android:maxLines="1"
android:maxLength="15"
android:hint="密码"
android:drawableLeft="@drawable/ic_lock_black_24dp"
android:drawablePadding="15dp"
android:inputType="textPassword"
android:layout_marginTop="10dp"
android:paddingLeft="15dp"
android:padding="5dp"
android:layout_below="@id/edit1"
android:background="@drawable/edit_contour"/>

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登陆"
android:id="@+id/btn_login"
android:layout_below="@id/edit2"
android:background="#3F51B5"
android:layout_marginTop="40dp"/>


</RelativeLayout>

然后后台定义登陆方法

editText1 = findViewById(R.id.edit1);
editText2 = findViewById(R.id.edit2);
mloginBtn = findViewById(R.id.btn_login);
mloginBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (editText1.getText().toString().equals("lrhh") && editText2.getText().toString().equals("12345678")){
Toast.makeText(EditTextActivity.this,"登陆成功",Toast.LENGTH_SHORT).show();
}
}
});

如果不想就有一条横线就自定义background,方法同按钮那个一样 20200426223634

然后这个文本编辑窗口也可以添加文本输入监听事件

mloginBtn.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) {
//charSequence 表示当前输入框的内容
}
@Override
public void afterTextChanged(Editable editable) {
}
});

RadioButton

RadioButton 也有方向 checked属性可以设置为默认选中

20200427132824

<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rb1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:checked="true"
android:textSize="16sp"/>
<RadioButton
android:id="@+id/rb2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textSize="16sp"/>
</RadioGroup>

自定义样式:

20200427134440

自定义样式同之前按钮按下特效原理一样,但是这里属性改成state_checked

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<shape>
<solid
android:color="#FF9900"/>
<corners
android:radius="15dp"/>
</shape>
</item>
<item android:state_checked="false">
<shape>
<solid
android:color="#674F2C"/>
<corners
android:radius="15dp"/>
</shape>
</item>
</selector>

然后RedioButton中

让按钮圆框消失属性 android:button="@null" 组里面的按钮都加上,否则不会生效

<RadioGroup
android:id="@+id/rg2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_below="@id/rg1"
android:layout_marginTop="20dp">
<RadioButton
android:id="@+id/rb3"
android:layout_width="60dp"
android:layout_height="30dp"
android:text=""
android:checked="true"
android:gravity="center"
android:background="@drawable/btn_radio"
android:button="@null"
android:textSize="16sp"/>
<RadioButton
android:id="@+id/rb4"
android:layout_width="60dp"
android:layout_height="30dp"
android:text=""
android:gravity="center"
android:background="@drawable/btn_radio"
android:button="@null"
android:textSize="16sp"
android:layout_marginLeft="10dp"/>
</RadioGroup>

通过java获取按钮

private RadioGroup mRg1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_radio_button);
mRg1 = findViewById(R.id.rg1);
mRg1.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
//这个RadioButton有点特殊,需要通过RadioGroup来获取(主要是为了识别是组里的哪个按钮)
RadioButton radioButton = radioGroup.findViewById(i);
Toast.makeText(radioButtonActivity.this,radioButton.getText(),Toast.LENGTH_SHORT).show();
}
});
}

CheckBox

复选框

<CheckBox
android:id="@+id/cb1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="复选框1" />
<CheckBox
android:id="@+id/cb2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/cb1"
android:text="复选框2" />
CheckBox cb1,cb2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_check_box);
cb1 = findViewById(R.id.cb1);
cb2 = findViewById(R.id.cb2);
cb1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (b){
Toast.makeText(checkBoxActivity.this,"选中了第一个",Toast.LENGTH_SHORT).show();
}
}
});
cb2.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (b){
Toast.makeText(checkBoxActivity.this,"选中了第二个",Toast.LENGTH_SHORT).show();
}
}
});
}

ImageView

图片要前面加上bg前缀 例如bg_testimage.jpg 注意文件名不能大写

  • 常用属性
    • src:就像HTML那里的那个src作用一样,都是传入一张图片
    • scaleType
      • fitXY:撑满控件,宽高比可能发生改变
      • fitCenter:保持宽高比缩放,直至能够完全显示
      • centerCrop:保持宽高比缩放,直至完全覆盖控件,裁剪效果
<ImageView
android:layout_width="200dp"
android:layout_height="100dp"
android:id="@+id/im1"
android:background="#ff957a"
android:src="@drawable/bg_tempimage"/>

<ImageView
android:layout_marginTop="15dp"
android:layout_width="200dp"
android:layout_height="100dp"
android:id="@+id/im2"
android:scaleType="fitXY"
android:layout_below="@id/im1"
android:background="#ff957a"
android:src="@drawable/bg_tempimage"/>

<ImageView
android:layout_marginTop="15dp"
android:layout_width="200dp"
android:layout_height="100dp"
android:id="@+id/im3"
android:scaleType="centerCrop"
android:layout_below="@id/im2"
android:background="#ff957a"
android:src="@drawable/bg_tempimage"/>
  • 加载网络图片 加载网络图片需要使用到第三方库glide 也可以直接在中央库里引用

因为使用到了网络,所以需要在AndroidManifest.xml添加网络权限

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

然后在java中使用Glide加载图片

ImageView imageView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_view);
imageView = findViewById(R.id.im4);
Glide.with(this).load("https://image.alsritter.icu/wp-content/uploads/2015/05/activity.jpg").into(imageView);
}

滚动视图

滚动视图 垂直滚:ScrollView 水平滚:HorizontalScrollView

用法,直接包住子元素就行了,但是只能有一个子元素,所以一般都是用布局当子元素

RecyclerView

What is RecyclerView? 官方介绍 首先需要把这个坐标映入Gradle

implementation 'com.android.support:recyclerview-v7:28.0.0'

不过创建的项目好像会自动导入这个

Adapter和ViewHolder

使用这个RecyclerView要先理解AdapterViewHolder

Adapter 的理解:

Adapter :适配器,因为 ListView 是一个 View ,不能添加子项,因此在呈现数据的时候就需要某种工具将数据呈现在 ListView 上,而 Adapter 就能充当此角色。常用的 Adapter:ArrayAdapter、BaseAdapter等。

这个Adapter就是适配器模式的那个适配器,在代码里去实现这个抽象类

ViewHolder 的理解:

要想使用 ListView 就需要编写一个 Adapter 将数据适配到 ListView上,而为了节省资源提高运行效率,一般自定义类 ViewHolder 来减少 findViewById() 的使用以及避免过多地 inflate view,从而实现目标。

public View getView(int posion, View itemView, ViewGroup viewGroup) 
{
return null;
}

aHR0cDovL2ltZy5ibG9nLmNzZG4ubmV0LzIwMTYxMTEwMTMzMjAxNTg1

ListView的复用机制 因为ListView默认缓存一页的View,什么叫一页,也就是你当前listview界面上有几个Item可以显示,listview就缓存几个. 当现实第一页的时候,由于没有一个Item被创建,所以第一页的Item的getView方法中的第二个参数都是为null的 假如listview只能最多显示8条记录,则第一页显示的时候listview内部缓存了这8个itemView.当第九条记录出现在视野中的时候,listview就会在调用getView方法的时候在第二个参数处传入之前用过的itemView。

ListView的复用机制与unity里的对象池有异曲同工之妙,本质都是消耗内存来节省性能,但是这里的ListView还有一个问题就是需要通过findViewById()来找到 litm,而这个ViewHolder就是对这块进行优化(getView方法传入itemView这个参数之前会对item查找),所以可以定义一个ViewHolder类对这个itemView进行绑定,这样就省下每次传入itemView这个参数时都要调用findViewById()。

所以:如果itemView中只有一个控件需要显示,那么ViewHolder就不需要了,可以直接把这个控件和itemView进行关联

使用例:

RecyclerView

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

<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/rv_main"
android:background="#FFC107"/>

</RelativeLayout>

创建一个item样式

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:textColor="#000"
android:background="#C7B9DF"
android:textSize="20sp"
android:layout_marginTop="20dp"/>
</LinearLayout>

创建一个实现一个Adapter类

public class LinearAdapter extends RecyclerView.Adapter<LinearAdapter.LinearViewHolder> {

private Context mContext;

public LinearAdapter(Context context) {
this.mContext = context;
}

@NonNull
@Override
public LinearAdapter.LinearViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//绑定一个item
//这个LayoutInflater方法,它的作用類似於findViewById()。不同點是LayoutInflater是用來找res/layout/下的xml佈局檔案,並且例項化;而findViewById()是找xml佈局檔案下的具體widget控制元件(如Button、TextView等)。
// 具體作用:

//1、對於一個沒有被載入或者想要動態載入的介面,都需要使用LayoutInflater.inflate()來載入;

//2、對於一個已經載入的介面,就可以使用Activiyt.findViewById()方法來獲得其中的介面元素。
return new LinearViewHolder(LayoutInflater.from(mContext).inflate(R.layout.layout_linear_item, parent, false));
}

//向绑定的ViewHolder里面的TextView设置值,以及给绑定的TextView添加点击事件
@Override
public void onBindViewHolder(@NonNull LinearAdapter.LinearViewHolder holder, final int position) {
holder.textView.setText("Hello world" + position);
holder.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext,"选中了" + position,Toast.LENGTH_SHORT).show();
}
});
}

//设置绘制的数量
@Override
public int getItemCount() {
return 30;
}


// 这里自定一个ViewHolder类型(绑定了TextView这个对象)
class LinearViewHolder extends RecyclerView.ViewHolder {
private TextView textView;

public LinearViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tv_title);
}
}
}

然后在RecyclerView上添加上这个适配器

public class LinearRecyclerViewActivity extends AppCompatActivity {

private RecyclerView mRvMain;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear_recycler_view);
mRvMain = findViewById(R.id.rv_main);
//这里给这个RecyclerView添加一个普通的Linear管理器(也可以改成其他的布局)
mRvMain.setLayoutManager(new LinearLayoutManager(LinearRecyclerViewActivity.this));
//如果上面那个改成就能横着排列(水平滚动)
//LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
//linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
//mRvMain.setLayoutManager(linearLayoutManager);
//添加适配器
mRvMain.setAdapter(new LinearAdapter(LinearRecyclerViewActivity.this));
}
}

ItemDecoration

条目装饰,用于在绘制Item前或者绘制后添加样式(有点代理模式内味了) 例如使用 ItemDecoration绘制分割线 使用ItemDecoration实现侧边字母提示 等 使用 参考 参考2

  • 这里展示绘制分割线

先创建一个资源(分割线像素)文件(随便命名,这里叫dimen.xml)

<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="myitemHeight">3dp</dimen>
</resources>

@Override
protected void onCreate(Bundle savedInstanceState) {
...
mRvMain.setLayoutManager(new LinearLayoutManager(LinearRecyclerViewActivity.this));
// 然后去继承这个ItemDecoration重新getItemOffsets方法
mRvMain.addItemDecoration(new RecyclerView.ItemDecoration() {
//这里理由的是item偏移,所以背景(RecyclerView)需要设置成深色才能看的清
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//引用上面创建的资源文件
outRect.set(0,0,0,getResources().getDimensionPixelOffset(R.dimen.myitemHeight));
}
});

//添加适配器
mRvMain.setAdapter(new LinearAdapter(LinearRecyclerViewActivity.this));
}

还有几个常用的绘制方法

// 在item绘制之前进行绘制
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) {
onDraw(c, parent);
}

// 在item绘制之后进行绘制
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) {
onDrawOver(c, parent);
}

//想象成图层,onDraw在图层最底下,item在中间,onDrawOver在最上面

垂直滚动

public class LinearRecyclerViewActivity extends AppCompatActivity {

private RecyclerView mRvMain;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear_recycler_view);
mRvMain = findViewById(R.id.rv_main);
//这里给这个RecyclerView添加一个普通的Linear管理器(也可以改成其他的布局)
mRvMain.setLayoutManager(new LinearLayoutManager(LinearRecyclerViewActivity.this));
mRvMain.setAdapter(new LinearAdapter(LinearRecyclerViewActivity.this));
}
}

水平滚动

public class LinearRecyclerViewActivity extends AppCompatActivity {

private RecyclerView mRvMain;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear_recycler_view);
mRvMain = findViewById(R.id.rv_main);

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
//设置滚动方向就行了
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
mRvMain.setLayoutManager(linearLayoutManager);
//添加适配器
mRvMain.setAdapter(new LinearAdapter(LinearRecyclerViewActivity.this));
}
}

网格视图

public class LinearRecyclerViewActivity extends AppCompatActivity {

private RecyclerView mRvMain;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear_recycler_view);
mRvMain = findViewById(R.id.rv_main);
// 使用这个网格视图时需要指定一行有几个格子,例如这里设置为3
mRvMain.setLayoutManager(new GridLayoutManager(this,3));
//添加适配器
mRvMain.setAdapter(new LinearAdapter(LinearRecyclerViewActivity.this));
}
}

瀑布流

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:orientation="vertical">
<ImageView
android:id="@+id/rv_im_v"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"/>
</LinearLayout>
public class fallsAdapter extends RecyclerView.Adapter<fallsAdapter.fallsViewHolder> {

...前面基本一样操作,别忘了要替换xml场景

@Override
public void onBindViewHolder(@NonNull fallsViewHolder holder, final int position) {
//奇偶来分别两张图
//这里使用三目运算(奇偶)
holder.imageView.setImageResource(((position&1) == 0?R.drawable.bg_tempimage:R.drawable.bg_tempimage2));
holder.imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
itemOnClickCallback.onClick(position);
}
});
}

@Override
public int getItemCount() {
return 30;
}

//这里把需要绑定的控件换成 ImageView
class fallsViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;

public fallsViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.rv_im_v);
}
}

...

}

item多样式

这就需要不同的ViewHolder了

public class KindsLinearAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private Context mContext;
private MyItemOnClickCallback itemOnClickCallback;

public KindsLinearAdapter(Context context , MyItemOnClickCallback itemOnClickCallback) {
this.mContext = context;
this.itemOnClickCallback = itemOnClickCallback;
}

// 用来标识position处应该返回所需的视图类型
@Override
public int getItemViewType(int position) {
return ((position&1) == 0?0:1);
}

@NonNull
@Override // 这里的 viewType 就是下面的 getItemViewType 方法返回的值
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == 0) {
return new LinearViewHolder(LayoutInflater.from(mContext).inflate(R.layout.layout_linear_item, parent, false));
}else {
// 可以在不同的view取得控件,只要那个view包含这个控件就行
return new LinearViewHolder2(LayoutInflater.from(mContext).inflate(R.layout.layout_staggered_grid_item, parent, false));
}

}

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {

if (getItemViewType(position) == 0){
LinearViewHolder linearViewHolder = (LinearViewHolder) holder;
linearViewHolder.textView.setText("Hello world" + position);
linearViewHolder.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
itemOnClickCallback.onClick(position);
}
});
}else {
LinearViewHolder2 linearViewHolder = (LinearViewHolder2) holder;
linearViewHolder.imageView.setImageResource(R.drawable.bg_tempimage);
linearViewHolder.imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
itemOnClickCallback.onClick(position);
}
});
}

}

@Override
public int getItemCount() {
return 30;
}


//第一个item需要绑定的
class LinearViewHolder extends RecyclerView.ViewHolder {
private TextView textView;

public LinearViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.tv_title);
}
}

//第二个item需要绑定的
class LinearViewHolder2 extends RecyclerView.ViewHolder {
private ImageView imageView;

public LinearViewHolder2(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.rv_im_v);
}
}


// 创建一个回调函数
public interface MyItemOnClickCallback {
void onClick(int pos);
}

}

上拉刷新,下拉刷新

XRecyclerView

使用参考

addheadView addFootView

就是使用这个库里的XRecyclerView代替原本的RecyclerView就可以了,如果要加载则加上setLoadingListener添加加载监听

别忘了加上refreshComplete()loadMoreComplete()来结束刷新特效

DEMO

public class Main2Activity extends AppCompatActivity {
XRecyclerView recyclerView;
List<Integer> data=new ArrayList<Integer>();
MyAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
recyclerView=(XRecyclerView)findViewById(R.id.xrecy);
LinearLayoutManager xLinearLayoutManager = new LinearLayoutManager(this);
//xLinearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(xLinearLayoutManager);
View header= LayoutInflater.from(this).inflate(R.layout.header,null);
recyclerView.addHeaderView(header); //添加头部
recyclerView.setRefreshProgressStyle(ProgressStyle.BallZigZag); //设定下拉刷新样式
recyclerView.setLoadingMoreProgressStyle(ProgressStyle.BallZigZag);//设定上拉加载样式
recyclerView.setArrowImageView(R.drawable.qwe); //设定下拉刷新显示图片(不必须)
initData(); //初始化数据
adapter=new MyAdapter(data);
recyclerView.setAdapter(adapter);
/**
*设定下拉刷新和上拉加载监听
*/
recyclerView.setLoadingListener(new XRecyclerView.LoadingListener() {
//上拉加载监听
@Override
public void onLoadMore() {
addData(); //上拉加载添加数据
recyclerView.loadMoreComplete(); //加载数据完成(取消加载动画)
};
//下拉刷新监听
@Override
public void onRefresh() {
initData(); //初始化数据
adapter.notifyDataSetChanged();
recyclerView.refreshComplete(); //刷新数据完成(取消刷新动画)
}
});
}

/**
*上拉加载添加数据
*/
private void addData() {
for (int i=0;i<20;i++){
Integer r= Integer.valueOf((int) (Math.random()*100));
data.add(r);
}
adapter.notifyDataSetChanged();
}

/**
*初始化数据
*/
private void initData() {
data.clear();
for (int i=0;i<40;i++){
Integer r= Integer.valueOf(i);
data.add(r);
}
}
}

WebView

  • 加载网络URL
webview.loadUrl("网址")

  • 加载assets下的html文件
webview.loadUrl("file:///android_asset/...")
//例如
webView.loadUrl("file:///android_asset/learnCSS/index.html");

  • 加载html代码
webview.loadData();

注意webView默认是不支持js的,所以需要添加js的支持

//启用js
webView.getSettings().setJavaScriptEnabled(true);

如果不想跳转到别的浏览器则加上这个

//设置默认不跳转到浏览器打开
webView.setWebViewClient(new WebViewClient());
  • 网页返回
webview.canGoBack() //判断能否返回
webview.goBack() //网页返回

webview.canGoForward() //判断能否前进
webview.goForward() //网页前进

webview.canGoBackOrForward(int steps) //判断能否向前或向后steps步(正数表向前,负数表向后)

重写一个自己的浏览器客户端

demo

public class WebViewActivity extends AppCompatActivity {

private WebView webView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view);
webView = findViewById(R.id.webview1);

//启用js
webView.getSettings().setJavaScriptEnabled(true);
//设置默认不跳转到浏览器打开
webView.setWebViewClient(new MywebViewClient());
webView.loadUrl("https://alsritter.github.io/");
}

//继承自这个WebViewClient类,去复写它的方法
class MywebViewClient extends WebViewClient{

//监听到页面要跳转时的情况,默认是要跳转到浏览器的,这里可以拦截到url实现在webview里打开
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
view.loadUrl(request.getUrl().toString());
return true;
}

//加载前执行
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
//执行一些加载前的操作
}

//加载后执行
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//执行一些加载之后的操作
}
}

// 重写返回键方法,使之返回网页页面上一级而不是Activity页面
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 判断是否按下了返回键以及有返回的页面
if(keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()){
webView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}

Toast

UI组件之弹出组件

屏幕下方弹出一个提示信息 example

Toast.makeText(this,"提示一条消息",Toast.LENGTH_LONG).show();

常用的两种如下

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_toast_1:
//这个getApplicationContext可以获取当前的context
Toast.makeText(getApplicationContext(),"普通toast",Toast.LENGTH_SHORT).show();
break;
case R.id.btn_toast_2:
//Toast.makeText的返回值就是Toast类型的
Toast toastCenter = Toast.makeText(getApplicationContext(),"居中toast",Toast.LENGTH_SHORT);
//后面那两个参数是偏移量
toastCenter.setGravity(Gravity.CENTER,0,0);
toastCenter.show();
break;
}
}
  • Toast有个缺点就是如果一直按那个按钮就会排队等待弹出(每次弹出都有一定时间的) 为什么会发生这种问题呢? 因为下面这种形式实际上是每次都实例化了一个新的Toast,只需把这个Toast包装成全局的就可以了
Toast.makeText(getApplicationContext(),"普通toast",Toast.LENGTH_SHORT).show();

所以封装一个自己的消息Util

public class ToastUtil {
private static Toast toast;

//注意使用getApplicationContext()得到的context整个应用只有一个(Application)
public static void showMsg(Context context,String msg){
if (toast == null) {
toast = Toast.makeText(context,msg,Toast.LENGTH_SHORT);
}else{
toast.setText(msg);
}
toast.show();
}
}

使用=>ToastUtil.showMsg(getApplicationContext(),"封装的toast");

Dialog

AlertDialog

警式对话框

DEMO

public class DialogActivity extends AppCompatActivity {

private Button btn1, btn2, btn3, btn4,btn5;

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

btn1 = findViewById(R.id.dialog_btn1);
btn2 = findViewById(R.id.dialog_btn2);
btn3 = findViewById(R.id.dialog_btn3);
btn4 = findViewById(R.id.dialog_btn4);
btn5 = findViewById(R.id.dialog_btn5);

OnClick onClick = new OnClick();
btn1.setOnClickListener(onClick);
btn2.setOnClickListener(onClick);
btn3.setOnClickListener(onClick);
btn4.setOnClickListener(onClick);
btn5.setOnClickListener(onClick);

}

class OnClick implements View.OnClickListener {

//两个全局变量
int temp = 0;
boolean[] isSelected = {false,false,true};
//注意下面的监听事件全部用的是匿名类,所以每点一次按钮就实例化一次,如果需要更高的效率

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.dialog_btn1:
AlertDialog.Builder builder = new AlertDialog.Builder(DialogActivity.this);
//setPositiveButton是肯定按钮 setNeutralButton是一般按钮 setNegativeButton是否定按钮
builder.setTitle("请回答")
.setIcon(R.drawable.bg_tempimage)
.setMessage("骑马与砍杀好玩?")
.setPositiveButton("好玩", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ToastUtil.showMsg(getApplicationContext(), "点击了好玩");
}
}).setNegativeButton("不好玩", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ToastUtil.showMsg(getApplicationContext(), "点击了不好玩");
}
}).setNeutralButton("还行", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ToastUtil.showMsg(getApplicationContext(), "点击了一般");
}
}).show();

break;
case R.id.dialog_btn2:
final String[] array = {"男", "女"};
AlertDialog.Builder builder2 = new AlertDialog.Builder(DialogActivity.this);
builder2.setTitle("选择性别")
.setItems(array, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ToastUtil.showMsg(getApplicationContext(), array[which]);
}
}).show();
break;
//RadioButton形式
case R.id.dialog_btn3:
final String[] array2 = {"男", "女"};
AlertDialog.Builder builder3 = new AlertDialog.Builder(DialogActivity.this);
builder3.setTitle("选择性别")
// 第二个参数是默认选中的项(注意!!!这个变量一定要是全局变量,不然无法修改)
.setSingleChoiceItems(array2, temp, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
temp = which;
ToastUtil.showMsg(getApplicationContext(), array2[which]);
//加上这个关闭对话框,否则会无法关闭对话框
dialog.dismiss();
}
})//设置对话框是否可以取消
.setCancelable(false).show();

break;
//CheckBox形式
case R.id.dialog_btn4:
final String[] array3 = {"为了荣誉", "为了金钱","为了权力"};
AlertDialog.Builder builder4 = new AlertDialog.Builder(DialogActivity.this);
builder4.setTitle("选择游戏的目的")
// 第二个参数是默认选中的项
.setMultiChoiceItems(array3, isSelected, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
ToastUtil.showMsg(getApplicationContext(), array3[which]+":"+isChecked);
}
}).show();
break;
//自定义布局
case R.id.dialog_btn5:
AlertDialog.Builder builder5 = new AlertDialog.Builder(DialogActivity.this);
//LayoutInflater 一个用于加载布局的系统服务(一般用于动态加载布局或者添加控件)
//LayoutInflater 不能直接使用,需要通过 getLayoutInflater() 方法或 getSystemService() 方法获得与当前 Context 绑定的 LayoutInflater 实例
View view = LayoutInflater.from(DialogActivity.this).inflate(R.layout.layout_dialog,null);
EditText editText1 = view.findViewById(R.id.et_username);
EditText editText2 = view.findViewById(R.id.et_password);
Button button = view.findViewById(R.id.btn_login);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

}
});
builder5.setTitle("登陆")
.setView(view).show();

break;
}
}
}
}

自定义对话框 动态加载页面

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

<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:hint="username"
android:maxLines="1" />

<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:hint="password"
android:inputType="textPassword"
android:maxLines="1" />

<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:layout_marginRight="20dp"
android:text="Login"
android:textAllCaps="false" />

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

<ProgressBar
android:id="@+id/pd1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<ProgressBar
android:id="@+id/pd2"
style="@android:style/Widget.ProgressBar.Inverse"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp" />

<!-- 注意,Widget.Material 是新样式,Widget是旧的样式-->

<ProgressBar
android:id="@+id/pd3"
style="@android:style/Widget.Material.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:max="100"
android:progress="10"
android:secondaryProgress="30" />

<ProgressBar
android:id="@+id/pd4"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:max="100"
android:progress="10"
android:secondaryProgress="30" />

<Button
android:id="@+id/btn_strat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="模拟进度" />

</LinearLayout>

ProgressBar

进度条

private ProgressBar pg3;
private Button startBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_progress);
pg3 = findViewById(R.id.pd3 );
startBtn = findViewById(R.id.btn_strat);
startBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 用来模拟进度条
handler.sendEmptyMessage(0);
}
});
}

// 用来模拟进度条
//handler是容器
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (pg3.getProgress() < 100) {
handler.postDelayed(runnable,500);
}else {
ToastUtil.showMsg(getApplicationContext(),"加载完成");
}
}
};
//Runnable是线程
Runnable runnable = new Runnable() {
@Override
public void run() {
pg3.setProgress(pg3.getProgress()+5);
handler.sendEmptyMessage(0);
}
};

自定义Dialog

20200501162131

圆角的样式

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="15dp"/>
<solid android:color="#FFFFFF"/>
</shape>

对话框

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

<TextView
android:id="@+id/custom_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="提示"
android:textColor="#AA000000"
android:textSize="20sp"
android:textStyle="bold" />

<TextView
android:id="@+id/custom_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:gravity="center"
android:text="这里是消息内容!"
android:textSize="20sp" />

<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@android:color/black" />

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

<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="取消"
android:textColor="#03A9F4"
android:textSize="20sp" />

<View
android:layout_width="0.5dp"
android:layout_height="match_parent"
android:background="@android:color/black"/>

<TextView
android:id="@+id/tv_confirm"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="确定"
android:textColor="#03A9F4"
android:textSize="20sp" />

</LinearLayout>

</LinearLayout>

实现这个Dialog

public class CustomDialog extends Dialog implements View.OnClickListener {

private TextView mTvTitle, mTvMessage, mTvCancel, mTvConfirm;
private String title, message, cancel, confirm;
private IOnCancelListener iOnCancelListener;
private IOnConfirmListener iOnConfirmListener;

public CustomDialog(@NonNull Context context) {
super(context);
}

public CustomDialog(@NonNull Context context, int themeId) {
super(context);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//注意!!一定要加上这句,这句的意思是指定后面 findViewById 在哪里找控件的
setContentView(R.layout.layout_custom_dialog);
mTvTitle = findViewById(R.id.custom_title);
mTvMessage = findViewById(R.id.custom_message);
mTvCancel = findViewById(R.id.tv_cancel);
mTvConfirm = findViewById(R.id.tv_confirm);

if (!TextUtils.isEmpty(title)) {
Log.d("title",mTvTitle.toString());
mTvTitle.setText(title);
}
if (!TextUtils.isEmpty(message)) {
mTvMessage.setText(message);
}
if (!TextUtils.isEmpty(cancel)) {
mTvCancel.setText(cancel);
}
if (!TextUtils.isEmpty(confirm)) {
mTvConfirm.setText(confirm);
}

//这里直接实现了这个 View.OnClickListener 接口,所以可以直接把自己传进去
mTvCancel.setOnClickListener(this);
mTvConfirm.setOnClickListener(this);

}

//通过自己实现这个接口来避免每次都重新实例化一个匿名类
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_cancel:
if (iOnCancelListener != null) {
iOnCancelListener.onCancel(this);
}
break;
case R.id.tv_confirm:
if (iOnConfirmListener != null) {
iOnConfirmListener.onConfirm(this);
}
break;
}
}

//给取消按钮添加一个回调
public interface IOnCancelListener {
void onCancel(CustomDialog customDialog);
}

//给确定按钮添加一个回调
public interface IOnConfirmListener {
void onConfirm(CustomDialog customDialog);
}


public String getTitle() {
return title;
}

// 这里使用了链式调用类
public CustomDialog setTitle(String title) {
this.title = title;
return this;
}

public String getMessage() {
return message;
}

public CustomDialog setMessage(String message) {
this.message = message;
return this;
}

public String getCancel() {
return cancel;
}

public CustomDialog setCancel(String cancel, IOnCancelListener cancelListenerImp) {
this.iOnCancelListener = cancelListenerImp;
this.cancel = cancel;
return this;
}

public String getConfirm() {
return confirm;
}

public CustomDialog setConfirm(String confirm, IOnConfirmListener confirmListenerImp) {
this.iOnConfirmListener = confirmListenerImp;
this.confirm = confirm;
return this;
}
}

弹出式窗口 20200501215302

同样先制作一个弹出样式layout_pop.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">

<TextView
android:id="@+id/tv_good"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text=""
android:textColor="@color/cardview_dark_background"
android:textSize="20sp" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text="还行"
android:textColor="@color/cardview_dark_background"
android:textSize="20sp" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text=""
android:textColor="@color/cardview_dark_background"
android:textSize="20sp" />

</LinearLayout>

java中生成这个弹出框(下面依然用了getLayoutInflater

public class PopupWindowActivity extends AppCompatActivity {

private Button btnPop;
private PopupWindow pop;

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

btnPop = findViewById(R.id.btn_popup_a);
btnPop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//动态加载样式页面
View view = getLayoutInflater().inflate(R.layout.layout_pop,null);
// 给弹出栏的事件添加一个点击事件(这里就只做第一个,其他的按钮原理相同)
TextView textView = view.findViewById(R.id.tv_good);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//先让弹出栏关闭再做一些操作
pop.dismiss();
//下面开始操作
ToastUtil.showMsg(getApplicationContext(),"点击了弹出栏item");
}
});
pop = new PopupWindow(view,btnPop.getWidth(), ViewGroup.LayoutParams.WRAP_CONTENT);
// 点击外部区域可以使之消失
pop.setOutsideTouchable(true);
// 弹出状态下点击弹出按钮也使之消失
pop.setFocusable(true);
// 标明在哪个控件下落下(弹出)
pop.showAsDropDown(btnPop);
}
});
}
}

Activity的跳转和数据传递

显示跳转和隐式跳转

  • 显示跳转
public class AActivity extends AppCompatActivity {

private Button btnJump;

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

btnJump = findViewById(R.id.btn_jump);
btnJump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//显示1
Intent intent = new Intent(AActivity.this , BActivity.class);
startActivity(intent);

//显示2
Intent intent2 = new Intent();
intent2.setClass(AActivity.this , BActivity.class);
startActivity(intent2);

//显示3
Intent intent3 = new Intent();
intent3.setClassName(AActivity.this , "com.alsritter.learnandroidexploit.jump.BActivity");
startActivity(intent2);

}
});
}
}

  • 隐式跳转

先在AndroidManifest给需要跳转的目标添加一个intent-filter

<activity android:name=".ProgressActivity" android:label="这是跳转的目标">
<action android:name="自定义的标识名称"/>
<!-- 注意下面的category设置为DEFAULT -->
<category android:name="android.intent.category.DEFAULT">
</activity>
public class AActivity extends AppCompatActivity {

private Button btnJump;

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

btnJump = findViewById(R.id.btn_jump);
btnJump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//隐示1
Intent intent = new Intent();
intent.setAction("前面AndroidManifest自定义的标识名称")
startActivity(intent);

}
});
}
}

Activity之间的数据传递

Intent intent = new Intent(AActivity.this,BActivity.class);
Bundle bundle = new Bundle();
bundle.putString("key","value");
intent.putExtras(bundle);
startActivity(intent);

然后在另一个页面取得这个bundle

public class BActivity extends AppCompatActivity {
private TextView title;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a);
title = findViewById(R.id.tv_title);
//因为这个页面是上个页面通过Intent跳转过来的,所以可以之间get到这个Intent
Bundle bundle = getIntent().getExtras();
String str = bundle.getString("key");
title.setText(str);
}
}
  • startActivityForResult :启动Activity,结束后返回结果

    public class AActivity extends AppCompatActivity {

    private Button btn;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_a);
    btn = findViewById(R.id.jump_to_b);
    textView = findViewById(R.id.tv_A);

    btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    Intent intent = new Intent(AActivity.this, BActivity.class);
    Bundle bundle = new Bundle();
    bundle.putString("key1", "这是传给B的值");
    intent.putExtras(bundle);
    //如果要接收下个页面的返回值就需要使用这个 startActivityForResult
    //第二个参数是用来标识的,B界面返回value,而A接收就是通过这个0来标识返回的是哪个页面
    startActivityForResult(intent, 0);
    }
    });
    }
public class BActivity extends AppCompatActivity {

private Button btn,btn2;
private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
btn =findViewById(R.id.jump_to_a);
btn2 =findViewById(R.id.jump_to_a_2);

textView = findViewById(R.id.tv_B);

String str = getIntent().getExtras().getString("key1");
textView.setText(str);

btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("key2","这里是B返回给A的值");
intent.putExtras(bundle);
//这个Activity.RESULT_OK是来标识的状态码,在A接收时可以通过这个状态码来进行不同的操作
setResult(Activity.RESULT_OK,intent);
//因为是通过上个界面跳转过来的,所以这个页面结束之后就回到上个页面去了
finish();
}
});

btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("key3","这里是B返回给A其他状态码的值");
intent.putExtras(bundle);

setResult(Activity.RESULT_CANCELED,intent);
finish();
}
});
}
}

在A界面接收B界面返回来的结果(在A中重写这个onActivityResult方法)

//requestCode是前面通过startActivityForResult(intent,0);设置的那个码(比如这里就是0)
//resultCode是Activity.RESULT_OK之类的状态码(例如这个就是-1)
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (resultCode == Activity.RESULT_OK) {
textView.setText(data.getExtras().getString("key2"));
}

if (resultCode == Activity.RESULT_CANCELED) {
textView.setText(data.getExtras().getString("key3"));
}
}

Activity的4种启动模式

Activity的android:launchMode属性

  • standard:标准模式,默认(每次都新建一个Activity)
  • singleTop:Task栈顶复用模式(在栈顶找到则不新建一个Activity,否则新建)
  • singleTask:Task栈内复用模式(在栈内找到则不新建一个Activity,否则新建)
  • singleInstance:全局单例模式(查询所有栈,如果都没有才创建一个)
standard
  • Activity是由任务栈管理的,每启动一个Activity,就会被放入栈中,按返回键,就会从栈顶移除一个Activity。

  • standard是默认的启动模式,即标准模式。每启动一个Activity,都会创建一个新的实例。

singleTop
  • 当要启动的目标Activity已经位于栈顶时,不会创建新的实例,会复用栈顶的Activity,并且其onNewIntent()方法会被调用; 如果目标Activity不在栈顶,则跟standard一样创建新的实例。
singleTask

在同一个任务栈中,如果要启动的目标Activity已经在栈中,则会复用该Activity,并调用其onNewIntent()方法,且该Activity上面的Activity会被清除; 如果栈中没有,则创建新的实例。

可以在AndroidManifest里设置自定义的任务栈(如下的taskAffinity,里面的名字可以自定义)

<activity android:name=".jump.AActivity" android:launchMode="standard" android:taskAffinity="testTask"/>
singleInstance

全局复用,不管哪个Task栈,只要存在目标Activity,就复用。每个Activity占有一个新的Task栈。

Fragment

参考 参考

Fragment就是碎片化的Activity(碎片化加载页面)

  • Fragment有自己的生命周期
  • Fragment依赖于Activity,不能独立存在的。
  • Fragment通过getActivity()可以获取所在的Activity; Activity通过FragmentManager的findFragmentById()或findFragmentByTag()获取Fragment
  • Fragment和Activity是多对多的关系

fragment-02

  • onAttach():Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。
  • onCreate():Fragment被创建时调用。
  • onCreateView():创建Fragment的布局。
  • onActivityCreated():当Activity完成onCreate()时调用。
  • onStart():当Fragment可见时调用。
  • onResume():当Fragment可见且可交互时调用。
  • onPause():当Fragment不可交互但可见时调用。
  • onStop():当Fragment不可见时调用。
  • onDestroyView():当Fragment的UI从视图结构中移除时调用。
  • onDestroy():销毁Fragment时调用。
  • onDetach():当Fragment和Activity解除关联时调用。

上面的方法中,只有onCreateView()在重写时不用写super方法,其他都需要。

与Activity的关系

fRxIQ

使用例如下

Fragment写法例:(BFragment)

public class AFragment extends Fragment {

private TextView title;

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

//Created是已建立的意思,所以这个方法是在该Fragment创建之后调用的
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
title = view.findViewById(R.id.tv_title);
}
}

在Activity中调用该Fragment

public class ContainerActivity extends AppCompatActivity {

private AFragment aFragment;
private BFragment bFragment;
private Button btn;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_container);
btn = findViewById(R.id.change_btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bFragment == null) {
bFragment = new BFragment();
}
getSupportFragmentManager().beginTransaction().add(R.id.fl_container,bFragment).commitAllowingStateLoss();
}
});

//实例化Fragment
aFragment = new AFragment();
//把AFragment添加到Activity中,最后加上commitAllowingStateLoss(),表示允许犯错
getSupportFragmentManager().beginTransaction().add(R.id.fl_container,aFragment).commitAllowingStateLoss();
}
}

Activity向Fragment传递数据

Activity向Fragment传递数据比较简单,获取Fragment对象,并调用Fragment的方法即可,比如要将一个字符串传递给Fragment,则在Fragment中定义方法:

public void setString(String str) {
this.str = str;
}

并在Activity中调用fragment.setString("hello")即可。

这里以后补吧...

Android事件处理

基于监听的事件处理机制 监听三要素:

  • Event Source(事件源)
  • Event(事件)
  • Event Listener(事件监听器)

这里以后补吧...

Handler消息处理

作用:

  • 未来某时做某事(就是延时执行某些操作)
  • 线程间通信

延时执行操作

public class HandlerActivity extends AppCompatActivity {

private Handler handler;

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

//演示延时执行线程的操作
handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
ToastUtil.showMsg(HandlerActivity.this,"这里是延时的消息");
}
}, 3 * 1000);
}
}

线程之间通信

public class HandlerActivity extends AppCompatActivity {

private Handler handler;

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

//线程通信,这里演示子线程与主线程直接的通信,注意这个Handler不是开辟线程,而是执行操作
handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
ToastUtil.showMsg(HandlerActivity.this,"线程通信成功!");
break;
}
}
};

//开辟一个线程
new Thread(){
@Override
public void run() {
super.run();
Message message = new Message();
message.what = 1;
try {
Thread.sleep(3*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//注意这个handler是一个全局变量,所以只要里面添加了消息,主线程就能捕捉到,因此放在sleep下面
handler.sendMessage(message);
}
}.start();

}
}

数据存储

SharedPreferences 轻量数据存储

xml文件,以key-value的形式储存

Demo

public class InputActivity extends AppCompatActivity {

private Button saveBtn1,showBtn2;
private TextView textView;
private EditText editText;
//SharedPreferences用于存储数据
private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mEditor;

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

saveBtn1 = findViewById(R.id.btn_save);
showBtn2 = findViewById(R.id.btn_show);
editText = findViewById(R.id.et_data);
textView = findViewById(R.id.tv_show);

//MODE_PRIVATE表示默认只有本应用可读可写
mSharedPreferences = getSharedPreferences("data",MODE_PRIVATE);
mEditor = mSharedPreferences.edit();
saveBtn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mEditor.putString("name",editText.getText().toString());
//commit()方法是同步存储的过程(就是后台等待执行完才进行下一步),apply()是异步存储的过程
//一般用apply()效率比较高
mEditor.apply();
}
});

showBtn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//第二个参数是默认的key名,不写就行了(作用就是在第一个key没有找到时在默认key里找)
textView.setText(mSharedPreferences.getString("name",""));
}
});
}
}

20200504012414

在这个目录下可以看到安卓本地目录 20200504012828

文件存储

file 利用java的IO流 FileOutputStream & FileInputStream

补充: Android存储的概念 20200504013327

内部存储:随着应用卸载而被删除