安卓(Android)是一种基于Linux内核(不包含GNU组件)的自由及开放源代码的移动操作系统。主要应用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。Android操作系统最初由安迪·鲁宾开发,主要支持手机。2005年8月由Google收购注资。2007年11月,Google与84家硬件制造商、软件开发商及电信营运商组建开放手机联盟共同研发改良Android系统。随后Google以Apache开源许可证的授权方式,发布了Android的源代码。
1.Android基本 1.1.下载Android Studio和SDK Install Android Studio | Android Developers (google.cn)
1.2.观察APP运行日志 Log.e表示错误信息
Log.w表示警告信息
Log.i表示一般消息
Log.d表示调试信息
Log.v表示冗余信息
1.3.工程目录结构 1.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 用于指定项目中所有引入的模块。需要我们手动去修改这个文件的场景可能比较少
1.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 这个文件用于指定项目代码的混淆规则,当代码开发完成后打成安装包文件,如果不希望代码被别人破解
1.4.创建新的App页面 1.创建一个layout布局文件
xxx.xml
2.创建一个Activity Java类
使用startActivity切换页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class MainActivity extends AppCompatActivity { @Override 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() { @Override public void onClick (View v) { Intent intent = new Intent (); intent.setClass(MainActivity.this , MainActivity_2.class); startActivity(intent); } }); } }
2.Andriod控件 2.1.设置文本的内容 1.在XML文件中通过属性andriod:text
1 2 3 4 <resources > <string name ="mytext" translatable ="false" > HAHAHA</string > </resources >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?xml version="1.0" encoding="utf-8" ?> <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方法设置文本
1 2 3 4 <resources > <string name ="mytext" translatable ="false" > HAHAHA</string > </resources >
1 2 3 4 5 6 7 8 9 10 public class MainActivity_2 extends AppCompatActivity { @Override protected void onCreate (@Nullable Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.test); TextView tv = findViewById(R.id.tv); tv.setText(R.string.mytext); } }
2.2.设置文本的大小
px 像素
dpi 指屏幕每英寸有多少个像素点
dip 长度单位,同一个单位在不同设备上有不同的显示效果,简称dp,与设备无关只与屏幕的尺寸有关
sp 专门用来设置字体大小,在系统中可以设置调整字体大小
px = dpi*dip/160
相同尺寸的手机,即使分辨率不同,同dp的组件占用屏幕比例也相同
2.3.设置文本颜色
使用自带的颜色,在Color类里面选择
tv.setTextColor(Color.BLACK);
使用自定义的颜色类似于argb() 前两位是透明度 ff为不透明,不设置透明度就直接透明了
1 tv.setTextColor(0xff00ff00 );
在布局文件中设置 后两位是透明度ff为透明
八位编码#FFEEDDCC中,FF表示透明度,EE表示红色的浓度,DD表示 绿色的浓度,CC表示蓝色的浓度。透明度为FF表示完全不透明,为00表示完全透明。
1 2 3 4 5 6 7 <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" />
2.4.设置视图的宽高
视图宽高通过属性Android:layout_width表达,视图高度通过属性android:layout_height表达
match_parent 表示与上级视图保持一致
wrap_content 表示与内容自适应
以dp为单位的具体尺寸
1 2 3 4 5 6 7 8 9 <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" />
1 2 3 4 5 6 7 8 9 10 11 public class MainActivity_2 extends AppCompatActivity { @Override protected void onCreate (@Nullable 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
1 2 3 4 5 6 7 8 public class diptopx { public static int dip (Context context, float dip) { float scale = context.getResources().getDisplayMetrics().density; return (int )(dip * scale +0.5f ); } }
2.5.设置视图的间距 margin 当前视图和周围平级视图之间的距离
padding 当前视图与内部下级视图之间的距离
与前端的margin和padding类似有四个方向,layout_marginTop….
2.6.设置视图的对齐方式
layout_gravity 当前视图相对于上级视图的对齐方式
gravity下级视图相对于当前视图的对齐方式
left right top bottom
用|连接表示合并方向left|top 为左上方
3.布局 3.1LinearLayout线性布局
orientation
horizontal 内部视图在水平方向从左到右排列
vertical 内部视图在水平方向从上到下排列
如果不指定orientation属性,则LinearLayout默认水平方向排列
线性布局的权重概念,值得是线性布局的下级视图各自拥有多大比例的宽高
权重属性名叫layout_weight.但该属性不在LinearLayout节点设置,而在线性布局的直接下级视图设置,表示该下级视图占据的宽高比例。
layout_width=0 layout_weight表示水平方向的宽高比例
layout_height=0 layout_weight表示垂直方向的宽高比例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <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 >
3.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
控件的基线与指定控件的基线对齐。
3.3GridLayout网格布局
支持多行多列的布局
默认从左往右从上到下
columnCount属性,指定了网格的列数
rowCount属性,指定了网格的行数
设置视图的对齐方式
采用layout_gravity属性,它指定了当前视图相对于上级视图的对齐方式
采用gravity属性,它指定了下级视图相对于当前视图的对齐方式
layout_gravity和gravity取值为left,top,right,bottom ,center
left|top为左上
1 2 3 4 5 6 <GridLayout android:layout_width ="match_parent" android:layout_height ="match_parent" > <TextView android:layout_columnWeight ="1" android:layout_rowWeight ="1" />
设置权重layout_columnWeight,layout_rowWeight
ScrollView,它是垂直方向的滚动视图;垂直方向滚动时,layout_width属性值设置为match_parent,layout_height属性值设置为wrap_content
HorizontalScrollView,它是水平方向的滚动视图水平方向滚动时,layout_width属性值设置为wrap_content,layout_height属性设置为match_parent
由TextView派生
相对于TextView新增的属性
android:textAllCaps=”true”
android:onClick=”doclick”
绑定事件(不常用)
3.6点击事件
setOnClickListener
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class MainActivity_2 extends AppCompatActivity { @Override protected void onCreate (@Nullable 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; } @Override public void onClick (View v) { String s = String.format("%s点击%s" , SimpleUtil.dateformat(),((Button)v)); tv.setText(s); } }
创建公用OnClickListener
1 2 3 4 5 6 7 8 public class MainActivity_2 extends AppCompatActivity implements View .OnClickListener{ @override public void Onclick (View v) { if (v.getId()==R.id.bt){ } } }
3.7长按点击事件
true则为开启事件冒泡,false则不开启
1 2 3 4 5 bt.setOnLongClickListener(v->{ String s = String.format("%s点击%s" , SimpleUtil.dateformat(),((Button)v)); tv.setText(s); return true ; });
3.8禁用恢复按钮
是否允许点击enable
1 2 bt1.setEnabled(true ); bt1.setEnabled(false );
true启用,false禁用
3.9ImageView
图像视图ImageView
图像视图展示的图片通常位于res/drawable***目录,设置图像视图的显示图片有两种方式:
在XML文件中,通过属性android:src设置图片资源,属性值格式形如“@drawable/不含扩展名的图片名称”。
在Java代码中,调用setlmageResource方法设置图片资源,方法参数格式形如“R.drawable.不含扩展名的图片名称”。
1 2 3 4 5 <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能完全显示该图片。(用于缩小图片)
1 2 3 4 5 ImageView imageView = findViewById(R.id.image);imageView.setImageResource(R.drawable.test); imageView.setScaleType(ImageView.ScaleType.FIT_XY);
同时显示文本和图像
通过Button上的drawable属性设置周围文本的图标
drawableTop:指定文字上方的图片
drawableBottom:指定文字下方的图片
drawableLeft:指定文字左方的图片
drawableRight:指定文字右方的图片
drawablePadding:指定图片与文字的间距
3.11计算器项目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 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 = "" ; @Override 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 ); } @Override 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; } 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 ; } }
4.Activity 4.1启停activity
启动新Activity
startActivity(new Intent(原页面.this,目标页面.class))
返回
finnish()//结束当前的活动页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class MainActivity extends AppCompatActivity implements View .OnClickListener { @SuppressLint("MissingInflatedId") @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn).setOnClickListener(this ); } @Override public void onClick (View v) { startActivity(new Intent (this ,MainActivity2.class)); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MainActivity2 extends AppCompatActivity implements View .OnClickListener { @SuppressLint("MissingInflatedId") @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main2); findViewById(R.id.btn1).setOnClickListener(this ); } @Override public void onClick (View v) { if (v.getId() == R.id.btn1){ finish(); } } }
4.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:重用已有的活动实例
4.3Activity启动模式 1.在配置文件中设置启动模式 1 2 3 4 <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:跳转到新页面时,栈中的原有实例都被清空
1 2 3 4 5 public void onClick (View v) { Intent intent = new Intent (this ,MainActivity2.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); }
登录后不再返回登录界面
1 2 3 Intent intent = new Intent (this ,MainActivity2.class);intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);
4.4传递数据 4.4.1显示Intent和隐式Intent
Intent是各个组件之间信息沟通的桥梁,用于各组件的通信
显示Intent,直接指定来源活动和目标活动,属于精确匹配
在Intent的构造函数中指定
调用意图对象的SetClass方法指定
调用意图对象的SetComponent方法指定
1 2 3 4 5 6 7 8 Intent intent = new Intent (this ,MainActivity2.class);Intent intent = new Intent ();intent.setClass(this , MainActivity2.class); ComponentName componentName = new ComponentName (this , MainActivity2.class);intent.setComponent(componentName);
隐式Intent,没有明确指定要跳转的目标活动,只要给出一个动作字符串让系统自动匹配,属于模糊匹配
1 2 3 4 5 6 7 String phoneNumber = "1234" ;Intent intent = new Intent ();intent.setAction(Intent.ACTION_DIAL); Uri uri = Uri.parse("tel:" +phoneNumber);intent.setData(uri); startActivity(intent);
启动自定义的程序
1 2 3 4 Intent intent = new Intent ();intent.setAction("android.intent.action.RICHU" ); intent.addCategory(Intent.CATEGORY_DEFAULT); startActivity(intent);
在要启动的程序中配置
1 2 3 4 5 <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
接听电话
4.4.2向下一个Activity发送数据
使用Bundle对象存储待传递的数据信息
发送
1 2 3 4 5 6 7 8 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);
接收
1 2 3 4 5 6 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);
4.4.3向上一个Activity返回数据
1.上一个页面打包好请求数据后,调用StartActivityForResult方法执行跳转动作
2.下一个页面接收并解析请求数据,进行相应处理
3.下一个页面返回上一个页面时,打包应答数据并调用SetResult方法返回数据包裹
4.上一个页面重写方法OnActivityResult,解析获得下一个页面的返回数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class MainActivity3 extends AppCompatActivity implements View .OnClickListener { private String myrequest = "this is a test" ; private String getrequest; private ActivityResultLauncher<Intent> register; @SuppressLint("MissingInflatedId") @Override 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); } } }); } @Override 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); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class MainActivity4 extends AppCompatActivity implements View .OnClickListener { @SuppressLint("MissingInflatedId") @Override 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 ); } @Override 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(); } }
4.4.4利用资源配置文件配置字符串 1 String test = getString(R.string.test);
4.4.5利用元数据传递配置信息 1 <meta-data android:name ="test" android:value ="test" > </meta-data >
5.Drawable
drawable-ldpi存放低分辨率的图片,基本没有这样的手机了
drawable-mdpi存放中等分辨率的图片,使用较少
drawable-hdpi存放高分辨率的图片 4-5英寸
drawable-xhdpi存放高分辨率的图片 5-5.5英寸
drawable-xxhdpi存放高分辨率的图片 6-6.5英寸
drawable-xxxhdpi存放高分辨率的图片 >7英寸
5.1形状图形
Shape图形又称形状图形,它用来描述常见的几何形状,包括矩形,圆角矩形,圆形,椭圆等等
rectangle矩形
oval椭圆
line直线
ring圆环
1 2 3 4 5 6 <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 >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class MainActivity6 extends AppCompatActivity implements View .OnClickListener { private View v_content; @Override 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); } @Override 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)
stroke(描边)
color颜色
dashGap像素;类型
dashWidth虚线的宽度
width厚度
dashWidth和dashGap为0为实线
solid填充
color:颜色
padding
上下左右
gradient渐变
angle 整形渐变的起始角度
type 渐变的类型
类型
说明
linear
线性渐变
radial
放射渐变
sweep
滚动渐变
5.2九宫格图片
test.9.png
上下左右边缘线
黑线区域以内的图像会拉伸,黑线以外的图像保持原状
内部文字只能放在黑线区域内
5.3状态列表图形 1 2 3 4 5 <?xml version="1.0" encoding="utf-8" ?> <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
是否选中
各组件通用
5.4复选框CheckBOx 1 2 3 4 <CheckBox android:layout_width ="match_parent" android:layout_height ="wrap_content" android:text ="系统checkbox" />
checked属性
设置是否选择,可以设置默认选择
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MainActivity7 extends AppCompatActivity implements CompoundButton .OnCheckedChangeListener { @SuppressLint("MissingInflatedId") @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main7); CheckBox cb = findViewById(R.id.select); cb.setOnCheckedChangeListener(this ); } @Override 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判断按钮是否勾选
5.5Switch Switch是开关按钮,它在选中和取消选中时可展现的界面元素比复选框丰富
textOn:打开时的文本
textOff:设置左侧关闭的文本
track:设置开关轨道的背景
thumb:设置开关表示的图标
1 2 3 4 5 6 7 <Switch android:id ="@+id/select" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:layout_gravity ="start|center" android:text ="switch" />
1 2 3 4 5 6 7 8 9 10 11 <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" />
5.6单选按钮
一组中只能选择一个
RadioButton,放入一个RadioGroup组内部
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <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 >
监听单选按钮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class MainActivity extends AppCompatActivity implements RadioGroup .OnCheckedChangeListener { private TextView tv; @Override 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 ); } @Override public void onCheckedChanged (RadioGroup group, int checkedId) { if (checkedId == R.id.male){ tv.setText("male" ); }else if (checkedId == R.id.female){ tv.setText("female" ); } } }
5.7文本输入框 1 2 3 4 <EditText android:layout_width ="match_parent" android:layout_height ="wrap_content" android:text ="输入登录信息" />
去掉默认边框
1 2 3 4 5 6 7 <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
事件格式
5.8焦点变更监听器
Toast.makeText(this,”请输入8位密码”,Toast.LENGTH_SHORT).show();
//弹出提示
LENGTH_SHORT
LENGTH_LONG 持续时间更长
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class MainActivity2 extends AppCompatActivity implements View .OnFocusChangeListener { private EditText et_message; @Override 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 ); } @Override 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(); } } } }
5.9文本变化监听器
调用编辑框对象的addTextChangelistener方法即可注册文本监听器
文本监听器的接口名位TextWatcher,该接口提供了3个监控方法
beforeTextChanged
OnTextChanged
afterTextChanged
隐藏输入框
1 2 3 4 5 6 7 8 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 ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class MainActivity2 extends AppCompatActivity { private EditText et_password; @Override 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; } @Override public void beforeTextChanged (CharSequence s, int start, int count, int after) { } @Override public void onTextChanged (CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged (Editable s) { String test = s.toString(); if (test.length() == 11 && maxLength==11 ){ hidenView.HidenInput(MainActivity2.this ,et_password); } } } }
5.10提醒对话框
AlertDialog
借助建造器AlertDialog.Builder才能完成设置
调用建造器的create方法生产对话框实例,再调用对话框实例的show方法,在页面提示对话框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class MainActivity3 extends AppCompatActivity implements View .OnClickListener { private TextView tv_dialog; @Override 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); } @Override 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设置中性按钮
5.11日期选择器 1 2 3 4 5 6 <DatePicker android:id ="@+id/dp_date" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:calendarViewShown ="false" android:datePickerMode ="spinner" />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override 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); } @Override 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实例获取时间
1 2 3 4 Calendar calendar = Calendar.getInstance();calendar.get(Calendar.YEAR); calendar.get(Calendar.MONTH); calendar.get(Calendar.DAY_OF_MONTH);
DatePickDiolog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Override public void onClick (View v) { if (v.getId() == R.id.dp_btn){ DatePickerDialog dialog = new DatePickerDialog (this ,this ,2090 ,5 ,11 ); dialog.show(); } } @Override 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); }
5.12时间选择器
TimePickDialog
TimePicker类似DatePicker,可以选择具体的小时
TimePickDiolog的用法类似DatePickDiolog
1 2 3 TimePickDialog dialog = new TimePickDialog (this ,[android.R.style.Theme.Holo_Light_dialog ],this ,calendar.get(Calendar.HOUR_OF_DAY),calender.get(Calendar.MINUTE),true )
5.13小案例
页面布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 <?xml version="1.0" encoding="utf-8" ?> <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 >
代码逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 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; @SuppressLint("MissingInflatedId") @Override 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>() { @Override public void onActivityResult (ActivityResult o) { Intent intent = o.getData(); if (intent != null && o.getResultCode()== Activity.RESULT_OK){ String newpassword = intent.getStringExtra("password" ); clearPassword = newpassword; } } }); } @Override 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); } } @Override 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; } @Override public void beforeTextChanged (CharSequence s, int start, int count, int after) { } @Override public void onTextChanged (CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged (Editable s) { if (s.toString().length() == maxlength ){ hidenView.HidenInput(MainActivity5.this ,et); } } } }
找回密码布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 <?xml version="1.0" encoding="utf-8" ?> <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 >
找回密码逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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; @SuppressLint("MissingInflatedId") @Override 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); } @Override 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(); } } }
6.SharePreferences
SharePreferences是Android的一个轻量级存储工具,采用键值对的方式
/data/data/应用包名/shared_prefs/文件名.xml
App个性化配置信息,用户使用App的行为信息、临时需要保存的片段信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 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; @Override 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); } @Override 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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)); } }
7.SQLLite 7.1.管理类 openDatabase:打开指定路径的数据库
isopen:判断数据库是否打开
close:关闭数据库
getVersion:获取数据库的般般号
setVersion:设置数据库的版本号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class MainActivity8 extends AppCompatActivity implements View .OnClickListener { private String mDatabase; private TextView tv_database; @Override 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); } @Override 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 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 ; } } @Override 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); } @Override public void onUpgrade (SQLiteDatabase db, int oldVersion, int newVersion) { } }
增删改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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}); }
7.2.事务类
beginTransaction:开始事务
setTransactionSuccessful:设置事务成功的标志
endTransaction:结束事务
7.3.数据库版本更新 1 2 3 public void onUpgrade (SQLiteDatabase db, int oldVersion, int newVersion) {}
7.4.外部存储和内部存储,外部SD卡
读写文件操作类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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(); } }
获取外部存储的私有空间
1 String path = directory+ File.separatorChar+fileName;
获取外部存储公共空间
1 2 directory=Environment.getExternalStorageState();
内部存储私有空间
1 2 directory = getFilesDir().toString();
7.5.在存储卡读写图片文件 Bitmap
BitmaoFactory读写各种来源的图片
decodeResource 从资源文件中读取图片信息
decodeFile 指定路径的图片获取到bitmap对象
decodeStream从输入流获取位图数据
1 2 3 4 5 6 7 8 9 10 11 12 @Override 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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; }
8.Application 8.1Application
Application是Android的一大组件,在App运行过程中有且仅有一个Application对象贯穿整个生命周期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Application extends android .app.Application { @Override public void onCreate () { super .onCreate(); Log.d("richu" ,"onCreate" ); } @Override public void onTerminate () { super .onTerminate(); Log.d("richu" ,"onTerminate" ); } @Override public void onConfigurationChanged (@NonNull Configuration newConfig) { super .onConfigurationChanged(newConfig); Log.d("richu" ,"onConfigure" ); } }
8.2全局变量
适用:会频繁读取的信息,用户名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class Application extends android .app.Application { private static Application application; @Override public void onCreate () { super .onCreate(); } public static Application getinstance () { return application; } @Override public void onTerminate () { super .onTerminate(); application = this ; Log.d("richu" ,"onTerminate" ); } public HashMap<String,String> infoMap= new HashMap <>(); @Override public void onConfigurationChanged (@NonNull Configuration newConfig) { super .onConfigurationChanged(newConfig); Log.d("richu" ,"onConfigure" ); } }
1 2 application.infoMap.put("name" ,"richu" ); name = application.infoMap.get("name" );
9.Room 1 2 3 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 的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Dao public interface BookDao { @Insert() void insert (Books... books) ; @Delete void delete (Books... books) ; @Update int update (Books... books) ; @Query("select * from Books") List<Books> queryAll () ; @Query("select * from Books where id= :id") Books queryById (int id) ; }
1 2 3 4 5 @Database(entities = {Books.class},version = 1,exportSchema = false) public abstract class BookDatabase extends RoomDatabase { }
在自定义的Appication创建BookDatabase实例
1 2 3 4 5 bookDatabase = Room.databaseBuilder(this ,BookDatabase.class,"book" ). addMigrations(). allowMainThreadQueries().build();
10.ContentProvider 10.1.service端代码 编写
为App存储内部数据提供统一的外部接口,让不同的外部应用之间得以共享数据
1 2 3 4 5 <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。
1 2 3 4 <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
,可以用于更精细地划分数据访问路径,这里可能表示访问用户信息中的用户资料部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class UserinfoProvider extends ContentProvider { private UserinfoDatabase userinfoDatabase; public UserinfoProvider () { } @Override public int delete (Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException ("Not yet implemented" ); } @Override public String getType (Uri uri) { throw new UnsupportedOperationException ("Not yet implemented" ); } @Override 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; } @Override public boolean onCreate () { userinfoDatabase = UserinfoDatabase.getInstance(getContext()); return true ; } @Override 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 ); } @Override public int update (Uri uri, ContentValues values, String selection, String[] selectionArgs) { throw new UnsupportedOperationException ("Not yet implemented" ); } }
10.2.client端代码
通过ContentResolver
访问数据
1 2 3 4 5 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()
获取数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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.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
文件中声明需要访问的其他软件包
1 2 3 <queries > <provider android:authorities ="com.richuff.server.provider.UserinfoProvider" /> </queries >
提供contentprovider的uri
1 2 3 4 5 6 7 8 9 10 11 public class UserinfoContent { public static final String AUTHORITIES = "com.richuff.server.provider.UserinfoProvider" ; 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" ; }
10.3.进行数据的删除
在provider中修改delete函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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
1 2 3 4 5 6 7 8 9 10 11 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 ); int count = getContentResolver().delete(uri, "name=?" , new String []{"rc" });if (count > 0 ){ Toastshow.show(this ,"删除成功" ); }
在provider中选择路径匹配
1 2 3 4 5 6 7 8 9 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); }
11.运行时动态申请权限
提前声明需要的权限
1 2 3 4 <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是否开启了某个权限
1 2 3 4 5 6 7 8 9 10 11 12 13 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.请求系统弹窗,以便用户选择是否开启权限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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.判断用户的权限选择结果
1 2 3 4 5 6 7 8 9 10 11 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页面
1 2 3 4 5 6 7 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); }
12.通讯录 12.1.添加联系人 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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); 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); 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); email.put(ContactsContract.Data.DATA1,contact.email); email.put(ContactsContract.Data.DATA2,ContactsContract.CommonDataKinds.Email.TYPE_WORK); contentResolver.insert(ContactsContract.Data.CONTENT_URI,email); }
12.2.批量添加联系人 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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). 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). 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). 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); } }
12.3.查询联系人 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @SuppressLint("Range") 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(); }
13.短信 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public class MainActivity4 extends AppCompatActivity { public SmsGetObserver mObserver; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); EdgeToEdge.enable(this ); setContentView(R.layout.activity_main4); Uri uri = Uri.parse("content://sms" ); mObserver = new SmsGetObserver (this ); getContentResolver().registerContentObserver(uri,true ,mObserver); } @Override 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; } @SuppressLint("Range") @Override public void onChange (boolean selfChange, @Nullable 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(); } } }
选择图片并且显示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class MainActivity5 extends AppCompatActivity implements View .OnClickListener { private ImageView view; private ActivityResultLauncher<Intent> register; @Override 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>() { @Override 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()); } } } }); } @Override 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); } } }
跳转到短信,并且发送短信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 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.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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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
权限
1 <uses-permission android:name ="android.permission.READ_MEDIA_IMAGES" />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 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); 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); } } }
14FileProvider
权限
1 2 3 4 5 6 7 <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
1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="utf-8" ?> <paths > <external-path name ="external_storage_pictures" path ="DCIM/Camera" /> <external-path name ="external_storage_pictures" path ="Pictures" /> </paths >
检测文件是否是指定fileProvider路径下的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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.getUriForFile(ctx,ctx.getString(R.string.file_provider),file); } }catch (Exception e){ e.printStackTrace(); return false ; } return true ; }
15.安装应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 public class MainActivity7 extends AppCompatActivity implements View .OnClickListener { private static final String[] PERMISSION = new String []{ Manifest.permission.READ_MEDIA_IMAGES }; private static final int REQUEST_CODE_ALL = 1 ; @Override protected void onCreate (Bundle savedInstanceState) { findViewById(R.id.down).setOnClickListener(this ); } @Override 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(); } } } } @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)){ installApk(); } } private void CheckAndinstallApk () { 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); intent.setDataAndType(uri,"application/vnd.android.package-archive" ); startActivity(intent); } }
需要的权限
1 2 <uses-permission android:name ="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name ="android.permission.REQUEST_INSTALL_PACKAGES" />
16.高级控件 16.1.Spinner 1 2 3 4 5 <Spinner android:id ="@+id/drop_down" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:spinnerMode ="dropdown" />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private Spinner sp;private final static String[] startArray = {"水星" ,"火星" ,"金星" ,"木星" ,"木星" };@SuppressLint("ResourceType") @Override 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 ); } @Override public void onItemClick (AdapterView<?> parent, View view, int position, long id) { Toastshow.show(this ,"选择" +startArray[position]); }
16.2.SimpleAdapter
构建每一个条目的基本构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?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:orientation ="horizontal" > <ImageView android:src ="@drawable/na" android:id ="@+id/tv_im" android:layout_width ="0dp" android:layout_height ="50dp" android:scaleType ="centerCrop" android:layout_weight ="1" /> <TextView android:id ="@+id/tv_name" android:layout_width ="0dp" android:layout_height ="match_parent" android:text ="图1" android:textSize ="10sp" android:layout_weight ="3" android:gravity ="center" android:textColor ="#ff0000" /> </LinearLayout >
基本代码逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private int [] iconArray = {R.drawable.na,R.drawable.na,R.drawable.na,R.drawable.na,R.drawable.na};private String[] nameArray = {"图一" ,"图二" ,"图三" ,"图四" ,"图五" };@Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); EdgeToEdge.enable(this ); setContentView(R.layout.activity_main2); List<Map<String, Object>> list = new ArrayList <>(); for (int i=0 ;i<iconArray.length;i++){ Map<String,Object> item = new HashMap <>(); item.put("name" ,nameArray[i]); item.put("icon" ,iconArray[i]); list.add(item); } SimpleAdapter startAdapter = new SimpleAdapter (this ,list,R.layout.item_simple, new String []{"name" ,"icon" },new int []{R.id.tv_name,R.id.tv_im}); Spinner sp_icon = findViewById(R.id.tv_sp); sp_icon.setAdapter(startAdapter); sp_icon.setSelection(0 ); sp_icon.setOnItemSelectedListener(this ); }
16.3BaseAdapter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class MyAdapter extends BaseAdapter { private int [] imageArr; private String[] nameArr; private Context mContext; public MyAdapter (Context context,int [] imageArr,String[] nameArr) { this .mContext = context; this .imageArr = imageArr; this .nameArr = nameArr; } @Override public int getCount () { return imageArr.length; } @Override public Object getItem (int position) { return imageArr[position]; } @Override public long getItemId (int position) { return position; } @Override public View getView (int position, View convertView, ViewGroup parent) { View view = LayoutInflater.from(mContext).inflate(R.layout.item_list, null ); ImageView image = view.findViewById(R.id.ts_im); TextView maintext = view.findViewById(R.id.ts_name); TextView childtext = view.findViewById(R.id.ts_chd); String name = nameArr[position]; int imgsrc = imageArr[position]; image.setImageResource(imgsrc); maintext.setText(name); childtext.setText(name+"id" ); return view; } }
使用
1 2 3 4 5 6 lv = findViewById(R.id.ts_lv); MyAdapter startAdapter = new MyAdapter (this ,iconArray,nameArray);lv.setAdapter(startAdapter); lv.setSelection(0 );
进行优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public class MyAdapter extends BaseAdapter { private int [] imageArr; private String[] nameArr; private Context mContext; public MyAdapter (Context context,int [] imageArr,String[] nameArr) { this .mContext = context; this .imageArr = imageArr; this .nameArr = nameArr; } @Override public int getCount () { return imageArr.length; } @Override public Object getItem (int position) { return imageArr[position]; } @Override public long getItemId (int position) { return position; } @Override public View getView (int position, View convertView, ViewGroup parent) { ViewHolder holder = new ViewHolder (); if (convertView == null ) { convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list, null ); holder.iv_icon = convertView.findViewById(R.id.ts_im); holder.tv_name = convertView.findViewById(R.id.ts_name); holder.tv_child = convertView.findViewById(R.id.ts_chd); convertView.setTag(holder); }else { holder = (ViewHolder)convertView.getTag(); } String name = nameArr[position]; int imgsrc = imageArr[position]; holder.iv_icon.setImageResource(imgsrc); holder.tv_name.setText(name); holder.tv_child.setText(name+"id" ); return convertView; } public final class ViewHolder { public ImageView iv_icon; public TextView tv_name; public TextView tv_child; } }
16.4.ListView
基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private int [] iconArray = {R.drawable.na,R.drawable.na,R.drawable.na,R.drawable.na,R.drawable.na};private String[] nameArray = {"图一" ,"图二" ,"图三" ,"图四" ,"图五" };@Override protected void onCreate (Bundle savedInstanceState) { ListView listView = findViewById(R.id.tv_lv); List<Map<String, Object>> list = new ArrayList <>(); for (int i=0 ;i<iconArray.length;i++){ Map<String,Object> item = new HashMap <>(); item.put("name" ,nameArray[i]); item.put("icon" ,iconArray[i]); list.add(item); } SimpleAdapter startAdapter = new SimpleAdapter (this ,list,R.layout.item_simple, new String []{"name" ,"icon" },new int []{R.id.tv_name,R.id.tv_im}); listView.setAdapter(startAdapter); listView.setOnItemClickListener(this ); } @Override public void onItemClick (AdapterView<?> parent, View view, int position, long id) { Toastshow.show(this ,"点击了" +nameArray[position]); }
更改样式
1 2 3 4 5 6 7 8 <ListView android:id ="@+id/tv_lv" android:layout_height ="wrap_content" android:layout_width ="match_parent" android:divider ="@null" android:dividerHeight ="0dp" android:listSelector ="@color/transparent" />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Override public void onClick (View v) { if (v.getId() == R.id.cb_bg){ if (ck_bg.isChecked()){ listView.setSelector(R.drawable.listselector); }else { listView.setSelector(R.drawable.ic_launcher_foreground); } }else if (v.getId() == R.id.cb_fg){ if (ck_fg.isChecked()){ Drawable drawable = getResources().getDrawable(R.color.black,getTheme()); listView.setDivider(drawable); listView.setDividerHeight(diptopx.dip(this ,1 )); }else { listView.setDivider(null ); listView.setDividerHeight(0 ); } } }
解决条目时间冲突问题
1 2 3 4 5 6 <LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:orientation ="horizontal" android:descendantFocusability ="blocksDescendants" >
使用代码控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 public class MyAdapter extends BaseAdapter { private int [] imageArr; private String[] nameArr; private Context mContext; public MyAdapter (Context context, int [] imageArr, String[] nameArr) { this .mContext = context; this .imageArr = imageArr; this .nameArr = nameArr; } @Override public int getCount () { return imageArr.length; } @Override public Object getItem (int position) { return imageArr[position]; } @Override public long getItemId (int position) { return position; } @Override public View getView (int position, View convertView, ViewGroup parent) { ViewHolder holder = new ViewHolder (); if (convertView == null ) { convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_withbutton, null ); holder.tv_ll = convertView.findViewById(R.id.ll_ts); holder.iv_icon = convertView.findViewById(R.id.ts_im); holder.tv_name = convertView.findViewById(R.id.ts_name); holder.tv_child = convertView.findViewById(R.id.ts_chd); holder.ts_bt = convertView.findViewById(R.id.ts_bt); convertView.setTag(holder); }else { holder = (ViewHolder)convertView.getTag(); } String name = nameArr[position]; int imgsrc = imageArr[position]; holder.iv_icon.setImageResource(imgsrc); holder.tv_ll.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); holder.tv_name.setText(name); holder.tv_child.setText(name+"id" ); holder.ts_bt.setOnClickListener((v)->{ Toast.makeText(mContext,name,Toast.LENGTH_SHORT).show(); }); return convertView; } public final class ViewHolder { public LinearLayout tv_ll; public ImageView iv_icon; public TextView tv_name; public TextView tv_child; public Button ts_bt; } }
16.5GridView
更改样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <GridView android:id ="@+id/gv" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:numColumns ="2" /> <GridView android:id ="@+id/gv" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:numColumns ="2" android:verticalSpacing ="3dp" //垂直间距为3dp android:background ="#ff00ff" android:columnWidth ="100dp" //列宽为100dp android:stretchMode ="none" //拉伸模式 />
使用代码控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public class MyPAdapter extends BaseAdapter { private int [] imageArr; private String[] nameArr; private Context mContext; public MyPAdapter (Context context, int [] imageArr, String[] nameArr) { this .mContext = context; this .imageArr = imageArr; this .nameArr = nameArr; } @Override public int getCount () { return imageArr.length; } @Override public Object getItem (int position) { return imageArr[position]; } @Override public long getItemId (int position) { return position; } @Override public View getView (int position, View convertView, ViewGroup parent) { ViewHolder holder = new ViewHolder (); if (convertView == null ) { convertView = LayoutInflater.from(mContext).inflate(R.layout.grid_list, null ); holder.iv_icon = convertView.findViewById(R.id.ts_im); holder.tv_name = convertView.findViewById(R.id.ts_name); holder.tv_child = convertView.findViewById(R.id.ts_chd); convertView.setTag(holder); }else { holder = (ViewHolder)convertView.getTag(); } String name = nameArr[position]; int imgsrc = imageArr[position]; holder.iv_icon.setImageResource(imgsrc); holder.tv_name.setText(name); holder.tv_child.setText(name+"id" ); return convertView; } public final class ViewHolder { public ImageView iv_icon; public TextView tv_name; public TextView tv_child; } }
实现引导页 :很多应用在首次启动时会展示一系列介绍性或欢迎界面,ViewPager
可以实现连续的翻页效果,让用户轻松浏览并快速进入主界面。
图片轮播或浏览 :在需要展示多张图片的场景下,如产品详情页或照片查看器,ViewPager
可以实现图片的左右滑动切换,提供良好的用户体验。
多页面内容展示 :用于展示多个页面的内容,比如在复杂的设置选项中,将不同设置类别或子菜单组织为可滑动的列表,每个页面对应一个设置类别。
结合 TabLayout 使用 :与 TabLayout
结合是 ViewPager
的经典应用场景,通过顶部标签栏与底部内容区域联动,实现类似浏览器标签页的功能,每个标签对应一个 ViewPager
页面,便于用户在多个内容片段之间快速切换。
屏幕滑动导航 :在一些新闻类、博客阅读类应用中,ViewPager
可以实现不同文章之间的平滑过渡和预加载,提供沉浸式阅读体验。
1 2 3 4 <androidx.viewpager.widget.ViewPager android:id ="@+id/vp" android:layout_width ="match_parent" android:layout_height ="match_parent" />
1. 创建Fragment 创建多个Fragment,每个Fragment代表一个页面。例如:
1 2 3 4 5 6 public class FirstFragment extends Fragment { @Override public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_first, container, false ); } }
2. 创建适配器 创建一个继承自FragmentPagerAdapter
或FragmentStatePagerAdapter
的适配器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class MyPagerAdapter extends FragmentPagerAdapter { private final List<Fragment> fragmentList = new ArrayList <>(); private final List<String> titleList = new ArrayList <>(); public MyPagerAdapter (FragmentManager fm) { super (fm); } public void addFragment (Fragment fragment, String title) { fragmentList.add(fragment); titleList.add(title); } @Override public Fragment getItem (int position) { return fragmentList.get(position); } @Override public int getCount () { return fragmentList.size(); } @Override public CharSequence getPageTitle (int position) { return titleList.get(position); } }
在Activity的布局文件中添加ViewPager和PagerTitleStrip:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <androidx.viewpager.widget.ViewPager android:id ="@+id/viewPager" android:layout_width ="match_parent" android:layout_height ="match_parent" > <androidx.viewpager.widget.PagerTitleStrip android:id ="@+id/pagerTitleStrip" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:layout_gravity ="top" android:paddingTop ="20dp" android:paddingBottom ="20dp" android:textSize ="18sp" /> </androidx.viewpager.widget.ViewPager >
在Activity中初始化ViewPager和适配器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class MainActivity extends FragmentActivity { private ViewPager viewPager; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewPager = findViewById(R.id.viewPager); MyPagerAdapter adapter = new MyPagerAdapter (getSupportFragmentManager()); adapter.addFragment(new FirstFragment (), "第一页" ); adapter.addFragment(new SecondFragment (), "第二页" ); adapter.addFragment(new ThirdFragment (), "第三页" ); viewPager.setAdapter(adapter); } }
16.8简单的启动引导页
每一个页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <?xml version="1.0" encoding="utf-8" ?> <RelativeLayout xmlns:android ="http://schemas.android.com/apk/res/android" xmlns:app ="http://schemas.android.com/apk/res-auto" xmlns:tools ="http://schemas.android.com/tools" android:id ="@+id/main" android:layout_width ="match_parent" android:layout_height ="match_parent" tools:context =".MainActivity" > <ImageView android:id ="@+id/iv" android:layout_width ="match_parent" android:layout_height ="match_parent" android:contentDescription ="imageview" android:scaleType ="fitXY" /> <RadioGroup android:id ="@+id/rg" android:layout_width ="wrap_content" android:layout_height ="wrap_content" android:layout_alignParentBottom ="true" android:layout_centerHorizontal ="true" android:paddingBottom ="20dp" android:orientation ="horizontal" /> <Button android:id ="@+id/btn" android:textColor ="#ff3300" android:layout_centerInParent ="true" android:visibility ="gone" android:text ="立即开始" android:layout_width ="wrap_content" android:layout_height ="wrap_content" /> </RelativeLayout >
ViewPager
1 2 3 4 5 6 7 8 9 10 <?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" > <androidx.viewpager.widget.ViewPager android:id ="@+id/vp" android:layout_width ="match_parent" android:layout_height ="370dp" /> </LinearLayout >
设置adapter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public class LauchSimpleAdapter extends PagerAdapter { private List<View> mView = new ArrayList <>(); public LauchSimpleAdapter (Context mcontext,int [] launchImage) { for (int i=0 ;i<launchImage.length;i++){ View inflate = LayoutInflater.from(mcontext).inflate(R.layout.activity_main, null ); ImageView iv = inflate.findViewById(R.id.iv); RadioGroup rg = inflate.findViewById(R.id.rg); Button btn = inflate.findViewById(R.id.btn); iv.setImageResource(launchImage[i]); for (int j = 0 ; j < launchImage.length; j++) { RadioButton rb = new RadioButton (mcontext); rb.setLayoutParams(new ViewGroup .LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT )); rb.setPadding(10 ,10 ,10 ,10 ); rg.addView(rb); } ((RadioButton) rg.getChildAt(i)).setChecked(true ); if (i == launchImage.length-1 ){ btn.setVisibility(View.VISIBLE); btn.setOnClickListener( v ->{ Toast.makeText(mcontext,"welcome" , Toast.LENGTH_SHORT).show(); }); } mView.add(inflate); } } @Override public int getCount () { return mView.size(); } @Override public boolean isViewFromObject (@NonNull View view, @NonNull Object object) { return view == object; } @NonNull @Override public Object instantiateItem (@NonNull ViewGroup container, int position) { View item = mView.get(position); container.addView(item); return item; } @Override public void destroyItem (@NonNull ViewGroup container, int position, @NonNull Object object) { container.removeView(mView.get(position)); } }
创建界面
1 2 3 4 5 6 7 8 9 10 @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); EdgeToEdge.enable(this ); setContentView(R.layout.activity_main); ViewPager vp = findViewById(R.id.vp); LauchSimpleAdapter adapter = new LauchSimpleAdapter (this ,launchImage); vp.setAdapter(adapter); }
17.Fragment 1.创建fragment
静态注册
1 2 3 4 5 <fragment android:id ="@+id/fg" android:name ="com.richuff.server.fragment.BlankFragment" //自己创建的fragment的路径 android:layout_width ="match_parent" android:layout_height ="match_parent" />
动态注册
1 2 3 4 5 6 7 8 9 10 11 12 13 public class MainActivity extends AppCompatActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null ) { getSupportFragmentManager().beginTransaction() .add(R.id.fragment_container, MyFragment.class, null ) .commit(); } } }
2.改进启动引导页
在Activity里声明
1 2 3 4 5 ViewPager vp = findViewById(R.id.vp);LauchImproveAdapter adapter = new LauchImproveAdapter (getSupportFragmentManager(),launchImage);vp.setAdapter(adapter);
适配器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class LauchImproveAdapter extends FragmentPagerAdapter { private int [] mImageArray; public LauchImproveAdapter (@NonNull FragmentManager fm,int [] launchImage) { super (fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); this .mImageArray = launchImage; } @NonNull @Override public Fragment getItem (int position) { return TestFragment.newInstance(mImageArray.length,position,mImageArray[position]); } @Override public int getCount () { return mImageArray.length; } }
Fragment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class TestFragment extends Fragment { public static TestFragment newInstance (int count,int position, int imageId) { TestFragment fragment = new TestFragment (); Bundle args = new Bundle (); args.putInt("position" , position); args.putInt("imageId" , imageId); args.putInt("count" ,count); fragment.setArguments(args); return fragment; } @Override public void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); } @Override public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Bundle bundle = getArguments(); Context mcontext = getContext(); View minflate = LayoutInflater.from(mcontext).inflate(R.layout.view_page, null ); if (bundle != null ){ int position = bundle.getInt("position" ); int imageId = bundle.getInt("imageId" ); int count = bundle.getInt("count" ); ImageView iv = minflate.findViewById(R.id.iv); RadioGroup rg = minflate.findViewById(R.id.rg); Button btn = minflate.findViewById(R.id.btn); iv.setImageResource(imageId); for (int j = 0 ; j < count; j++) { RadioButton rb = new RadioButton (mcontext); rb.setLayoutParams(new ViewGroup .LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT )); rb.setPadding(10 ,10 ,10 ,10 ); rg.addView(rb); } ((RadioButton) rg.getChildAt(position)).setChecked(true ); if (position == count-1 ){ btn.setVisibility(View.VISIBLE); btn.setOnClickListener( v ->{ Toast.makeText(mcontext,"welcome" , Toast.LENGTH_SHORT).show(); }); } } return minflate; } }
优势