Android开发学习
学习资源
安卓应用的生命周期
回调 | 描述 |
---|---|
onCreate() | 这是第一个回调,在活动第一次创建时调用 |
onStart() | 这个回调在活动为用户可见时被调用 |
onResume() | 这个回调在应用程序与用户开始可交互的时候调用 |
onPause() | 被暂停的活动无法接受用户输入,不能执行任何代码。当前活动将要被暂停,上一个活动将要被恢复时调用 |
onStop() | 当活动不在可见时调用 |
onDestroy() | 当活动被系统销毁之前调用 |
onRestart() | 当活动被停止以后重新打开时调用 |
前置知识
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的创建三部曲
- 新建一个类去继承
Activity
或其子类(现在是继承AppCompatActivity
) - 在
AndroidManifest
中声明 - 创建一个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一共有三种类型,分别是Application
、Activity
和Service
。
那么一个应用程序中到底有多少个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字符串在同一布局里是唯一的,不能重名,不同布局可以重名
布局
控件
细节
绘图兼容性
在项目的build.gradle添加
android.defaultConfig.vectorDrawables.useSupportLibrary = true
px和dp的区别
px代表像素,dp代表像素密度,dpi:每英寸包含的像素数
内边距和外边距
常用布局
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>
基础控件
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
,方法同按钮那个一样
然后这个文本编辑窗口也可以添加文本输入监听事件
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属性可以设置为默认选中
<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>
自定义样式:
自定义样式同之前按钮按下特效原理一样,但是这里属性改成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"/>
因为使用到了网络,所以需要在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
要先理解Adapter
和 ViewHolder
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;
}
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);
}
}
上拉刷新,下拉刷新
使用参考
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
圆角的样式
<?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;
}
}
PopupWindow
弹出式窗口
同样先制作一个弹出样式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是多对多的关系
- 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的关系
使用例如下
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",""));
}
});
}
}
在这个目录下可以看到安卓本地目录
文件存储
file 利用java的IO流 FileOutputStream & FileInputStream
补充: Android存储的概念
内部存储:随着应用卸载而被删除