目录
- 一:Android系统架构
- 二:Actvity相关
- 三:UI相关
-
- 3.1简单基础控件
- 3.2 布局相关
- 3.3 高级控件
-
- 3.3.1 ListView 列表
- 3.3.1+ RecyclerView
- 3.3.2 Spinner 下拉列表
- 3.3.3 GridView 网格视图
- 3.3.4 ViewPager 视图滑动切换工具
- 3.3.5 ScrollView 滚动视图
- 3.3.6 ProgressBar 进度条
- 3.3.7 ProgressDialog 对话框形式进度条
- 3.3.8 SeekBar 可拖动进度条
- 3.3.9 DataPicker 日历选择器
- 3.3.10 DataPickerDialog 对话框形式日历选择器
- 3.3.11 TimePicker 时间选择器
- 3.3.12 TimePickerDialog 对话框形式时间选择器
- 3.3.13 WebView 网络视图
- 3.3.14 Dialog 对话框
- 3.3.15 Notification 通知
- 3.3.16 Meau 菜单
- 四:Fragment
- 五:广播 Broadcast Receiver
- 六:数据存储
- 七:ContentProvider
- 八:Service
- 九:番外篇
一:Android系统架构
二:Actvity相关
2.1基础相关
- 项目中任何活动都应该重写Activity的onCreate方法,如下是默认实现
public class FirstActivity extends AppcompatActivity{
protected void onCreate(Bundle savedInstanceStatus)
{
super.onCreate(saveInstanceState);
}
}
-
Android程序的设计讲究逻辑和视图分离,最好每个Activity都能对应一个布局。
-
所有的活动都要再AndroidManifest.xml中进行注册才能生效,如下:
可以看到,Activity的注册声明要放在标签内,这里通过标签来对活动进行注册的。
不过,仅仅注册活动仍然不行,因为需要为程序配置主活动:在标签内部加入<intent - filter>标签:
做完以上动作,FirstActivity就成为我们的主活动了
-
Toast提醒
在Activity中我们可以通过findViewById()获取到布局文件中定义的元素
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zSTYybRh-1619003793921)(en-resource://database/1800:0)]
Toast.makeText(A,B,C)分别表示:
A:Context,Toast要求的上下文
B:需要显示的文本内容
C:显示的时长 -
Menu
-
销毁Activity
按一下back键就可以销毁or:
2.2 Intent相关
Intent是一个将要执行的动作的抽象的描述,一般来说是作为参数来使用,由 Intent来协助完成 Android各个组件之间的通讯。比如说调用startActivity()来启动一个Activity,或者由broadcaseIntent()来传递给所有感兴趣的BroadcaseReceiver,再或者由startService() / bindservice()来启动一个后台的 service。所以可以看出来,Intent 主要是用来启动其他的 activity 或者 service,所以可以将 intent 理解成 activity 之间的粘合剂。
Intent作用的表现形式为:
启动Activity
通过Context.startActvity() / Activity.startActivityForResult()启动一个Activity;
启动Service
通过Context.startService()启动一个服务,或者通过Context.bindService()和后台服务交互;
发送Broadcast
通过广播方法Context.sendBroadcasts() / Context.sendOrderedBroadcast() / Context.sendStickyBroadcast()发给Broadcast Receivers
2.2.1 Intent的组成
Intent由6部分信息组成:Component Name、Action、Data、Category、Extras、Flags。根据信息的作用用于,又可分为三类:
-
A: Component Name、Action、Data、Category为一类,这4中信息决定了Android会启动哪个组件,其中Component Name用于在显式Intent中使用,Action、Data、Category、Extras、Flags用于在隐式Intent中使用。
-
B:Extras为一类,里面包含了具体的用于组件实际处理的数据信息。
-
C:Flags为一类,其是Intent的元数据,决定了Android对其操作的一些行为,下面会介绍。
1.Component name
要启动的组件的名称。如果你想使用显式的Intent,那么你就必须指定该参数,一旦设置了component name,Android会直接将Intent传递给组件名所指定的组件去启动它。如果没有设置component name,那么该Intent就是隐式的,Android系统会根据其他的Intent的信息(例如下面要介绍到的action、data、category等)做一些比较判断决定最终要启动哪个组件。所以,如果你启动一个你自己App中的组件,你应该通过指定component name通过显式Intent去启动它(因为你知道该组件的完整类名)。
需要注意的是,当启动Service的时候,你应该总是指定Component Name。否则,你不确定最终哪个App的哪个组件被启动了,并且用户也看不到哪个Service启动了。
2.Action
是表示了要执行操作的字符串,比如查看或选择,其对应着Intent Filter中的action标签。
你可以指定你独有的action以便于你的App中的Intent的使用或其他App中通过Intent调用你的App中的组件。Intent类和Android中其他framework级别的一些类也提供了许多已经定义好的具有一定通用意义的action。以下是一些用于启动Activity的常见的action:
3.Intent.ACTION_VIEW 其值为 “android.intent.action.VIEW”,当你有一些信息想让通过其他Activity展示给用户的时候,你就可以将Intent的action指定为ACTION_VIEW,比如在一个图片应用中查看一张图片,或者在一个地图应用中展现一个位置。
4.Intent.ACTION_SEND 其值为”android.intent.action.SEND”,该action常用来做“分享”使用,当你有一些数据想通过其他的App(例如QQ、微信、百度云等)分享出去的时候,就可以使用此action构建Intent对象,并将其传递给startActivity()方法,由于手机上可能有多个App的Activity均支持ACTION_SEND这一action,所以很有可能会出现如下的图片所示的情形让用户具体选择要通过哪个App分享你的数据:
5.Data
此处所说的Intent中的data指的是Uri对象和数据的MIME类型
6.Category
category包含了关于组件如何处理Intent的一些其他信息,虽然可以在Intent中加入任意数量的category,但是大多数的Intent其实不需要category。
以下是一些常见的category:
CATEGORY_BROWSABLE 目标组件会允许自己通过一个链接被一个Web浏览器启动,该链接可能是一个图片链接或e-mail信息等。
CATEGORY_LAUNCHER 用于标识Activity是某个App的入口Activity。
7.Extras
extras,顾名思义,就是额外的数据信息,Intent中有一个Bundle对象存储着各种键值对,接收该Intent的组件可以从中读取出所需要的信息以便完成相应的工作。有的Intent需要靠Uri携带数据,有的Intent是靠extras携带数据信息。
你可以通过调用Intent对象的各种重载的putExtra(key, value)方法向Intent中加入各种键值对形式的额外数据。你也可以直接创建一个Bundle对象,向该Bundle对象传入很多键值对,然后通过调用Intent对象的putExtras(Bundle)方法将其一块设置给Intent对象中去。
例如,你创建了一个action为ACTION_SEND的Intent对象,然后想用它启动e-mail发送邮件,那么你需要给该Intent对象设置两个extra的值:
用Intent.EXTRA_EMAIL 作为key值设置收件方,用Intent.EXTRA_SUBJECT 作为key值设置邮件标题。
8.Flags
flag就是标记的意思,Intent类中定义的flag能够起到作为Intent对象的元数据的作用。这些flag会告知Android系统如何启动Activity(例如,新启动的Activity属于哪个task)以及在该Activity启动后如何对待它(比如)。更多信息可参见Intent的setFlags()方法。
2.2.2 显式Intent
显式,即直接指定需要打开的activity对应的类。
1)构造方法传入Component,最常用的方式:
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
2)setComponent方法
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
//或者intent.setClassName(this, "com.example.app.SecondActivity"); //或者intent.setClassName(this.getPackageName(),"com.example.app.SecondActivity"); startActivity(intent);
3)setClass / setClassName方法
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
//或者intent.setClassName(this, "com.example.app.SecondActivity"); //或者intent.setClassName(this.getPackageName(),"com.example.app.SecondActivity"); startActivity(intent);
显式Intent通过Component可以直接设置需要调用的Activity类,可以唯一确定一个Activity,意图特别明确,所以是显式的。设置这个类的方式可以是Class对象(如SecondActivity.class),也可以是包名加类名的字符串(如"com.example.app.SecondActivity")。
2.2.3 隐式Intent
隐式,不明确指定启动哪个Activity,而是设置Action、Data、Category,让系统来筛选出合适的Activity。筛选是根据所有的来筛选。
下面以Action为例:
AndroidManifest.xml文件中,首先被调用的Activity要有一个带有并且包含的Activity,设定它能处理的Intent,并且category设为"android.intent.category.DEFAULT"。action的name是一个字符串,可以自定义,例如这里设成为"mark":
<activity
android:name="com.example.app.SecondActivity">
<intent-filter>
<action android:name="mark"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
然后,在MainActivity,才可以通过这个action name找到上面的Activity。下面两种方式分别通过setAction和构造方法方法设置Action,两种方式效果相同。
1)setAction方法
Intent intent = new Intent();
intent.setAction("mark");
startActivity(intent);
2)构造方法直接设置Action
Intent intent = new Intent("mark");
startActivity(intent);
为了防止应用程序之间互相影响,一般命名方式是包名+Action名,例如这里命名"mark"就很不合理了,就应该改成"com.example.app.Test"。
2.2.4 Intent属性
Intent对象大致包括7大属性:Action(动作)、Data(数据)、Category(类别)、Type(数据类型)、Component(组件)、Extra(扩展信息)、Flag(标志位)。其中最常用的是Action属性和Data属性。
2.2.5 Intent用法
- 调用拨号程序:
// 调用拨打电话,给10010拨打电话
Uri uri = Uri.parse("tel:10010");
Intent intent = new Intent(Intent.ACTION_DIAL, uri);
startActivity(intent);
// 直接拨打电话,需要加上权限 <uses-permission id="android.permission.CALL_PHONE" />
Uri callUri = Uri.parse("tel:10010");
Intent intent = new Intent(Intent.ACTION_CALL, callUri);
- 发送短信或彩信
// 给10010发送内容为“Hello”的短信
Uri uri = Uri.parse("smsto:10010");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
intent.putExtra("sms_body", "Hello");
startActivity(intent);
// 发送彩信(相当于发送带附件的短信)
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra("sms_body", "Hello");
Uri uri = Uri.parse("content://media/external/images/media/23");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setType("image/png");
startActivity(intent);
- 通过浏览器打开网页
// 打开百度主页
Uri uri = Uri.parse("https://www.baidu.com");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
- 发送电子邮件
// 给[email protected]发邮件
Uri uri = Uri.parse("mailto:[email protected]");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
startActivity(intent);
// 给[email protected]发邮件发送内容为“Hello”的邮件
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, "[email protected]");
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Hello");
intent.setType("text/plain");
startActivity(intent);
// 给多人发邮件
Intent intent=new Intent(Intent.ACTION_SEND);
String[] tos = {
"[email protected]", "[email protected]"}; // 收件人
String[] ccs = {
"[email protected]", "[email protected]"}; // 抄送
String[] bccs = {
"[email protected]", "[email protected]"}; // 密送
intent.putExtra(Intent.EXTRA_EMAIL, tos);
intent.putExtra(Intent.EXTRA_CC, ccs);
intent.putExtra(Intent.EXTRA_BCC, bccs);
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
intent.putExtra(Intent.EXTRA_TEXT, "Hello");
intent.setType("message/rfc822");
startActivity(intent);
- 显示地图与路径规划
// 打开Google地图中国北京位置(北纬39.9,东经116.3)
Uri uri = Uri.parse("geo:39.9,116.3");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
// 路径规划:从北京某地(北纬39.9,东经116.3)到上海某地(北纬31.2,东经121.4)
Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=39.9 116.3&daddr=31.2 121.4");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
- 播放多媒体
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/foo.mp3");
intent.setDataAndType(uri, "audio/mp3");startActivity(intent);
Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
- 选择图片
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, 2);
- 拍照
// 打开拍照程序
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, 1);
// 取出照片数据
Bundle extras = intent.getExtras();Bitmap
Bitmap = (Bitmap) extras.get("data");
- 打开手机应用市场
// 打开手机应用市场,直接进入该程序的详细页面
Uri uri = Uri.parse("market://details?id=" + packageName);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
- 安装程序
String fileName = Environment.getExternalStorageDirectory() + "/myApp.apk";
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(fileName)),
"application/vnd.android.package-archive");
startActivity(intent);
- 卸载程序
Uri uri = Uri.parse("package:" + packageName);
Intent intent = new Intent(Intent.ACTION_DELETE, uri);
startActivity(intent);
- 进入设置界面
// 进入系统设置界面
Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
startActivity(intent);
2.2.6 Intent——Activity间传递数据
(1)传递数据给其他Activity
(2)返回数据给上一个Activity
2.3 Activity生命周期
-
Android是使用Task来管理活动的,启动一个新的Activity,他就会在返回栈中入栈,并处于栈顶的位置,按下back键/finish()方法销毁某个活动时,处于栈顶的函数就会出栈。
如下图:
-
Activity状态
-
Activity生命周期
-
Activity被回收了怎么办
当一个Activity进入到停止状态,有可能因为系统内存不足而被系统回收,此时用户如果按back键返回上一个Activity,仍然会正常显示,但是不会执行onRestart()方法,而是会执行onCreate()方法,重新创建一次该Activity。
但是如果上一个Activity中有数据的话,就会很影响用户体验了。
2.4 Activity启动模式
Activity一共有四种启动模式:
- Standard
- singleTop
- singleTask
- singleInstance
在AndroidManifest.xml中通过给<activity>标签制定android:launchMode属性选择启动模式
2.4.1 standard
- 不显示指定下,所有活动都会自动使用这种启动模式。
- android使用返回栈来管理,每启动一个新的Activity,系统不会管这个Activity是否存在,就会创建该Activity的一个新的实例。
在FirstActivity的基础上启动Activity,看起来有点奇怪,不过我们重点在于看standard模式。
现在重新运行程序,并在FirstActivity界面点击两次按钮。
从日志可见,每次点击一次按钮就会创建一个新的实例,所以需要三次Back才能退出程序
2.4.2 singleTop
- 启动模式指定为singleTop时,发现返回栈的栈顶已经是该Activity,就直接使用,不再创建新的实例
-A为singleTop
A->B->A,将会创建两个A实例,back返回B,back返回A,再back才退出程序
2.4.3 singleTask
- 使用singleTop时,如Activity未处于栈顶位置,可能会创建多个Activity。
- 启动模式指定为singleTask时,可以使整个应用程序的上下文只存在一个实例,每次启动活动时就会在返回栈中检查是否存在该Activity实例
2.4.4 singleInstance
- singleInstance模式的Activity启动时会启用一个新的返回栈,那么这样做有什么意义呢?
如果程序中有一个Activity是允许其他程序调用,就需要这个启动模式了,因为每个启动模式都有自己的返回栈,同一个Activity在不同的返回栈中入栈时必然会创建新的实例,
而使用singleInstance模式下,会有一个单独的返回栈管理这个Activity,不管哪个应用程序访问,都可以共用一个返回栈,解决了共享Activity实例的问题。
2.4 Activity实践
2.4.1 判断自己在哪一个Activity
- 如果接手别人的项目,很可能找不到界面对应的Activity,这时候就需要:
2.4.2 随时随地退出程序
2.4.3 启动Activity最佳方法
- startActivity()方法
- startActivityForResult()方法
- 如启动的时候有参数需要传递
- 如对接时,SecondActivity不是你开发的,你负责的只有启动SecondActivity这个功能,但是你不知道是否需要传递数据。(阅读SecondActivity代码/询问同事)
三:UI相关
3.1简单基础控件
- TextView 文本框
- EditText 可输入文本框
- AutoCompleteTextView 自动匹配文本内容
- MutiAutoCompleteTextView 支持多次自动匹配文本内容
- ImageView 图片
- Botton 按钮
- ImageButton 图片按钮
- ToggleButton 多状态按钮
- CheckBox 复选框
- RadioButton 单选按钮
3.1.1 TextView :显示文本框
红框内圈出的前三行:
android:id(指控件id,在其他地方可通过id找到这个控件,注意书写格式@+id/控件名);
android:layout_width(指控件的宽度,有两个常用选值,wrap_content包裹控件的宽度和match_parent铺满父容器的宽度 ,当然也可以自定义宽度,单位dp,如android:layout_width=“200dp”);
android:android_height(指控件的高度,可选值同layout_width);
安卓所有控件都有这三个属性,也是必不可少的属性。除了这些,每个控件还有属于自己的属性,下面介绍TextView常用的属性。
android:text(指文本内容,好编程习惯是将具体的文本内容放到values->strings里,然后用@string/名引用)
android:textSize(指文本大小,单位sp)
android:textColor(指字体颜色,以#开头的六位,可以在直接修改颜色)
android:background(指控件背景,可以是颜色也可以是图片,如果是图片,会铺满整个控件,也就是可能会变形)
3.1.2 EditText :可输入文本框
android:hint(指输入提示文本内容。当然EditText也有android:text属性,它们的区别是,当用户准备在输入文本框输入的时候,hint的文本内容会消失,而text的文本内容不会消失会跟在用户输入内容的后面)
android:inputType(指输入文本的类型,比如data,number等等,保证用户输入的格式正确)
3.1.3 AutoCompleteTextView :自动匹配文本内容
android:completionThreshold,指设置输入多少字符时提示内容。
实现方法,分三步:
第一步:在类内定义一个AutoCompleteTextView对象,然后在onCreate方法里用findViewById的方法找到之前定义好的AutoCompleteTextView控件,格式是R.id.控件id名,这就是为什么要在.xml布局文件里给控件一个id的原因,又由于findViewById返回的是View类对象,要在方法前加上强制转换(AutoCompleteTextView)。
第二步:在类内定义一个适配器ArrayAdapter,适配器是连接数据源和视图界面的桥梁,本例用数据适配器就足够,关于适配器详细内容后续会介绍。然后初始化适配器加载数据源,这里自定义的data数组就是被加载的数据源,其他两个参数this和android.R.layout.simple_list_item_1照写即可。
第三步:用 控件的自身方法setAdapter去加载适配器ArrayAdapter。
完成这三步就可以实现了!
3.1.4 MutiAutoCompleteTextView :支持多次自动匹配文本内容
同时给多个人发邮件的时候会注意到,每次输入一个收件邮箱都会有提示内容,这就是MutiAutoCompleteTextView功能,
它有个方法setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer())指设置以逗号分隔符为结束的符号。它的使用方法和AutoCompleteTextView的使用基本一致
//.xml布局文件里设置一个MultiAutoCompleteTextView控件的代码
<MultiAutoCompleteTextView
android:hint="请输入要发送的对象"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/multiAutoCompleteTextView" />
//在MainActivity里实现的代码
public class MainActivity extends AppCompatActivity {
private ArrayAdapter<String>