安卓(Android)是一种基于Linux内核(不包含GNU组件)的自由及开放源代码的移动操作系统。主要应用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。Android操作系统最初由安迪·鲁宾开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。随后Google以Apache开源许可证的授权方式,发布了Android的源代码。
1.下载Android Studio和SDK
Install Android Studio | Android Developers (google.cn)
2.观察APP运行日志
Log.e表示错误信息
Log.w表示警告信息
Log.i表示一般消息
Log.d表示调试信息
Log.v表示冗余信息
3.工程目录结构
3.1.gradle
.gradle和.idea Android Studio自动生成的文件,不用了解,一般不要改动。
app 代码、资源等内容(常用)
build 编译文件夹,一般不用
gradle gradle wrapper的配置文件,Android Studio默认没有启动gradle wrapper的方式,如果需要打开,可以点击Android Studio导航栏 –> File –> Settings –> Build,Execution,Deployment –> Gradle,进行配置更改。
.gitignore 将指定的目录或文件排除在版本控制之外
build.gradle 项目全局的gradle构建脚本,通常这个文件中的内容是不需要修改的
gradle.properties 全局的gradle配置文件,在这里配置的属性将会影响到项目中所有的gradle编译脚本
gradlew和gradlew.bat 在命令行界面中执行gradle命令,其中gradlew是在Linux或Mac系统中使用的,gradlew.bat是在Windows系统中使用的。
HelloWorld.iml iml文件是所有IntelliJ IDEA项目都会自动生成的一个文件(Android Studio是基于IntelliJ IDEA开发的),用于标识这是一个IntelliJ IDEA项目,我们不需要修改这个文件中的任何内容
local.properties 指定本机中的Android SDK路径,通常内容都是自动生成的,我们并不需要修改。除非你本机中的Android SDK位置发生了变化,那么就将这个文件中的路径改成新的位置即可。
settings.gradle 用于指定项目中所有引入的模块。需要我们手动去修改这个文件的场景可能比较少
3.2.app
build 编译时自动生成的文件,一般不用
libs 第三方jar包,jar包都放在libs目录下
AndroidTest 用来编写Android Test测试用例的,可以对项目进行一些自动化测试
java java目录是放置我们所有java代码的地方,展开该目录,你将看到我们刚才创建的HelloWorldActivity文件就在里面
res 所有资源文件都在这里 图片放在drawable目录下 布局放在layout目录下,(相当于html中的body) 应用图标在mipmap目录下
字符串放在values目录下
AndroidManifest.xml 整个Android项目的配置文件,你在程序中定义的所以四大组件都需要在这个文件里注册,另外还可以在这个文件中给应用程序添加权限声明。
test 此处是用来编写Unit Test测试用例的,是对项目进行自动化测试的另一种方式。
.gitignore 这个文件用于将app模块内的指定的目录或文件排除在版本控制之外,作用和外层的.gitignore文件类似。
app.iml IntelliJ IDEA项目自动生成的文件,我们不需要关心或修改这个文件中的内容。
build.gradle 这是app模块的gradle构建脚本,这个文件中会指定很多项目构建相关的配置。
proguard-rules.pro 这个文件用于指定项目代码的混淆规则,当代码开发完成后打成安装包文件,如果不希望代码被别人破解
4.创建新的App页面
1.创建一个layout布局文件
2.创建一个Activity Java类
使用startActivity切换页面
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.tv1);
tv.setText("你好,RICHU");
Log.d("ning","create");
Button btn = findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, MainActivity_2.class);
startActivity(intent);
}
});
}
}
5.Andriod控件
5.1.设置文本的内容
1.在XML文件中通过属性andriod:text
<!--res/values-->
<resources>
<string name="mytext" translatable="false">HAHAHA</string>
</resources>
<!--res/layoutres-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="@+id/mytest"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/mytext"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
2.在Java代码中调用文本视图对象的setText方法设置文本
<!--res/values-->
<resources>
<string name="mytext" translatable="false">HAHAHA</string>
</resources>
//java/com/example/application1/MainActivity_2.java
public class MainActivity_2 extends AppCompatActivity {
protected void onCreate( {
Bundle savedInstanceState)super.onCreate(savedInstanceState);
setContentView(R.layout.test);
TextView tv = findViewById(R.id.tv);
tv.setText(R.string.mytext);
}
}
5.2.设置文本的大小
px 像素
dpi 指屏幕每英寸有多少个像素点
dip 长度单位,同一个单位在不同设备上有不同的显示效果,简称dp,与设备无关只与屏幕的尺寸有关
sp 专门用来设置字体大小,在系统中可以设置调整字体大小
px = dpi*dip/160
相同尺寸的手机,即使分辨率不同,同dp的组件占用屏幕比例也相同
//默认使用sp
tv.setTextSize(30);
5.3.设置文本颜色
使用自带的颜色,在Color类里面选择
tv.setTextColor(Color.BLACK);
使用自定义的颜色类似于argb() 前两位是透明度 ff为不透明,不设置透明度就直接透明了
tv.setTextColor(0xff00ff00);
在布局文件中设置 后两位是透明度ff为透明
八位编码#FFEEDDCC中,FF表示透明度,EE表示红色的浓度,DD表示 绿色的浓度,CC表示蓝色的浓度。透明度为FF表示完全不透明,为00表示完全透明。
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/mytext"
android:textColor="#8ec5fc"
tools:ignore="MissingConstraints" />
5.4.设置视图的宽高
视图宽高通过属性Android:layout_width表达,视图高度通过属性android:layout_height表达
match_parent 表示与上级视图保持一致
wrap_content 表示与内容自适应
以dp为单位的具体尺寸
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="17sp"
android:text="@string/mytext"
android:background="#00ffff"
android:layout_marginTop="5dp"
/>
public class MainActivity_2 extends AppCompatActivity {
protected void onCreate( {
Bundle savedInstanceState)super.onCreate(savedInstanceState);
setContentView(R.layout.test);
TextView tv = findViewById(R.id.tv);
ViewGroup.LayoutParams params = tv.getLayoutParams();
params.width = diptopx.dip(this,300);
tv.setLayoutParams(params);
}
}
工具类将dip转为px
public class diptopx {
public static int dip(Context context, float dip){
//获取当前手机的像素密度
float scale = context.getResources().getDisplayMetrics().density;
//四舍五入
return (int)(dip * scale +0.5f);
}
}
5.5.设置视图的间距
margin 当前视图和周围平级视图之间的距离
padding 当前视图与内部下级视图之间的距离
与前端的margin和padding类似有四个方向,layout_marginTop….
5.6.设置视图的对齐方式
layout_gravity 当前视图相对于上级视图的对齐方式
gravity下级视图相对于当前视图的对齐方式
left right top bottom
用|连接表示合并方向left|top 为左上方
6.布局
6.1LinearLayout线性布局
orientation
- horizontal 内部视图在水平方向从左到右排列
- vertical 内部视图在水平方向从上到下排列
如果不指定orientation属性,则LinearLayout默认水平方向排列
线性布局的权重概念,值得是线性布局的下级视图各自拥有多大比例的宽高
权重属性名叫layout_weight.但该属性不在LinearLayout节点设置,而在线性布局的直接下级视图设置,表示该下级视图占据的宽高比例。
- layout_width=0 layout_weight表示水平方向的宽高比例
- layout_height=0 layout_weight表示垂直方向的宽高比例
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="横排第一个"
android:textSize="17sp"
android:textColor="#000000"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="横排第一个"
android:textSize="17sp"
android:textColor="#000000"/>
</LinearLayout>
6.2RelativeLayout相对布局
相对布局的下级视图位置由其他视图决定。用于确定下级视图位置的参照物分两种:
- 与该视图自身平级的视图
- 该视图的上级视图
如果不设置下级视图的参照物,那么下级视图默认显示在RelativeLayout内部的左上角
与父容器相关的定位:
android:layout_alignParentTop | 控件顶部与父布局的顶部对齐。 |
---|---|
android:layout_alignParentBottom | 控件底部与父布局的底部对齐。 |
android:layout_alignParentLeft | 控件左边与父布局的左边对齐。 |
android:layout_alignParentRight | 控件右边与父布局的右边对齐。 |
android:layout_alignParentStart | 将控件的起始边(根据布局方向,可能是左边或右边)与父容器的起始边对齐。 |
android:layout_alignParentEnd | 将控件的结束边(根据布局方向,可能是右边或左边)与父容器的结束边对齐。 |
android:layout_centerHorizontal | 将控件水平居中对齐。 |
android:layout_centerVertical | 将控件垂直居中对齐。 |
android:layout_centerInParent | 水平垂直都居中 |
相对定位属性:
android:layout_above | 控件放置在指定控件的上方。 |
---|---|
android:layout_below | 控件放置在指定控件的下方。 |
android:layout_toLeftOf | 控件放置在指定控件的左边。 |
android:layout_toRightOf | 控件放置在指定控件的右边。 |
android:layout_alignTop | 控件的顶部与指定控件的顶部对齐。 |
android:layout_alignBottom | 控件的底部与指定控件的底部对齐。 |
android:layout_alignLeft | 控件的左边与指定控件的左边对齐。 |
android:layout_alignRight | 控件的右边与指定控件的右边对齐。 |
android:layout_alignStart | 将控件的起始边(根据布局方向,可能是左边或右边)与指定控件的起始边对齐。 |
android:layout_alignEnd | 将控件的结束边(根据布局方向,可能是右边或左边)与指定控件的结束边对齐。 |
android:layout_alignBaseline | 控件的基线与指定控件的基线对齐。 |
6.3GridLayout网格布局
支持多行多列的布局
默认从左往右从上到下
- columnCount属性,指定了网格的列数
- rowCount属性,指定了网格的行数
设置视图的对齐方式
- 采用layout_gravity属性,它指定了当前视图相对于上级视图的对齐方式
- 采用gravity属性,它指定了下级视图相对于当前视图的对齐方式
layout_gravity和gravity取值为left,top,right,bottom ,center
left|top为左上
<GridLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_columnWeight="1"
android:layout_rowWeight="1"/>
设置权重layout_columnWeight,layout_rowWeight
6.4滚动视图ScrollView
ScrollView,它是垂直方向的滚动视图;垂直方向滚动时,layout_width属性值设置为match_parent,layout_height属性值设置为wrap_content
HorizontalScrollViewScrollView,它是水平方向的滚动视图水平方向滚动时,layout_width属性值设置为wrap_content,layout_height属性设置为match_parent
6.5.Button
由TextView派生
- 有默认背景
- 文本居中对齐
相对于TextView新增的属性
android:textAllCaps=”true”
- true将小写转为大写
- false则不会转换
android:onClick=”doclick”
绑定事件(不常用)
6.6点击事件
setOnClickListener
public class MainActivity_2 extends AppCompatActivity {
protected void onCreate( {
Bundle savedInstanceState)super.onCreate(savedInstanceState);
setContentView(R.layout.test);
TextView tv = findViewById(R.id.tv);
Button bt = findViewById(R.id.bt);
bt.setOnClickListener(new MyOnclickListener(tv));
}
static class MyOnclickListener implements View.OnClickListener{
private final TextView tv;
public MyOnclickListener(TextView tv) {
this.tv = tv;
}
public void onClick(View v) {
String s = String.format("%s点击%s", SimpleUtil.dateformat(),((Button)v));
tv.setText(s);
}
}
创建公用OnClickListener
public class MainActivity_2 extends AppCompatActivity implements View.OnClickListener{
public void Onclick(View v){
if (v.getId()==R.id.bt){
}
}
}
6.7长按点击事件
true则为开启事件冒泡,false则不开启
bt.setOnLongClickListener(v->{
String s = String.format("%s点击%s", SimpleUtil.dateformat(),((Button)v));
tv.setText(s);
return true;
});
6.8禁用恢复按钮
是否允许点击enable
bt1.setEnabled(true);
bt1.setEnabled(false);
true启用,false禁用
6.9ImageView
图像视图ImageView
图像视图展示的图片通常位于res/drawable***目录,设置图像视图的显示图片有两种方式:
在XML文件中,通过属性android:src设置图片资源,属性值格式形如“@drawable/不含扩展名的图片名称”。
在Java代码中,调用setlmageResource方法设置图片资源,方法参数格式形如“R.drawable.不含扩展名的图片名称”。
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="5dp"
android:src="@drawable/test"/>
scaleType属性
matrix:使用matrix方式进行缩放。
fitXY:横向、纵向独立缩放,以适应该ImageView。
fitStart:保持纵横比缩放图片,并且将图片放在ImageView的左上角。
fitCenter:保持纵横比缩放图片,缩放完成后将图片放在ImageView的中央。
fitEnd:保持纵横比缩放图片,缩放完成后将图片放在ImageView的右下角。
center:把图片放在ImageView的中央,但是不进行任何缩放。
centerCrop:保持纵横比缩放图片,以使图片能完全覆盖ImageView。
centerInside:保持纵横比缩放图片,以使得ImageView能完全显示该图片。(用于缩小图片)
ImageView imageView = findViewById(R.id.image);
//设置图片源
imageView.setImageResource(R.drawable.test);
//设置缩放对齐方式
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
6.10ImageButton
同时显示文本和图像
通过Button上的drawable属性设置周围文本的图标
drawableTop:指定文字上方的图片
drawableBottom:指定文字下方的图片
drawableLeft:指定文字左方的图片
drawableRight:指定文字右方的图片
drawablePadding:指定图片与文字的间距
6.11计算器项目
package com.example.application1;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class CaculateActivity extends AppCompatActivity implements View.OnClickListener {
private TextView tv_result;
private String firstNum = "";
private String operator="";
private String secondNum = "";
private String showText = "";
private String result = "";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_caculate);
//获取结果文本视图
tv_result = findViewById(R.id.tv_result);
//给每个按钮绑定事件
findViewById(R.id.btn_cancel).setOnClickListener(this);
findViewById(R.id.btn_div).setOnClickListener(this);
findViewById(R.id.btn_mul).setOnClickListener(this);
findViewById(R.id.btn_clear).setOnClickListener(this);
findViewById(R.id.btn_seven).setOnClickListener(this);
findViewById(R.id.btn_eight).setOnClickListener(this);
findViewById(R.id.btn_nine).setOnClickListener(this);
findViewById(R.id.btn_plus).setOnClickListener(this);
findViewById(R.id.btn_four).setOnClickListener(this);
findViewById(R.id.btn_five).setOnClickListener(this);
findViewById(R.id.btn_six).setOnClickListener(this);
findViewById(R.id.btn_mius).setOnClickListener(this);
findViewById(R.id.btn_one).setOnClickListener(this);
findViewById(R.id.btn_two).setOnClickListener(this);
findViewById(R.id.btn_three).setOnClickListener(this);
findViewById(R.id.btn_sqrt).setOnClickListener(this);
findViewById(R.id.btn_divone).setOnClickListener(this);
findViewById(R.id.btn_zero).setOnClickListener(this);
findViewById(R.id.btn_point).setOnClickListener(this);
findViewById(R.id.btn_equal).setOnClickListener(this);
}
public void onClick(View v) {
String inputtext;
if (v.getId() == R.id.btn_sqrt){
inputtext = "√";
}else{
inputtext = ((TextView) v).getText().toString();
}
if (v.getId() == R.id.btn_clear) {
clear();
}else if(v.getId() == R.id.btn_cancel) {
cancel();
}else if(v.getId() == R.id.btn_plus) {
operator = inputtext;
refreshText(showText+inputtext);
}else if (v.getId() == R.id.btn_mius) {
operator = inputtext;
refreshText(showText+inputtext);
}else if (v.getId() == R.id.btn_mul) {
operator = inputtext;
refreshText(showText+inputtext);
}else if (v.getId() == R.id.btn_div) {
operator = inputtext;
refreshText(showText+inputtext);
}else if (v.getId() == R.id.btn_equal) {
double caculate_result = Caculate();
refreshOpreator(String.valueOf(caculate_result));
refreshText(showText+"="+caculate_result);
}else if (v.getId() == R.id.btn_sqrt) {
double sqrt_result = Math.sqrt(Double.parseDouble(firstNum));
refreshOpreator(String.valueOf(sqrt_result));
refreshText(showText + "^=" + sqrt_result);
}else if (v.getId()==R.id.btn_divone) {
double sqrt_result = 1/Double.parseDouble(firstNum);
refreshOpreator(String.valueOf(sqrt_result));
refreshText(showText + "/=" + sqrt_result);
}
else{
if (!result.isEmpty()){
clear();
}
if (operator.isEmpty()) {
firstNum = firstNum + inputtext;
} else {
secondNum = secondNum + inputtext;
}
//整数不需要前面的0
if (showText.equals("0") && !inputtext.equals(".")) {
refreshText(inputtext);
} else {
refreshText(showText + inputtext);
}
}
}
private void cancel(){
if (showText.length()>1){
refreshText(showText.substring(0,showText.length()-1));
}else{
refreshText("0");
}
}
private void refreshText(String text){
showText = text;
tv_result.setText(showText);
}
private void clear() {
refreshOpreator("");
refreshText("0");
}
private void refreshOpreator(String newresult){
result = newresult;
firstNum = result;
secondNum = "";
operator = "";
}
private double Caculate(){
switch (operator){
case "+":
return Double.parseDouble(firstNum) + Double.parseDouble(secondNum);
case "-":
return Double.parseDouble(firstNum) - Double.parseDouble(secondNum);
case "X":
return Double.parseDouble(firstNum) * Double.parseDouble(secondNum);
case "÷":
return Double.parseDouble(firstNum) / Double.parseDouble(secondNum);
}
return 0;
}
}
7.Activity
7.1启停activity
启动新Activity
startActivity(new Intent(原页面.this,目标页面.class))
返回
finnish()//结束当前的活动页面
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn).setOnClickListener(this);
}
public void onClick(View v) {
startActivity(new Intent(this,MainActivity2.class));
}
}
public class MainActivity2 extends AppCompatActivity implements View.OnClickListener {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
findViewById(R.id.btn1).setOnClickListener(this);
}
public void onClick(View v) {
if (v.getId() == R.id.btn1){
finish();
}
}
}
7.2Activity生命周期
onCreate:表示Activity正在被创建。生命周期的第一个方法,当打开一个activity时首先回调这个方法。在这个方法中一般做一些初始化工作,例如加载界面布局资源(setContentView)、数据初始化(findviewbyid)
onRestart : 表示Activity正在被重新启动。当前activity从不可见变为可见状态时这个方法就会回调。一般是用户行为导致,比如用户摁home键回到桌面,当用户再次回到本activity时,当前activity 走 onRestart->onStart->onResume
onStart :表示activity正在被启动。activity可见,未出现在前台。可以理解为activity已经显示出来了,但是我们看不到,不能与之交互。
onResume :表示activity已经可见,并且出现在前台,可以与用户进行交互。例如activity上有Button,此时我们就可以进行点击了。
onPause:表示activity正在停止,接着很快执行onStop。注意极端情况下,本Activity跳转其他activity后快速的回到当前activity时,当前activity的生命周期:onPause->onResume
但是这个“快速回到”要很快,一般情况下都是onPause->onStop->onRestart->onStart->onResume。这里不能做太耗时操作,可以做一些数据存储,动画停止工作。
onStop:表示activity即将停止,可以做一些回收工作。但是不能太耗时。
onDestroy :activity即将被销毁 ,activity生命周期最后一个回调,这里可以做一些回收工作,资源释放。
onNewIntent:重用已有的活动实例
7.3Activity启动模式
1.在配置文件中设置启动模式
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="standard">
standard
默认启动模式
启动Activity的会依照顺序依次被压入Task栈内
singleTop
如果栈顶为新建的Activity(目标Activity),那么就不会重复创建新的Activity
适合开启渠道多,多应用开启调用的Activity,通过这种设置可以避免创建过的Activity被重复创建,多数通过动态设置使用
singleTask
栈内复用
程序的主界面,耗费系统资源的Activity
全局唯一模式
singleInstance
单独为它创建一个任务栈,新的Task只会有它一个实例。如果已经创建过,则不会创建新的Activity,而是将之前的Activity唤醒
2.在代码中动态设置
启动标志的取值说明如下:
Intent.FLAG_ACTIVITY_NEW_TASK:开辟一个新的任务栈
Intent.FLAG_ACTIVITY_SINGLE_TOP:当栈顶为待跳转的活动实例之时,则重用栈顶的实例
Intent.FLAG_ACTIVITY_CLEAR_TOP:当栈中存在待跳转的活动实例时,则重新创建一个新实例,并清除原实例上方的所有实例
Intent.FLAG_ACTIVITY_NO_HISTORY:栈中不保存新启动的活动实例
Intent.FLAG_ACTIVITY_CLEAR_TASK:跳转到新页面时,栈中的原有实例都被清空
public void onClick(View v) {
Intent intent = new Intent(this,MainActivity2.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
登录后不再返回登录界面
Intent intent = new Intent(this,MainActivity2.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
7.4传递数据
7.4.1显示Intent和隐式Intent
Intent是各个组件之间信息沟通的桥梁,用于各组件的通信
显示Intent,直接指定来源活动和目标活动,属于精确匹配
- 在Intent的构造函数中指定
- 调用意图对象的SetClass方法指定
- 调用意图对象的SetComponent方法指定
//第一种
Intent intent = new Intent(this,MainActivity2.class);
//第二种,调用意图对象的setClass方法指定
Intent intent = new Intent();
intent.setClass(this, MainActivity2.class);
//第三种,调用意图对象的SetComponet方法指定
ComponentName componentName = new ComponentName(this, MainActivity2.class);
intent.setComponent(componentName);
隐式Intent,没有明确指定要跳转的目标活动,只要给出一个动作字符串让系统自动匹配,属于模糊匹配
String phoneNumber ="1234";
Intent intent = new Intent();
//设置意图动作
intent.setAction(Intent.ACTION_DIAL);
Uri uri = Uri.parse("tel:"+phoneNumber);
intent.setData(uri);
startActivity(intent);
启动自定义的程序
Intent intent = new Intent();
intent.setAction("android.intent.action.RICHU");
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
在要启动的程序中配置
<intent-filter>
<action android:name="android.intent.action.RICHU" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
常见系统动作的取值说明
intent 类的系统动作常量名 | 系统动作的常量值 | 说明 |
---|---|---|
ACTION_MAIN | android.intent.action.MAIN | App启动时的入口 |
ACTION_VIEW | android.intent.action.VIEW | 向用户显示数据 |
ACTION_SEND | android.intent.action.SEND | 分享内容 |
ACTION_CALL | android.intent.action.CALL | 直接拨号 |
ACITON_DIAL | android.intent.action.DIAL | 准备拨号 |
ACTION_SENDTO | android.intent.action.SENDTO | 发送短信 |
ACTION_ANSWER | android.intent.action.ANSWER | 接听电话 |
7.4.2向下一个Activity发送数据
使用Bundle对象存储待传递的数据信息
发送
Intent intent = new Intent(this, MainActivity2.class);
//创建一个新包裹
Bundle bundle = new Bundle();
Button send = findViewById(R.id.btn);
bundle.putString("request_time", MyDate.gettime());
bundle.putString("request_content",send.getText().toString());
intent.putExtras(bundle);
startActivity(intent);
接收
TextView show = findViewById(R.id.show);
Bundle bundle = getIntent().getExtras();
String request_time = bundle.getString("request_time");
String request_content = bundle.getString("request_content");
String test = String.format("hahah\nha%s\n%s",request_time,request_content);
show.setText(test);
7.4.3向上一个Activity返回数据
1.上一个页面打包好请求数据后,调用StartActivityForResult方法执行跳转动作
2.下一个页面接收并解析请求数据,进行相应处理
3.下一个页面返回上一个页面时,打包应答数据并调用SetResult方法返回数据包裹
4.上一个页面重写方法OnActivityResult,解析获得下一个页面的返回数据
package com.example.application2;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.example.application2.Dateutil.MyDate;
public class MainActivity3 extends AppCompatActivity implements View.OnClickListener {
private String myrequest = "this is a test";
private String getrequest;
private ActivityResultLauncher<Intent> register;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
TextView begin = findViewById(R.id.begin);
begin.setText(myrequest);
findViewById(R.id.mybtn).setOnClickListener(this);
register = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), o -> {
if (o != null){
Intent intent = o.getData();
if (intent != null && o.getResultCode() == Activity.RESULT_OK){
Bundle bundle = intent.getExtras();
String request_time = bundle.getString("response");
String request_content = bundle.getString("content");
getrequest = String.format("hahah\n回复%s\n%s",request_time,request_content);
TextView end = findViewById(R.id.end);
end.setText(getrequest);
}
}
});
}
public void onClick(View v) {
Intent intent = new Intent(this, MainActivity4.class);
Bundle bundle = new Bundle();
bundle.putString("request_time", MyDate.gettime());
bundle.putString("request_content",myrequest);
intent.putExtras(bundle);
register.launch(intent);
}
}
package com.example.application2;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.icu.util.BuddhistCalendar;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.example.application2.Dateutil.MyDate;
public class MainActivity4 extends AppCompatActivity implements View.OnClickListener {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
TextView get = findViewById(R.id.get);
Bundle bundle = getIntent().getExtras();
String request_time = bundle.getString("request_time");
String request_content = bundle.getString("request_content");
String test = String.format("hahah\n接收%s\n%s",request_time,request_content);
get.setText(test);
findViewById(R.id.set).setOnClickListener(this);
}
public void onClick(View v) {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("response", MyDate.gettime());
//待返回的数据
bundle.putString("content","chli");
intent.putExtras(bundle);
setResult(Activity.RESULT_OK,intent);
finish();
}
}
7.4.4利用资源配置文件配置字符串
String test = getString(R.string.test);
7.4.5利用元数据传递配置信息
<meta-data android:name="test" android:value="test"></meta-data>
8.Drawable
drawable-ldpi存放低分辨率的图片,基本没有这样的手机了
drawable-mdpi存放中等分辨率的图片,使用较少
drawable-hdpi存放高分辨率的图片 4-5英寸
drawable-xhdpi存放高分辨率的图片 5-5.5英寸
drawable-xxhdpi存放高分辨率的图片 6-6.5英寸
drawable-xxxhdpi存放高分辨率的图片 >7英寸
8.1形状图形
Shape图形又称形状图形,它用来描述常见的几何形状,包括矩形,圆角矩形,圆形,椭圆等等
rectangle矩形
oval椭圆
line直线
ring圆环
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#66ffaa"/>
<stroke android:width="1dp" android:color="#66ffaa"/>
</shape>
package com.example.application2;
import android.os.Bundle;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity6 extends AppCompatActivity implements View.OnClickListener {
private View v_content;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main6);
v_content = findViewById(R.id.content);
findViewById(R.id.rectangle).setOnClickListener(this);
findViewById(R.id.oval).setOnClickListener(this);
v_content.setBackgroundResource(R.drawable.react_drawable);
}
public void onClick(View v) {
if (v.getId() == R.id.rectangle){
v_content.setBackgroundResource(R.drawable.react_drawable);
}else if(v.getId() == R.id.oval){
v_content.setBackgroundResource(R.drawable.react1_drawable);
}
}
}
size(尺寸)
size时shape的下级节点,描述形状图形的宽高尺寸,若无size,则与宿主图形一样(View)
- height
- width
stroke(描边)
- color颜色
- dashGap像素;类型
- dashWidth虚线的宽度
- width厚度
dashWidth和dashGap为0为实线
solid填充
color:颜色
padding
上下左右
gradient渐变
- angle 整形渐变的起始角度
- type 渐变的类型
类型 | 说明 |
---|---|
linear | 线性渐变 |
radial | 放射渐变 |
sweep | 滚动渐变 |
8.2九宫格图片
test.9.png
上下左右边缘线
黑线区域以内的图像会拉伸,黑线以外的图像保持原状
内部文字只能放在黑线区域内
8.3状态列表图形
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/test" android:state_pressed="true"/>
<item android:drawable="@drawable/tests"/>
</selector>
state
状态类型的属性名称 | 说明 | 适用的组件 |
---|---|---|
state_pressed | 是否按下 | 按钮Button |
state_checked | 是否勾选 | 复选框CheckBox、单选按钮RadioButton |
state_focused | 是否获取焦点 | 文本编辑EditText |
state_selected | 是否选中 | 各组件通用 |
8.4复选框CheckBOx
<CheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="系统checkbox"/>
checked属性
设置是否选择,可以设置默认选择
public class MainActivity7 extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main7);
CheckBox cb = findViewById(R.id.select);
cb.setOnCheckedChangeListener(this);
}
//CompoundButton
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
String desc = String.format("您%s这个Checkbox",isChecked?"勾选":"取消");
buttonView.setText(desc);
}
}
视图—》文本视图 —》按钮 —》复合按钮 ===》复选框、单选按钮、开关按钮
XML中CompoundButton
- checked;指定按钮的勾选状态
- button:指定左侧勾选图标的图像资源
Java代码中CompoundButton
- setChecked设置按钮的勾选状态
- setButtonDrawable设置左侧勾选图标的图像资源
- setOnCheckedChangeListener设置勾选状态变化的监听器
- isChecked判断按钮是否勾选
8.5Switch
Switch是开关按钮,它在选中和取消选中时可展现的界面元素比复选框丰富
textOn:打开时的文本
textOff:设置左侧关闭的文本
track:设置开关轨道的背景
thumb:设置开关表示的图标
<Switch
android:id="@+id/select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center"
android:text="switch"
/>
//使用checkBox实现Switch开关按钮
<CheckBox
android:id="@+id/select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start|center"
android:text="switch"
//设置按钮为空
android:button="@null"
android:background="@drawable/ic_launcher_background"
/>
8.6单选按钮
一组中只能选择一个
RadioButton,放入一个RadioGroup组内部
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提示信息"
android:layout_gravity="center"/>
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/male"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="man"/>
<RadioButton
android:id="@+id/female"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="woman"/>
</RadioGroup>
监听单选按钮
public class MainActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {
private TextView tv;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RadioGroup radioGroup = findViewById(R.id.gender);
tv = findViewById(R.id.show);
radioGroup.setOnCheckedChangeListener(this);
}
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId == R.id.male){
tv.setText("male");
}else if (checkedId == R.id.female){
tv.setText("female");
}
}
}
8.7文本输入框
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="输入登录信息"/>
去掉默认边框
<EditText
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="输入密码信息"
android:background="@null"
android:hint="自定义"
android:inputType="text"/>
inputType:指定输入的文本类型
允许输入的最大长度
hint:指定提示文本的内容
textColorHint指示文本的颜色
输入类型 | 说明 |
---|---|
text | 文本 |
textPassword | 文本密码 |
number | 整数型 |
numberSigned | 带符号的数字 |
numberDecimal | 带小数点的数字 |
numberpassword | 数字密码 |
datetime | 时间日期格式 |
date | 日期格式 |
time | 事件格式 |
8.8焦点变更监听器
Toast.makeText(this,”请输入8位密码”,Toast.LENGTH_SHORT).show();
//弹出提示
LENGTH_SHORT
LENGTH_LONG 持续时间更长
public class MainActivity2 extends AppCompatActivity implements View.OnFocusChangeListener {
private EditText et_message;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
et_message = findViewById(R.id.message);
EditText et_password = findViewById(R.id.password);
et_password.setOnFocusChangeListener(this);
}
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus){
String password = et_message.getText().toString();
if (TextUtils.isEmpty(password) || password.length() < 8){
et_message.requestFocus();
Toast.makeText(this,"请输入8位用户名",Toast.LENGTH_SHORT).show();
}
}
}
}
8.9文本变化监听器
调用编辑框对象的addTextChangelistener方法即可注册文本监听器
文本监听器的接口名位TextWatcher,该接口提供了3个监控方法
- beforeTextChanged
- OnTextChanged
- afterTextChanged
隐藏输入框
public class hidenView {
public static void HidenInput(Activity ac, View v){
//从系统获取输入法管理器
InputMethodManager imm = (InputMethodManager) ac.getSystemService(Context.INPUT_METHOD_SERVICE);
//关闭输入法 得到窗口管理器
imm.hideSoftInputFromWindow(v.getWindowToken(),0);
}
}
package com.example.application3;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.view.inputmethod.EditorBoundsInfo;
import android.widget.EditText;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.example.application3.utils.hidenView;
public class MainActivity2 extends AppCompatActivity{
private EditText et_password;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
et_password = findViewById(R.id.password);
et_password.addTextChangedListener(new HidenTextWatcher(et_password,11));
}
//定义,当达到指定值后隐藏文本框
private class HidenTextWatcher implements TextWatcher{
private EditText et;
private int maxLength;
public HidenTextWatcher(EditText et,int maxlength){
this.et = et;
this.maxLength = maxlength;
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
public void afterTextChanged(Editable s) {
String test = s.toString();
if (test.length() == 11 && maxLength==11){
hidenView.HidenInput(MainActivity2.this,et_password);
}
}
}
}
8.10提醒对话框
AlertDialog
借助建造器AlertDialog.Builder才能完成设置
调用建造器的create方法生产对话框实例,再调用对话框实例的show方法,在页面提示对话框
public class MainActivity3 extends AppCompatActivity implements View.OnClickListener {
private TextView tv_dialog;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
findViewById(R.id.btn_diolog).setOnClickListener(this);
tv_dialog = findViewById(R.id.diolog_view);
}
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("尊敬的用户");
builder.setMessage("是否卸载");
builder.setPositiveButton("取消",((dialog, which) -> {
tv_dialog.setText("没有卸载");
}));
builder.setNegativeButton("确认",((dialog, which) -> {
tv_dialog.setText("卸载成功");
}));
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
}
setIcon:设置图标
setTitle设置对话框的标题文本
setMessage设置对话框的文本
setPositiveButton设置肯定按钮
setNegativeButton 设置否定按钮
setNeutralButton设置中性按钮
8.11日期选择器
<DatePicker
android:id="@+id/dp_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:calendarViewShown="false"
android:datePickerMode="spinner" />
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
findViewById(R.id.dp_btn).setOnClickListener(this);
dp = findViewById(R.id.dp_date);
dp_show =findViewById(R.id.dp_dateshow);
}
public void onClick(View v) {
if (v.getId() == R.id.dp_btn){
String format = String.format("日期是%d年%d月%d日",dp.getYear(),dp.getMonth()+1,dp.getDayOfMonth());
dp_show.setText(format);
}
}
新建一个Calender实例获取时间
Calendar calendar = Calendar.getInstance();
calendar.get(Calendar.YEAR);
calendar.get(Calendar.MONTH);
calendar.get(Calendar.DAY_OF_MONTH);
DatePickDiolog
public void onClick(View v) {
if (v.getId() == R.id.dp_btn){
/* String format = String.format("日期是%d年%d月%d日",dp.getYear(),dp.getMonth()+1,dp.getDayOfMonth());
dp_show.setText(format);*/
/* Calendar calendar = Calendar.getInstance();
calendar.get(Calendar.YEAR);
calendar.get(Calendar.MONTH);
calendar.get(Calendar.DAY_OF_MONTH);*/
DatePickerDialog dialog = new DatePickerDialog(this,this,2090,5,11);
dialog.show();
}
}
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
String format = String.format("日期是%d年%d月%d日",year,month+1,dayOfMonth);
dp_show.setText(format);
}
8.12时间选择器
TimePickDialog
TimePicker类似DatePicker,可以选择具体的小时
TimePickDiolog的用法类似DatePickDiolog
TimePickDialog dialog = new TimePickDialog(this,[android.R.style.Theme.Holo_Light_dialog ],this,calendar.get(Calendar.HOUR_OF_DAY),calender.get(Calendar.MINUTE),true)
//android.R.style.Theme.Holo_Light_dialog 设置为横向选择样式
//true 为是否开启24小时制
8.13小案例
页面布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RadioGroup
android:id="@+id/rg_login"
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rg_withpwd"
android:checked="true"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/login_withpwd"
android:textSize="@dimen/item_line_height"
/>
<RadioButton
android:id="@+id/withoutpwd"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="@string/login_withoutpwd"
android:textSize="@dimen/item_line_height"/>
</RadioGroup>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/phonenumber"
android:gravity="center"
android:textColor="@color/black"
android:textSize="@dimen/item_line_height"/>
<EditText
android:id="@+id/et_phone"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
android:background="@drawable/shape"
android:hint="@string/inputnumber"
android:maxLength="11"
android:textColor="@color/black"
android:inputType="number"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_password"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/loginpassword"
android:gravity="center"
android:textColor="@color/black"
android:textSize="@dimen/item_line_height"/>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
android:background="@drawable/shape"
android:hint="@string/inputpassword"
android:maxLength="6"
android:textColor="@color/black"
android:inputType="numberPassword"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
<Button
android:id="@+id/btn_password"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/forgetpassword"
android:layout_alignParentEnd="true"
android:background="@color/gray"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
</RelativeLayout>
</LinearLayout>
<CheckBox
android:id="@+id/check_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/remenumber"/>
<Button
android:id="@+id/beginlogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login"
android:textSize="@dimen/item_line_height"
android:textColor="@color/gray"/>
</LinearLayout>
代码逻辑
package com.example.application3;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.ViewUtils;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.example.application3.utils.hidenView;
import java.util.Random;
public class MainActivity5 extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener, View.OnClickListener {
private TextView tv_password;
private EditText et_password;
private Button btn_password;
private CheckBox checkBox;
private RadioButton withpwd;
RadioButton withoutpwd;
private ActivityResultLauncher<Intent> register;
private EditText et_phone;
private Button btn_login;
private String clearPassword="111111";
private String verifycode;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main5);
RadioGroup group= findViewById(R.id.rg_login);
group.setOnCheckedChangeListener(this);
tv_password = findViewById(R.id.tv_password);
et_password = findViewById(R.id.et_password);
btn_password = findViewById(R.id.btn_password);
btn_password.setOnClickListener(this);
checkBox = findViewById(R.id.check_number);
et_phone = findViewById(R.id.et_phone);
withpwd = findViewById(R.id.rg_withpwd);
withoutpwd = findViewById(R.id.withoutpwd);
btn_login = findViewById(R.id.beginlogin);
btn_login.setOnClickListener(this);
et_phone.addTextChangedListener(new HideTextWatcher(et_phone,11));
et_password.addTextChangedListener(new HideTextWatcher(et_password,6));
register = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
public void onActivityResult(ActivityResult o) {
Intent intent = o.getData();
if (intent != null && o.getResultCode()== Activity.RESULT_OK){
String newpassword = intent.getStringExtra("password");
clearPassword = newpassword;
}
}
});
}
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId==R.id.rg_withpwd){
tv_password.setText(getString(R.string.loginpassword));
et_password.setHint(getString(R.string.inputpassword));
btn_password.setText(getString(R.string.forgetpassword));
checkBox.setVisibility(View.VISIBLE);
}else if (checkedId == R.id.withoutpwd){
tv_password.setText(getString(R.string.verifycode));
et_password.setHint(getString(R.string.input_verifycode));
btn_password.setText(getString(R.string.getverifycode));
checkBox.setVisibility(View.GONE);
}
}
public void onClick(View v) {
String phone = et_phone.getText().toString();
if (v.getId() == R.id.btn_password){
if (withpwd.isChecked()){
Intent intent = new Intent(this, MainActivity7.class);
intent.putExtra("phone",phone);
register.launch(intent);
}else if (withoutpwd.isChecked()){
verifycode = String.format("%06d",new Random().nextInt(999999));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("验证码");
builder.setMessage(verifycode);
builder.setPositiveButton("确认",null);
AlertDialog dialog = builder.create();
dialog.show();
}
}else if (v.getId() == R.id.beginlogin){
String password = et_password.getText().toString();
if (withpwd.isChecked()){
if (!password.equals(clearPassword)){
Toast.makeText(this,"请输入正确的密码",Toast.LENGTH_SHORT).show();
return;
}
LoinSuccess();
}else if (withoutpwd.isChecked()){
if (!password.equals(verifycode)){
Toast.makeText(this,"请输入正确的验证码",Toast.LENGTH_SHORT).show();
return;
}
LoinSuccess();
}
}
}
private void LoinSuccess() {
String mes = "登录成功";
AlertDialog.Builder builder= new AlertDialog.Builder(this);
builder.setTitle("登录成功");
builder.setMessage(mes);
builder.setPositiveButton("确认返回",((dialog, which) -> {
finish();
}));
builder.setNegativeButton("我再看看",null);
Dialog dialog = builder.create();
dialog.show();
}
private class HideTextWatcher implements TextWatcher {
private EditText et;
private int maxlength;
public HideTextWatcher(EditText et_phone,int maxlength){
this.et = et_phone;
this.maxlength = maxlength;
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
public void afterTextChanged(Editable s) {
if (s.toString().length() == maxlength ){
hidenView.HidenInput(MainActivity5.this,et);
}
}
}
}
找回密码布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/newpassword"
android:gravity="center"
android:textColor="@color/black"
android:textSize="@dimen/item_line_height"/>
<EditText
android:id="@+id/new_password"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
android:background="@drawable/shape"
android:hint="@string/inputnewpassword"
android:maxLength="6"
android:textColor="@color/black"
android:inputType="number"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/newpasswordagain"
android:gravity="center"
android:textColor="@color/black"
android:textSize="@dimen/item_line_height"/>
<EditText
android:id="@+id/new_passwordagain"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
android:background="@drawable/shape"
android:hint="@string/inputnewpasswordagain"
android:maxLength="6"
android:textColor="@color/black"
android:inputType="number"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_newverify"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/verifycode"
android:gravity="center"
android:textColor="@color/black"
android:textSize="@dimen/item_line_height"/>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<EditText
android:id="@+id/et_newverify"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
android:background="@drawable/shape"
android:hint="@string/input_verifycode"
android:maxLength="6"
android:textColor="@color/black"
android:inputType="numberPassword"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
<Button
android:id="@+id/btn_newverify"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/getverifycode"
android:layout_alignParentEnd="true"
android:background="@color/gray"
android:textColorHint="@color/gray"
android:textSize="@dimen/item_line_height"/>
</RelativeLayout>
</LinearLayout>
<Button
android:id="@+id/beginnewpassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/click"
android:textColor="@color/gray"
android:textSize="@dimen/item_line_height"/>
</LinearLayout>
找回密码逻辑
package com.example.application3;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import java.util.Random;
public class MainActivity7 extends AppCompatActivity implements View.OnClickListener {
private String phone;
private String verifycode;
private EditText tv_newpassword;
private EditText tv_newpasswordagin;
private EditText et_newverify;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main7);
phone = getIntent().getStringExtra("phone");
findViewById(R.id.btn_newverify).setOnClickListener(this);
findViewById(R.id.beginnewpassword).setOnClickListener(this);
tv_newpassword = findViewById(R.id.new_password);
tv_newpasswordagin = findViewById(R.id.new_passwordagain);
et_newverify = findViewById(R.id.et_newverify);
}
public void onClick(View v) {
if (v.getId()==R.id.btn_newverify){
verifycode = String.format("%06d",new Random().nextInt(999999));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("验证码");
builder.setMessage(verifycode);
builder.setPositiveButton("确认",null);
AlertDialog dialog = builder.create();
dialog.show();
}else if (v.getId() == R.id.beginnewpassword){
String passwd = tv_newpassword.getText().toString();
String newpasswd = tv_newpasswordagin.getText().toString();
String mycode = et_newverify.getText().toString();
if (passwd.length()<6 || newpasswd.length()<6){
Toast.makeText(this,"密码长度不合适",Toast.LENGTH_SHORT).show();
return;
}
if (!passwd.equals(newpasswd)){
Toast.makeText(this,"两次密码不一致",Toast.LENGTH_SHORT).show();
return;
}
if (!mycode.equals(verifycode)){
Toast.makeText(this,"验证码错误",Toast.LENGTH_SHORT).show();
return;
}
Intent intent = new Intent();
intent.putExtra("password",passwd);
setResult(Activity.RESULT_OK,intent);
finish();
}
}
}
9.SharePreferences
SharePreferences是Android的一个轻量级存储工具,采用键值对的方式
/data/data/应用包名/shared_prefs/文件名.xml
App个性化配置信息,用户使用App的行为信息、临时需要保存的片段信息
package com.richuff.application4;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText et_name;
private EditText et_age;
private EditText et_height;
private EditText et_weight;
private SharedPreferences preferences;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_name = findViewById(R.id.et_name);
et_age = findViewById(R.id.et_age);
et_height = findViewById(R.id.et_height);
et_weight = findViewById(R.id.et_weight);
findViewById(R.id.submit).setOnClickListener(this);
preferences = getSharedPreferences("config", Context.MODE_PRIVATE);
}
public void onClick(View v) {
String name = et_name.getText().toString();
String age = et_age.getText().toString();
String height = et_height.getText().toString();
String weight = et_weight.getText().toString();
SharedPreferences.Editor ediit = preferences.edit();
ediit.putString("name",name);
ediit.putInt("age",Integer.parseInt(age));
ediit.putFloat("height",Float.parseFloat(height));
ediit.putFloat("weight",Float.parseFloat(weight));
ediit.apply();
}
}
data/data/包名/shared_prefs
private void reload() {
//取数据
String name = preferences.getString("name",null);
if (name!=null){
et_name.setText(name);
}
int age = preferences.getInt("age",0);
if (age !=0){
et_age.setText(String.valueOf(age));
}
float height = preferences.getFloat("height",0f);
if (height !=0f){
et_height.setText(String.valueOf(height));
}
float weight = preferences.getFloat("weight",0f);
if (height !=0f){
et_weight.setText(String.valueOf(weight));
}
}
10.SQLLite
1.管理类
openDatabase:打开指定路径的数据库
isopen:判断数据库是否打开
close:关闭数据库
getVersion:获取数据库的般般号
setVersion:设置数据库的版本号
package com.example.application3;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity8 extends AppCompatActivity implements View.OnClickListener {
private String mDatabase;
private TextView tv_database;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main8);
findViewById(R.id.createdatabase).setOnClickListener(this);
findViewById(R.id.deletedatabase).setOnClickListener(this);
//数据库的路径
mDatabase = getFilesDir() + "/test.db";
tv_database = findViewById(R.id.tv_view);
}
public void onClick(View v) {
if (v.getId() == R.id.createdatabase){
SQLiteDatabase sqLiteDatabase = openOrCreateDatabase(mDatabase, Context.MODE_PRIVATE, null);
String desc = String.format("%s创建%s",sqLiteDatabase.getPath(), "成功");
tv_database.setText(desc);
}else if(v.getId() == R.id.deletedatabase) {
boolean result = deleteDatabase(mDatabase);
String desc = String.format("%s删除%s",mDatabase,result ? "成功" : "失败");
tv_database.setText(desc);
}
}
}
SQLiteOPenHeaper
package com.example.application3.datebase;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.example.application3.enity.user;
public class UserDBHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "user.db";
private static final int DB_VSERSION = 1;
private static final String TABLE_NAME = "user_info";
public static UserDBHelper mhelper = null;
private SQLiteDatabase mRdb = null;
private SQLiteDatabase mWdb = null;
private UserDBHelper(Context context){
super(context,DB_NAME,null,DB_VSERSION);
}
//利用单例模式获取数据库帮助器的唯一实例
public static UserDBHelper getInstance(Context context){
if (mhelper == null){
mhelper = new UserDBHelper(context);
}
return mhelper;
}
//打开数据库的读连接
public SQLiteDatabase openReadLink(){
if (mRdb == null || mRdb.isOpen()){
mRdb = mhelper.getReadableDatabase();
}
return mRdb;
}
//打开数据库的写连接
public SQLiteDatabase openWriteLink(){
if (mWdb == null || mWdb.isOpen()){
mWdb = mhelper.getWritableDatabase();
}
return mWdb;
}
//关闭数据库
public void closeLink(){
if (mRdb != null && mRdb.isOpen()){
mRdb.close();
mRdb = null;
}
if (mWdb != null && mWdb.isOpen()){
mWdb.close();
mWdb = null;
}
}
public void onCreate(SQLiteDatabase db) {
String sql = "create table if not exists user_info(_id integer primary key autoincrement not null,"
+"name varchar not null"+")";
db.execSQL(sql);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
增删改
//增
public long insert(user user){
ContentValues values = new ContentValues();
values.put("name",user.name);
return mWdb.insert(TABLE_NAME,null,values);
}
//删
public long delete(String name){
return mWdb.delete(TABLE_NAME,"name=?",new String[]{name});
}
//改
public long update(user user){
ContentValues contentValues = new ContentValues();
contentValues.put("name",user.name);
return mWdb.update(TABLE_NAME,contentValues,"name=?",new String[]{user.name});
}
2.事务类
beginTransaction:开始事务
setTransactionSuccessful:设置事务成功的标志
endTransaction:结束事务
3.数据库版本更新
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
4.外部存储和内部存储,外部SD卡
读写文件操作类
package com.richuff.application4.enity;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileUtil {
public static void saveText(String path,String txt) throws IOException {
BufferedWriter os = null;
try{
os = new BufferedWriter(new FileWriter(path));
} catch (IOException e) {
e.printStackTrace();
}finally {
if (os!=null){
os.close();
}
}
}
public static String openText(String path) throws IOException {
BufferedReader rs = null;
StringBuffer sb = new StringBuffer();
try{
rs = new BufferedReader(new FileReader(path));
String line = null;
while((line=rs.readLine())!=null){
sb.append(line);
}
}catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if (rs != null) {
rs.close();
}
}
return rs.toString();
}
}
获取外部存储的私有空间
String path = directory+ File.separatorChar+fileName;
获取外部存储公共空间
//获取外部公共空间
directory=Environment.getExternalStorageState();
内部存储私有空间
//内部存储私有空间
directory = getFilesDir().toString();
5.在存储卡读写图片文件
Bitmap
BitmaoFactory读写各种来源的图片
decodeResource 从资源文件中读取图片信息
decodeFile 指定路径的图片获取到bitmap对象
decodeStream从输入流获取位图数据
public void onClick(View v) {
if (v.getId() == R.id. create){
String fileName = System.currentTimeMillis() + ".png";
path = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString()+ File.separatorChar+fileName;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.gl);
FileUtil.saveImage(path,bitmap);
}else if (v.getId() == R.id.delete){
Bitmap bitmap = FileUtil.openImage(path);
im.setImageBitmap(bitmap);
}
}
文件操作FileUtil
public static void saveImage(String path, Bitmap bitmap) {
FileOutputStream fos = null;
try{
fos = new FileOutputStream(path);
bitmap.compress(Bitmap.CompressFormat.PNG,100,fos);
}catch (Exception e){
e.printStackTrace();
}finally {
if (fos != null){
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
public static Bitmap openImage(String path) {
Bitmap bitmap = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(path);
bitmap = decodeStream(fis);
}catch (Exception e){
e.printStackTrace();
}finally {
if (fis != null){
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return bitmap;
}
11.Application
11.1Application
Application是Android的一大组件,在App运行过程中有且仅有一个Application对象贯穿整个生命周期
package com.richuff.application4;
import android.content.res.Configuration;
import android.util.Log;
import androidx.annotation.NonNull;
public class Application extends android.app.Application {
public void onCreate() {
super.onCreate();
Log.d("richu","onCreate");
}
//Andriod设备上不会执行
public void onTerminate() {
super.onTerminate();
Log.d("richu","onTerminate");
}
//配置改变时
public void onConfigurationChanged( {
Configuration newConfig)super.onConfigurationChanged(newConfig);
Log.d("richu","onConfigure");
}
}
11.2全局变量
适用:会频繁读取的信息,用户名
package com.richuff.application4;
import android.content.res.Configuration;
import android.util.Log;
import androidx.annotation.NonNull;
import java.util.HashMap;
public class Application extends android.app.Application {
private static Application application;
public void onCreate() {
super.onCreate();
}
public static Application getinstance(){
return application;
}
//Andriod设备上不会执行
public void onTerminate() {
super.onTerminate();
application = this;
Log.d("richu","onTerminate");
}
public HashMap<String,String> infoMap= new HashMap<>();
//配置改变时
public void onConfigurationChanged( {
Configuration newConfig)super.onConfigurationChanged(newConfig);
Log.d("richu","onConfigure");
}
}
application.infoMap.put("name","richu");
name = application.infoMap.get("name");
12.Room
implementation ("androidx.room:room-runtime:2.2.6")
implementation ("androidx.room:room-ktx:2.2.6")
testImplementation ("androidx.room:room-testing:2.2.6")
数据实体 Entry 表示应用的数据库中的表。数据实体用于更新表中的行所存储的数据以及创建新行供插入。
数据访问对象 (DAO) 提供应用在数据库中检索、更新、插入和删除数据所用的方法。
数据库类持有数据库,并且是应用数据库底层连接的主要访问点。数据库类为应用提供与该数据库关联的 DAO 的实例。
package com.richuff.application4.dao;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import com.richuff.application4.enity.Books;
import java.util.List;
public interface BookDao {
void insert(Books... books);
void delete(Books... books);
int update(Books... books);
List<Books> queryAll();
Books queryById(int id);
}
//entities表示有哪些表 version为版本号 exportSchema表示是否导出数据库的json串
public abstract class BookDatabase extends RoomDatabase {
}
在自定义的Appication创建BookDatabase实例
bookDatabase = Room.databaseBuilder(this,BookDatabase.class,"book").
//允许迁移数据库
addMigrations().
//允许主线程中操作数据库
allowMainThreadQueries().build();
13.ContentProvider
1.service端代码 编写
为App存储内部数据提供统一的外部接口,让不同的外部应用之间得以共享数据
<provider
android:name=".provider.UserinfoProvider"
android:authorities="com.richuff.server.provider.UserinfoProvider"
android:enabled="true"
android:exported="true" />
uri 通用资源标识符 代表数据操作的地址 每一个ContentProvider都有一个唯一的值。ContentProvider使用的Uri的语法结构如下
在 Android 中,
ContentProvider
使用的Uri
(统一资源标识符)有一个标准的语法结构。一般形式为content://authority/path
。conten://是一个固定的前缀,用于表明这是一个内容提供者(
ContentProvider
)相关的Uri
,所有通过ContentResolver
访问ContentProvider
的Uri
都以这个前缀开头。关键部分之一,它用于唯一标识一个ContentProvider。通常,authority是一个字符串,格式类似于一个包名或者反向域名(例如com.example.app.provider)。在AndroidManifest.xml文件中,当注册ContentProvider时,android:authorities属性的值就是这个authority。
<provider android:name=".MyContentProvider" android:authorities="com.example.myapp.provider" android:exported="true"/>
应用通过这个
authority
来定位要访问的ContentProvider
。不同的ContentProvider
应该有不同的authority
,以避免冲突。path
是可选的,它用于在一个
ContentProvider内部进一步划分资源或者数据集合。例如,一个
ContentProvider可能管理用户信息和订单信息,那么可以通过不同的
path来区分访问用户信息(如
content://com.example.myapp.provider/users)和访问订单信息(如
content://com.example.myapp.provider/orders)。
path
可以包含多层路径,例如content://com.example.myapp.provider/users/profile
,可以用于更精细地划分数据访问路径,这里可能表示访问用户信息中的用户资料部分。
public class UserinfoProvider extends ContentProvider {
private UserinfoDatabase userinfoDatabase;
public UserinfoProvider() {
}
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Not yet implemented");
}
public String getType(Uri uri) {
throw new UnsupportedOperationException("Not yet implemented");
}
//添加
public Uri insert(Uri uri, ContentValues values) {
if ("/user".equals(uri.getPath())) {
SQLiteDatabase db = userinfoDatabase.getWritableDatabase();
db.insert(UserinfoDatabase.TABLE_NAME, null, values);
}
return uri;
}
public boolean onCreate() {
userinfoDatabase = UserinfoDatabase.getInstance(getContext());
return true;
}
//查询
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = userinfoDatabase.getReadableDatabase();
return db.query(UserinfoDatabase.TABLE_NAME, projection, selection, selectionArgs, null, null, null);
}
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
throw new UnsupportedOperationException("Not yet implemented");
}
}
2.client端代码
通过
ContentResolver
访问数据
ContentValues values = new ContentValues();
values.put(UserinfoContent.PASSWORD,"test");
values.put(UserinfoContent.PHONE,"11111");
values.put(UserinfoContent.ISCHECKED,1);
getContentResolver().insert(UserinfoContent.CONTENT_URI,values);
使用
getContentResolver()
获取数据
public void onClick(View v) {
if (v.getId() == R.id.read){
//查询
Cursor cursor = getContentResolver().query(UserinfoContent.CONTENT_URI,null,null,null,null,null);
if (cursor != null){
while (cursor.moveToNext()){
//接收数据
User info = new User();
info.id = cursor.getInt(cursor.getColumnIndex(UserinfoContent._ID));
info.name = cursor.getString(cursor.getColumnIndex(UserinfoContent.NAME));
info.height = cursor.getLong(cursor.getColumnIndex(UserinfoContent.HEIGHT));
info.age = cursor.getInt(cursor.getColumnIndex(UserinfoContent.AGE));
info.weight = cursor.getFloat(cursor.getColumnIndex(UserinfoContent.WEIGHT));
Log.d("mytag", info.toString());
}
//关闭cursor
cursor.close();
}
}else if (v.getId() == R.id.write){
//保存
ContentValues values = new ContentValues();
values.put(UserinfoContent.NAME,name.getText().toString());
values.put(UserinfoContent.AGE,age.getText().toString());
values.put(UserinfoContent.HEIGHT,height.getText().toString());
values.put(UserinfoContent.WEIGHT,weight.getText().toString());
//插入数据
getContentResolver().insert(UserinfoContent.CONTENT_URI,values);
//弹窗提示
Toastshow.show(this,"保存成功");
}
}
在android11之后要提前声明
在AndroidManifest.xml
文件中声明需要访问的其他软件包
<queries>
<provider android:authorities="com.richuff.server.provider.UserinfoProvider"/>
</queries>
提供contentprovider的uri
public class UserinfoContent {
public static final String AUTHORITIES = "com.richuff.server.provider.UserinfoProvider"; //服务端的具体类名
//访问内容器的URI
public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITIES+"/user");
public static final String HEIGHT = "height";
public static final String NAME = "name";
public static final String WEIGHT = "weight";
public static final String AGE = "age";
}
3.进行数据的删除
在provider中修改delete函数
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
//删除多行
if (URI_MATCHER.match(uri) == USERS){
SQLiteDatabase db = UserinfoDatabase.mhelper.getWritableDatabase();
count = db.delete(UserinfoDatabase.TABLE_NAME,selection,selectionArgs);
db.close();
}
//删除单行
else if (URI_MATCHER.match(uri) == USER) {
String id = uri.getLastPathSegment();
SQLiteDatabase db = UserinfoDatabase.mhelper.getWritableDatabase();
count = db.delete(UserinfoDatabase.TABLE_NAME,"_id=?",new String[]{id});
db.close();
}
return count;
}
使用provider
//删除多行
int count = getContentResolver().delete(UserinfoContent.CONTENT_URI, "name=?", new String[]{"rc"});
if (count > 0){
Toastshow.show(this,"删除成功");
}
//删除单行
Uri uri = ContentUris.withAppendedId(UserinfoContent.CONTENT_URI,2); //在uri后面附加一个/2
int count = getContentResolver().delete(uri, "name=?", new String[]{"rc"});
if (count > 0){
Toastshow.show(this,"删除成功");
}
在provider中选择路径匹配
private static final UriMatcher URI_MATCHER= new UriMatcher(UriMatcher.NO_MATCH);
private static final int USERS = 1;
private static final int USER = 2;
static {
URI_MATCHER.addURI(UserinfoContent.AUTHORITIES,"/user",USERS);
URI_MATCHER.addURI(UserinfoContent.AUTHORITIES,"/user/#",USER);
}
14.运行时动态申请权限
提前声明需要的权限
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
1.检查App是否开启了某个权限
private static final String[] PERMISSION_CONTACTS = new String[]{
Manifest.permission.READ_CONTACTS,
Manifest.permission.WRITE_CONTACTS
};
private static final String[] PERMISSION_SMS = new String[]{
Manifest.permission.READ_SMS,
Manifest.permission.SEND_SMS
};
private static final int REQUEST_CODE_CONTACTS = 1;
private static final int REQUEST_CODE_SMS = 2;
2.请求系统弹窗,以便用户选择是否开启权限
public static boolean checkPermission(Activity act,String[] permissions,int requestCode){
int check = PackageManager.PERMISSION_GRANTED;
for (String permission:permissions){
check = ContextCompat.checkSelfPermission(act,permission);
if (check != PackageManager.PERMISSION_GRANTED){
break;
}
}
if (check != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(act, permissions, requestCode);
return false;
}
return true;
}
3.判断用户的权限选择结果
public static boolean checkGrant(int[] grantResults) {
if (grantResults != null) {
for (int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
return false;
}
跳转到setting页面
public void goToSetting(){
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package",getPackageName(),null));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
15.通讯录
1.添加联系人
private void addContact(ContentResolver contentResolver, Contact contact) {
ContentValues values = new ContentValues();
Uri uri = contentResolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
long rawContentID = ContentUris.parseId(uri);
ContentValues name = new ContentValues();
//关联联系人编号
name.put(ContactsContract.Data.RAW_CONTACT_ID,rawContentID);
//姓名的数据类型
name.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
//添加的类型 1表示家庭 2表示工作
name.put(ContactsContract.Data.DATA2,contact.name);
//联系人的姓名
contentResolver.insert(ContactsContract.Data.CONTENT_URI,name);
ContentValues phone = new ContentValues();
//关联联系人编号
phone.put(ContactsContract.Data.RAW_CONTACT_ID,rawContentID);
//姓名的数据类型
phone.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
//添加的类型 1表示家庭 2表示工作
phone.put(ContactsContract.Data.DATA1,contact.phone);
phone.put(ContactsContract.Data.DATA2,ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
//联系人的手机号
contentResolver.insert(ContactsContract.Data.CONTENT_URI,phone);
ContentValues email = new ContentValues();
//关联联系人编号
email.put(ContactsContract.Data.RAW_CONTACT_ID,rawContentID);
//姓名的数据类型
email.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
//添加的类型 1表示家庭 2表示工作
email.put(ContactsContract.Data.DATA1,contact.email);
email.put(ContactsContract.Data.DATA2,ContactsContract.CommonDataKinds.Email.TYPE_WORK);
//联系人的手机号
contentResolver.insert(ContactsContract.Data.CONTENT_URI,email);
}
2.批量添加联系人
public void addFullContacts(ContentResolver contentResolver, Contact contact){
ContentProviderOperation op_main = ContentProviderOperation.
newInsert(ContactsContract.Data.CONTENT_URI).withValue(ContactsContract.RawContacts.ACCOUNT_NAME,null).
build();
ContentProviderOperation op_name = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).
//将第0个操作的id,即raw_contracts的id作为data表中的raw_contract_id
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID,0).
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.Data.DATA2,contact.name)
.build();
ContentProviderOperation op_phone = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).
//将第0个操作的id,即raw_contracts的id作为data表中的raw_contract_id
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID,0).
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.Data.DATA1,contact.phone)
.withValue(ContactsContract.Data.DATA2,ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
.build();
ContentProviderOperation op_email = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).
//将第0个操作的id,即raw_contracts的id作为data表中的raw_contract_id
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID,0).
withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.Data.DATA1,contact.email)
.withValue(ContactsContract.Data.DATA2,ContactsContract.CommonDataKinds.Email.TYPE_WORK)
.build();
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
operations.add(op_main);
operations.add(op_name);
operations.add(op_phone);
operations.add(op_email);
try {
contentResolver.applyBatch(ContactsContract.AUTHORITY,operations);
} catch (OperationApplicationException e) {
throw new RuntimeException(e);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
3.查询联系人
public void readPhoneContacts(ContentResolver resolver){
Cursor cursor = resolver.query(ContactsContract.RawContacts.CONTENT_URI, new String[]{ContactsContract.RawContacts._ID}, null, null,null);
while (cursor.moveToNext()){
int rawContactId = cursor.getInt(0);
Uri uri = Uri.parse("content://com.android.contacts/contacts/" + rawContactId + "/dataa");
Cursor query = resolver.query(uri, new String[]{ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2}, null, null, null);
Contact contact = new Contact();
while (query.moveToNext()){
String data1 = query.getString(query.getColumnIndex(ContactsContract.Data.DATA1));
String mimeType = query.getString(query.getColumnIndex(ContactsContract.Data.MIMETYPE));
//查询姓名
if (mimeType.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)){
contact.name = data1;
}//查询电话号
else if (mimeType.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)){
contact.phone = data1;
}//查询邮箱
else if (mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)){
contact.email = data1;
}
}
query.close();
}
cursor.close();
}
16.短信
public class MainActivity4 extends AppCompatActivity {
public SmsGetObserver mObserver;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main4);
//给指定的uri注册内容监听器,一旦发生数据变化,就触发观察器的onChange方法
Uri uri = Uri.parse("content://sms");
mObserver = new SmsGetObserver(this);
//notifyForDescendants为false表示uri的精确匹配,为true表示可以匹配其派生路径
getContentResolver().registerContentObserver(uri,true,mObserver);
}
protected void onDestroy() {
super.onDestroy();
getContentResolver().unregisterContentObserver(mObserver);
}
private static class SmsGetObserver extends ContentObserver {
private final Context mContext;
public SmsGetObserver(Context context) {
super(new Handler(Looper.getMainLooper()));
this.mContext = context;
}
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
if (uri == null) {
return;
}
if (uri.toString().contains("content://sms/raw") || uri.toString().equals("content://sms")){
return;
}
Cursor cursor = mContext.getContentResolver().query(uri, new String[]{"address", "body", "date"}, null, null, "date DESC");
if (cursor.moveToNext()){
String sender = cursor.getString(cursor.getColumnIndex("address"));
String content = cursor.getString(cursor.getColumnIndex("body"));
Log.d("mytag", String.format("sender:%s,content:%s",sender,content));
}
cursor.close();
}
}
}
选择图片并且显示
public class MainActivity5 extends AppCompatActivity implements View.OnClickListener {
private ImageView view;
private ActivityResultLauncher<Intent> register;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main5);
view = findViewById(R.id.e_image);
view.setOnClickListener(this);
//跳转到系统相册,并且返回
register = registerForActivityResult(new StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
public void onActivityResult(ActivityResult o) {
if (o.getResultCode() == RESULT_OK){
Intent intent = o.getData();
//选择图片的路径
Uri uri = intent.getData();
if (uri != null){
view.setImageURI(uri);
Log.d("mytag", uri.toString());
}
}
}
});
}
public void onClick(View v) {
if (v.getId() == R.id.e_image){
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
register.launch(intent);
}
}
}
跳转到短信,并且发送短信
public class MainActivity5 extends AppCompatActivity implements View.OnClickListener {
private EditText title;
private EditText phone;
private ImageView view;
private EditText content;
private ActivityResultLauncher<Intent> register;
private Uri uri;
@Override
protected void onCreate(Bundle savedInstanceState) {
view = findViewById(R.id.e_image);
view.setOnClickListener(this);
findViewById(R.id.e_send).setOnClickListener(this);
phone = findViewById(R.id.e_phone);
title = findViewById(R.id.e_title);
content = findViewById(R.id.e_content);
@Override
public void onClick(View v) {
if (v.getId() == R.id.e_send) {
sendMsm(phone.getText().toString(),
title.getText().toString(),
content.getText().toString());
}
}
private void sendMsm(String phone, String title, String message) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//intent的接收者将被准许读取携带的uri数据
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra("address",phone);
intent.putExtra("title",title);
intent.putExtra("sms_body",message);
//图片 文件流
intent.putExtra(Intent.EXTRA_STREAM,uri);
//附件的类型
intent.setType("image/*");
//系统会弹出窗口让你选择
startActivity(intent);
}
}
通过MediaStore查询图片
PermissionUtil
public class PermissionUtil {
public static boolean checkPermission(Activity act,String[] permissions,int requestCode){
int check = PackageManager.PERMISSION_GRANTED;
for (String permission:permissions){
check = ContextCompat.checkSelfPermission(act,permission);
if (check != PackageManager.PERMISSION_GRANTED){
break;
}
}
if (check != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(act, permissions, requestCode);
return false;
}
return true;
}
public static boolean checkGrant(int[] grantResults) {
if (grantResults != null) {
for (int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
return false;
}
}
Activity
权限
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
public class MainActivity6 extends AppCompatActivity {
private static final String[] PERMISSION = new String[]{
Manifest.permission.READ_MEDIA_IMAGES
};
private static final int REQUEST_CODE_ALL = 1;
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_ALL && PermissionUtil.checkGrant(grantResults)){
LoadImageList();
ShowImageGrid();
}
}
private ArrayList<ImageInfo> mImageInfoList = new ArrayList<>();
private GridLayout gl;
@Override
protected void onCreate(Bundle savedInstanceState) {
gl = findViewById(R.id.e_grid);
//手动让MediaStore扫描入库
MediaScannerConnection.scanFile(this,new String[]{
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString()
},null,null);
//引发系统弹窗
if (PermissionUtil.checkPermission(this,PERMISSION,REQUEST_CODE_ALL)){
LoadImageList();
ShowImageGrid();
}
}
private void ShowImageGrid(){
gl.removeAllViews();
for (ImageInfo imageInfo:mImageInfoList){
ImageView iv = new ImageView(this);
Bitmap bitmap = BitmapFactory.decodeFile(imageInfo.getPath());
iv.setImageBitmap(bitmap);
iv.setScaleType(ImageView.ScaleType.FIT_CENTER);
int px = diptopx.dip(this,110);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(px,px);
iv.setLayoutParams(layoutParams);
int padding = diptopx.dip(this,5);
iv.setPadding(padding,padding,padding,padding);
iv.setOnClickListener(v->{
});
gl.addView(iv);
}
}
@SuppressLint("Range")
private void LoadImageList() {
String[] column = new String[]{
MediaStore.Images.Media._ID,
MediaStore.Images.Media.TITLE,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.DATA,
};
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
column,
"_size < 307200", null, "_size DESC"
);
int count = 0;
while (cursor.moveToNext() && count < 6){
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID));
String title = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.TITLE));
long size = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.SIZE));
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
ImageInfo imageInfo = new ImageInfo(id, title, size, path);
count++;
mImageInfoList.add(imageInfo);
}
}
}
17FileProvider
权限
<provider
android:authorities="@string/file_provider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/>
</provider>
xml/file_paths.xml
<paths>
<external-path
name="external_storage_pictures"
path="DCIM/Camera" />
<external-path
name="external_storage_pictures"
path="Pictures" />
</paths>
检测文件是否是指定fileProvider路径下的
public static boolean CheckFileUri(Context ctx,String path){
File file = new File(path);
if (!file.exists() || !file.isFile() || file.length() <= 0){
return false;
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
//通过FileProvider获得文件路径
FileProvider.getUriForFile(ctx,ctx.getString(R.string.file_provider),file);
}
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
18.安装应用
package com.richuff.client;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.richuff.client.utils.PermissionUtil;
import java.io.File;
public class MainActivity7 extends AppCompatActivity implements View.OnClickListener {
//兼容安卓11之前的版本
private static final String[] PERMISSION = new String[]{
Manifest.permission.READ_MEDIA_IMAGES
};
private static final int REQUEST_CODE_ALL = 1;
protected void onCreate(Bundle savedInstanceState) {
findViewById(R.id.down).setOnClickListener(this);
}
public void onClick(View v) {
if(v.getId() == R.id.down){
//版本兼容
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
CheckAndinstallApk();
}else{
if (PermissionUtil.checkPermission(this,PERMISSION,REQUEST_CODE_ALL)){
installApk();
}
}
}
}
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_ALL && PermissionUtil.checkGrant(grantResults)){
installApk();
}
}
private void CheckAndinstallApk() {
//检查是否有MANAGE_EXTERNAL_STORAGE权限,没有则跳转到设置页面
if (!Environment.isExternalStorageEmulated()){
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.fromParts("package",getPackageName(),null));
}else {
installApk();
}
}
public void installApk(){
String apkPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/.apk";
Log.d("mytag", "apkPath"+apkPath);
PackageManager pm = getPackageManager();
PackageInfo pi;
try {
pi = pm.getPackageInfo(apkPath, PackageManager.GET_ACTIVITIES);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
if (pi == null){
Toastshow.show(this,"文件已损坏");
}
Uri uri = Uri.parse(apkPath);
uri = FileProvider.getUriForFile(this,getString(R.string.file_provider),new File(apkPath));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//设置uri的类型为apk文件
intent.setDataAndType(uri,"application/vnd.android.package-archive");
//启动
startActivity(intent);
}
}
需要的权限
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
18.高级控件
1.Spinner
<Spinner
android:id="@+id/drop_down"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:spinnerMode="dropdown"/>
private Spinner sp;
private final static String[] startArray = {"水星","火星","金星","木星","木星"};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_test);
sp = findViewById(R.id.drop_down);
sp.setAdapter(new ArrayAdapter<String>(this,R.id.drop_down,startArray));
//默认选择第一项
sp.setSelection(0);
sp.setOnItemClickListener(this);
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toastshow.show(this,"选择"+startArray[position]);
}