课程目录
Android应用开发基础课程--15天做一个合格的Android程序员--课程目录
上一章,我们已经将大部分的布局和控件,以及控件的属性都在xml中操作了,对布局和控件已经有了初步的了解,后面就是在学习和项目中去加深和完善了,使用多了自然就会熟练的。
下面进入界面开发的下一阶段。
前面讲的是怎么用布局和控件来实现app的静态界面,而app的界面肯定不是静态的,那么在哪里来对静态的界面进行动态的操作呢?这里我们首先来熟悉activity的概念。
Activity
Activity是Android组件中最基本也是最为常见用的四大组件(Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器)之一。
Activity
是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。 每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。
一个应用通常由多个彼此松散联系的 Activity 组成。 一般会指定应用中的某个 Activity 为“主”Activity,即首次启动应用时呈现给用户的那个 Activity。 而且每个 Activity 均可启动另一个 Activity,以便执行不同的操作。 每次新 Activity 启动时,前一 Activity 便会停止,但系统会在堆栈(“返回栈”)中保留该 Activity。 当新 Activity 启动时,系统会将其推送到返回栈上,并取得用户焦点。 返回栈遵循基本的“后进先出”堆栈机制,因此,当用户完成当前 Activity 并按“返回”按钮时,系统会从堆栈中将其弹出(并销毁),然后恢复前一 Activity。 (任务和返回栈文档中对返回栈有更详细的阐述。)
当一个 Activity 因某个新 Activity 启动而停止时,系统会通过该 Activity 的生命周期回调方法通知其这一状态变化。Activity 因状态变化—系统是创建 Activity、停止 Activity、恢复 Activity 还是销毁 Activity— 而收到的回调方法可能有若干种,每一种回调都会为您提供执行与该状态变化相应的特定操作的机会。 例如,停止时,您的 Activity 应释放任何大型对象,例如网络或数据库连接。 当 Activity 恢复时,您可以重新获取所需资源,并恢复执行中断的操作。 这些状态转变都是 Activity 生命周期的一部分。
activity生命周期(也可以理解为activity执行流程)见图:
上面我们对activity做了一个简单的概述,也了解了activity的生命周期,那么我们怎么使用呢?
activity注册
所有的activity必须要在AndroidManifest.xml中进行注册才能生效,才可以被唤起打开。如果不注册的话,打开activity会出现下面的错误。
Process: com.example.administrator.hello, PID: 1862
android.content.ActivityNotFoundException: Unable to find explicit activity class {com.example.administrator.hello/com.example.administrator.hello.activity.LayoutAndWidgetDemoActivity}; have you declared this activity in your AndroidManifest.xml?
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2005)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1673)
at android.app.Activity.startActivityForResult(Activity.java:4586)
at android.app.Activity.startActivityForResult(Activity.java:4544)
at android.app.Activity.startActivity(Activity.java:4905)
at android.app.Activity.startActivity(Activity.java:4873)
at com.example.administrator.hello.MainActivity.goToLayoutWidgetDemo(MainActivity.java:40)
at com.example.administrator.hello.MainActivity.access$000(MainActivity.java:14)
at com.example.administrator.hello.MainActivity$1.onClick(MainActivity.java:33)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25885)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6680)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
怎么在AndroidManifest.xml中进行注册呢?先看下as自动生成的。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.administrator.hello">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
activity都是在manifest文件中的application中注册的,看上面的代码,这里已经有一个MainActivity注册了,那么我们可以按照这个activity来注册吗?不行。这个activity中有一个属性:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
这一段是干什么的呢?这是表示这个activity是这个app的第一个页面也就是入口,而在app中只能有一个入口。那么怎么注册其他activity呢?
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.administrator.hello">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.LayoutAndWidgetDemoActivity" />
</application>
</manifest>
这是一个简单的注册例子,在activity标签中还可以加入很多属性,例如横竖、theme等,具体请大家搜索引擎查询。
完成注册那么下一步就是起启动activity。
activity启动
要了解activity的启动首先我们先要了解Intent。
Intent
1、Intent的概念:
- Android中提供了Intent机制来协助应用间的交互与通讯,或者采用更准确的说法是,Intent不仅可用于应用程序之间,也可用于应用程序内部的activity, service和broadcast receiver之间的交互。Intent这个英语单词的本意是“目的、意向、意图”。
- Intent是一种运行时绑定(runtime binding)机制,它能在程序运行的过程中连接两个不同的组件。通过Intent,你的程序可以向Android表达某种请求或者意愿,Android会根据意愿的内容选择适当的组件来响应。
activity、service和broadcast receiver之间是通过Intent进行通信的,而另外一个组件Content Provider本身就是一种通信机制,不需要通过Intent。通俗来讲,Intnet类似于中介、媒婆的角色。我们来看下面这个图就知道了:
具体的信息在这里不再一一表述,https://www.cnblogs.com/engine1984/p/4146621.html 更详细的信息可以看这里。
启动activity
启动activity的方式有显式启动、隐式启动。这里我贴一个最简单的显示启动:
Intent intent = new Intent(MainActivity.this, LayoutAndWidgetDemoActivity.class);
startActivity(intent);
直接new一个intent对象,在通过调用activity里面的startActivity方法来启动其他activity。
这里引申一下:
启动activity分为方式和模式,这两者不是同一个东西,我这里还是详细描述下。
启动activity的方式有显式启动、隐式启动。
1. 显式启动:通过包名来启动,写法如下:
①最常见的:
startActivity(new Intent(当前Act.this,要启动的Act.class));
startActivityForResult
②通过Intent的ComponentName:
ComponentName cn = new ComponentName("当前Act的全限定类名","启动Act的全限定类名") ;
Intent intent = new Intent() ;
intent.setComponent(cn) ;
startActivity(intent) ;
③初始化Intent时指定包名:
Intent intent = new Intent("android.intent.action.MAIN");
intent.setClassName("当前Act的全限定类名","启动Act的全限定类名");
startActivity(intent);
2.隐式启动:通过Intent-filter的Action,Category或data来实现 这个是通过Intent的 intent-filter**来实现的,这个Intent那章会详细讲解! 这里知道个大概就可以了!
3. 另外还有一个直接通过包名启动apk的:
Intent intent = getPackageManager().getLaunchIntentForPackage
("apk第一个启动的Activity的全限定类名") ;
if(intent != null) startActivity(intent) ;
启动activity的四种模式:
1、standard 标准模式,也是默认模式。每当我们启动一个Activity,系统就会相应的创建一个实例,不管这个实例是否已经存在。
2、singleTop 栈顶复用模式,如果要启动的Activity处于栈的顶部,那么此时系统不会创建新的实例,而是直接打开此页面,同时它的onNewIntent()方法会被执行,可以通过Intent进行传值,而且它的onCreate(),onStart()方法不会被调用,因为它并没有发生任何变化。
3、singleTask 栈内复用模式,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,因为singleTask本身自带clearTop这种功能。并且会回调该实例的onNewIntent()方法。
4、singleInstance 单实例模式,该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。
启动模式的设置(Intent设置的优先级>AndroidMainifest设置):
1、在AndroidMainifest设置:在AndroidMainifest的Activity配置进行设置,android:launchMode="启动模式"
2、通过Intent设置标志位:
Intent inten = new Intent (ActivityA.this,ActivityB.class);
intent,addFlags(Intent,标记位属性);
startActivity(intent);
标记位属性:
Intent.FLAG_ACTIVITY_SINGLE_TOP:Activity指定SingleTop启动模式
Intent.FLAG_ACTIVITY_NEW_TASK:Activity指定SingleTask启动模式
Intent.FLAG_ACTIVITY_CLEAR_TOP:具有此标记位的Activity,启动时会将与该Activity在同一任务栈的其他Activity出栈。一般与SingleTask启动模式一起出现。
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有此标记位的Activity不会出现在历史Activity的列表中,使用场景:当某些情况下我们不希望用户通过历史列表回到Activity时,此标记位便体现了它的效果。
到这里我们了解了activity的启动,下面做一个简单的例子。
首先在我们的主页上做一个入口:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/layout_widget_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colorPrimaryDark"
android:text="布局和控件demo:"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:id="@+id/layout_widget_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/layout_widget_text"
android:background="@null"
android:textColor="@color/colorAccent"
android:text="进入 >>"/>
</android.support.constraint.ConstraintLayout>
界面的效果如上。
打开MainActivity文件,我们开始来对这个界面的控件做动态的操作。
package com.example.administrator.hello;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.example.administrator.hello.activity.BaseActivity;
import com.example.administrator.hello.activity.LayoutAndWidgetDemoActivity;
public class MainActivity extends BaseActivity {
private Button layout_widget_btn; // 按钮对象
@Override
protected void initView() {
setContentView(R.layout.activity_main); // 设置activity要加载的界面
}
@Override
protected void findView() {
layout_widget_btn = findViewById(R.id.layout_widget_btn); // 初始化按钮对象
}
@Override
protected void addAction() {
// 给按钮设置点击事件
layout_widget_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
goToLayoutWidgetDemo();
}
});
}
// 启动activity
private void goToLayoutWidgetDemo() {
Intent intent = new Intent(MainActivity.this, LayoutAndWidgetDemoActivity.class);
startActivity(intent);
}
}
直接上代码,代码中有相应的注释。这里有几个知识点:
1、如何配置activity的加载界面
2、如何获取界面里的控件对象
3、如何给控件对象设置点击事件
4、如何启动activity
这里把要启动的activity也贴出来:
package com.example.administrator.hello.activity;
import com.example.administrator.hello.R;
public class LayoutAndWidgetDemoActivity extends BaseActivity {
@Override
protected void initView() {
setContentView(R.layout.activity_layout_widget_demo);
}
@Override
protected void findView() {
}
@Override
protected void addAction() {
}
}
下面我们运行一下:
这个是demo下载地址:https://download.csdn.net/download/ovejur/11088009