简单说下:本人是一名安卓初学者,马上奔大四,之前上过一门安卓的课,没怎么听,但考试复习还是让我学了点东西。暑假里参加过一次为期10天的安卓培训还做了个小软件,回来以后就对此念念不忘,决心学习安卓,这是我的第一站《第一行代码》
一 开始启程,你的第一行代码
Log,优于调代码用的System.out.print();
Log.d("HelloWorldActivity", "onCreate execute");
//Log.d 方法中传入了两个参数,第一个参数是tag,一般传入当前的类名就好,主要用于对打印信息进行过滤。第二个参数是msg,即想要打印的具体的内容。
过滤器添加方法:
点击加号,然后设置如by Log Tag:data,就可以过滤名为data的tag
日志级别控制:
Once之Activity中控件的引用:
1.在onCreate()中,定义一个控件,并与xml中控件关联:
Button switcher=(Button)findViewById(R.id.switcher);
//可以把该button在OnCreate()外声明为私有:private Button switcher;
Once之点击事件写法
对于已经定义好的控件,比如button,设置其点击事件:
1.在Activity声明处添加接口: implements OnClickListener(选android.view.View,如果选错了,删掉上面import的包,重新添加接口)
2.OnCreate()中,在定义好名为switcher的button后
switcher.setOnClickListener(this);
3.添加事件处理方法:
public void onClick(View arg0) {
// TODO Auto-generated method stub
}
或者第2步不以this为变量,而是新建一个点击事件直接操作:
switcher.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "You clicked Button 1", Toast.LENGTH_SHORT)
.show();
}
});
二 先从看得到的入手,探究活动
Once之添加一个完整的Activity
1.新建活动,右击包名,New-Class,在Name栏输入大写开头的名字,在Superclass中输入android.app.Activity,点击确定
添加代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
2.新建xml文件,右击res/layout,New-Android XML File
3.关联:
setContentView(R.layout.first_layout);//放到super.onCreate(savedInstanceState);后面,如果让import R.class,一定要选Activity所在的包,血的教训啊~
4.打开AndroidManifest.xml 来给FirstActivity 注册
<activity
android:name=".FirstActivity"
android:label="This is FirstActivity" >//活动中标题栏的内容&应用程序显示的名称
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
如果不是主activity,不需要intent-filter这个标签,label也不需要
<activity android:name=".SecondActivity" >
</activity>
隐藏标题栏:在OnCreate()之setContentView()前添加:
requestWindowFeature(Window.FEATURE_NO_TITLE);
Toast(活动中偶尔弹出的透明的提示):
在点击事件OnClick()中添加:
Toast.makeText(FirstActivity.this, "You clicked Button 1",Toast.LENGTH_SHORT).show();
在活动中使用Menu——ADT 在HelloWorldActivity 中自动创建了一个onCreateOptionsMenu()方法
菜单xml中加菜单:
<item
android:id="@+id/remove_item"
android:title="Remove"/>
MainActivity中onCreateOptionsMenu()对菜单xml文件的引用:
getMenuInflater().inflate(R.menu.main, menu);
return true;//允许创建的菜单显示出来
菜单响应事件:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_settings:
Toast.makeText(this, "你丫点了设置", Toast.LENGTH_SHORT).show();
break;
case R.id.mymenu1:
Toast.makeText(this, "你丫点了woshi菜单", Toast.LENGTH_SHORT).show();
break;
default:
}
return true;
}
销毁一个活动:
默认按下back键就销毁
若想点按钮销毁,在按钮监听事件中添加一句:finish();
显式Intent之跳转
Intent 不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。它一般可被用于启动活动、启动服务、以及发送广播等场景
按钮点击事件中:
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
//传入FirstActivity.this 作为上下文,传入SecondActivity.class作为目标活动
startActivity(intent);
隐式Intent
隐式启动Activity的intent到底发给哪个activity,需要进行三个匹配,一个是action,一个是category,一个是data,可以是全部或部分匹配
在某个Activity里用startActivity()方法发送一个intent,这个intent设定了一些条件,比如用方法setAction(),addCategory()设定了两个属性(其中category属性可以设置多个)
发送了这个intent之后,android会去系统里保存的AndroidManifest.xml清单(假设这个系统存放全部apk清单的文件为AndroidManifest.xml)里查找符合这两个属性的activity,然后启动它。
欲匹配Activity在AndroidManifest.xml中的标签:
<intent-filter >
<action android:name="cc.android/myaction.leo"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
原Activity中隐式启动Activity:
intent.setAction( "cc.android/myaction.leo");
//不加下面这行也行,因为intent的这个属性默认值即系Intent.CATEGORY_DEFAULT
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity( intent );
More usage about 隐式Intent
(AndroidManifest.xml<data>标签):
隐式Intent可以用来启动其他应用程序的Activity
<data>标签用于更精确地指定当前活动能够响应什么类型的数据
跳转网页&Activity<data>标签:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
Action:Intent.ACTION_VIEW,常量值:android.intent.action.VIEW,效果:打开浏览器,跳转到http://www.baidu.com
setData():接收一个Uri 对象,主要用于指定当前Intent 正在操作的数据
<data>标签中主要可以配置以下内容。
1. android:scheme
用于指定数据的协议部分,如上例中的http 部分。
2. android:host
用于指定数据的主机名部分,如上例中的www.baidu.com 部分。
3. android:port
用于指定数据的端口部分,一般紧随在主机名之后。
4. android:path
用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容。
5. android:mimeType
用于指定可以处理的数据类型,允许使用通配符的方式进行指定。
做一个可以响应网页请求的Intent的Activity
Activity<intent-filter >配置为<data android:scheme="http" />:当前活动能够响应的action 是Intent.ACTION_VIEW 的常量值
在程序清单文件activity<intent-filter>标签中添加
<data android:scheme="http" />
效果:在点击之前的按钮后,可选用ActivityTest打开网页
Intent除了http 协议外,还可以指定很多其他协议,比如geo 表示显示地理位置、tel 表示拨打电话
如:
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
就会给10086打电话
Once之Intent传值:
FirstActivity中点击事件:
String data = "Hello SecondActivity";
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("extra_data", data);
startActivity(intent);
SecondActivity的OnCreate()中:
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");
Log.d("SecondActivity", data);
以extra_data为键,从FirstActivity的Intent中取出值data,再在SecondActivity的Intent得到extra_data的值,并输出
Once之Intent返回值:
FirstActivity中点击事件:
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivityForResult(intent, 1);//多了个请求码,自己设值
SecondActivity的新建点击事件:
Intent intent = new Intent();
intent.putExtra("data_return", "Hello FirstActivity");//键-值,不解释
setResult(RESULT_OK, intent);//结果码:返回处理结果,传回带值的Intent
finish();//销毁当前活动SecondActivity
FirstActivity中生成的函数:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {//对请求码和结果码匹配
case 1:
if (resultCode == RESULT_OK) {
String returnedData = data.getStringExtra("data_return");
Log.d("FirstActivity", returnedData);//将获取的值显示
}
break;
default:
}
}
如果按下back键返回,不会出发SecondActivity的点击事件,可以通过重写按下back键的onBackPressed()方法来解决
Once之back键处理方法onBackPressed():
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("data_return", "Hello FirstActivity");
setResult(RESULT_OK, intent);
finish();
}
ActivityTest:体验android生命周期
1.尝试新建好MainActivity和activity-main.xml并弄成初始化的样子,过程之前有说
2.新建normal_layout.xml和dialog_layout.xml文件,各自带一个TextView控件,标识自己。
3.新建NormalActivity和DialogActivity类,都只是初始化的样子(一个oncreate(),一个setContentView())
4.AndroidManifest.xml中分别注册为:
<activity android:name=".NormalActivity" >
</activity>
<activity
android:name=".DialogActivity"
android:theme="@android:style/Theme.Dialog" >
</activity>
5.MainActivity的xml添加俩按钮,分别设置点击事件跳转到另外的activity,并在各个回调方法打印信息
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
效果:打开apk以后,打印onCreate()、onStart()、onResume()
点击跳转normalactivity后,打印onPause()、onStop()
按下back键返回MainActivity,打印onRestart()、onStart()、onResume()
点击跳转DialogActivity,出现对话框活动DialogActivity(MainActivity被部分遮挡),打印onPause()
按下back键返回MainActivity,打印onResume()
保存被挤掉activity数据的onSaveInstanceState()方法:
onSaveInstanceState()回调方法,这个方法会保证一定在活动被回收之前调用,因此我们可以通过这个方法来解决活动被回收时临时数据得不到保存的问题。
onSaveInstanceState()方法会携带一个Bundle 类型的参数,Bundle 提供了一系列的方法用于保存数据,比如可以使用putString()方法保存字符串,使用putInt()方法保存整型数据,以此类推。每个保存方法需要传入两个参数,第一个参数是键,用于后面从Bundle 中取值,第二个参数是真正要保存的内容。
在MainActivity 中添加如下代码就可以将临时数据进行保存:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String tempData = "Something you just typed";
outState.putString("data_key", tempData);
}
当活动被系统回收之前有通过onSaveInstanceState()方法来保存数据的话,onCreate()方法的Bundle参数就会带有之前所保存的全部数据,我们只需要再通过相应的取值方法将数据取出即可。
if (savedInstanceState != null) {
String tempData = savedInstanceState.getString("data_key");
Log.d(TAG, tempData);
}
取出值之后再做相应的恢复操作就可以了
Activity的启动模式(返回栈存储方式):
在AndroidManifest.xml 中通过给<activity> 标签指定android:launchMode 属性来选择启动模式,如:android:launchMode="singleTop"
Standard:默认的启动模式,启动任何Activity都直接放到栈顶。即使上次已经新启动了这个Activity,再次启动后也是新实例后放到栈顶。
singleTop:android:launchMode="singleTop" 优化内容:如果栈顶的Activity实例就是欲启动的Activity,不会再添加新的Activity而是直接使用栈顶的实例。
singleTask:优化内容:每次启动活动的时候,都会在返回栈中检查是否有相同的Activity实例,如果有,一直出栈,直到该Activity到达栈顶。
singleInstance最特殊最复杂的一种启动模式。机理:设为singleInstance启动模式的活动会放到一个单独的返回栈里。解决问题:如果程序中有一个活动是允许其他程序调用的,在别的返回栈入栈必然创建一个新的Activity实例,本模式就是用来解决这个问题的。
注意:每次按下back键,都是进行一次出栈然后显示栈顶的Activity,所以可能调用顺序是A、B(单独栈)、C,而按下back显示的Activity顺序是:C->A->B。
老司机的实践技巧:
快速定位当前活动:
自定义一个BaseActivity类继承自Activity,重写其onCreate()方法(打印当前所在活动),各个活动不再继承Activity而是继承BaseActivity。
原继承关系:Activity->当前活动
现继承关系:Activity->BaseActivity(重写onCreate()方法打印所在活动)->当前活动
BaseActivity:
重写onCreate()方法:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity", getClass().getSimpleName());
}
随时随地退出程序,不再狂按back键:
背景:如果现在处于程序的第n个活动,要按下很多次back键才能退出程序
解决方法:一个专门的集合类对所有的活动进行管理
新建活动管理器类ActivityCollector:
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<Activity>();
public static void addActivity(Activity activity) {
activities.add(activity);
}
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
public static void finishAll() {//一键退出功能实现(调用每个Activity的finish()方法)
for (Activity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
}
}
然后,在每个活动的onCreate()中都加一句:ActivityCollector.addActivity(this);每个活动的onDestroy()中都加一句:ActivityCollector.removeActivity(this);这样ActivityCollector中存储的就都是当前正在运行的Activity,如果想一键退出,只需要调用ActivityCollector.finishAll()方法即可
被传值活动的启动写法:
背景:面对一个复杂的需要传过去几个数据的陌生activity,如果一时不知道需要传给它什么数据,会非常难办。
应对习惯:当我们写这个被传值的Activity的时候,提前把要启动的Intent代码写在一个新建的方法里,如果其他Activity要给此Activity传值,直接调用定义好的启动方法,具体如下:
假设SecondActivity需要两个数据data1,data2,添加方法如下:
public static void actionStart(Context context, String data1, String data2) {
Intent intent = new Intent(context, SecondActivity.class);
intent.putExtra("param1", data1);
intent.putExtra("param2", data2);
context.startActivity(intent);
}
//该方法就已经封装好了启动所在Activity(SecondActivity)需要的4行代码,当其他Activity需要启动SencondActivity时,只需要调用该方法传参即可。
——郭霖大大真是棒棒哒,赞一个!
Tips:
1.程序名字在哪里:AndroidManifest.xml中,定义主Activity的时候,有一句
android:label="@string/app_name"表示主Activity的标题栏名称,同时表示应用程序的名称
2.安卓图标在哪里:AndroidManifest.xml中
android:icon="@drawable/ic_launcher"
3.AndroidManifest.xml默认的路径的com.example.activitytest.@#¥@#%//这里activitytest为工程名
4.对话框主题Activity,只需要在AndroidManifest.xml注册Activity的时候加一句:android:theme="@android:style/Theme.Dialog"
5.getTaskId()得到当前返回栈的ID
6.For(变量类型 变量名:数组名)//机理:每次循环里,将数组里的每个元素依次赋给该变量用来遍历。如:数组char b[]={'a','b','c','d'};
for(char ch : b)
//ch 代表数组中元素类型,从数组中第一个元素开始遍历,b代表被遍历的数组名
{
System.out.println(ch);//打印出数组b中的元素
}//ch值依次为‘a’,‘b’,‘c’,‘d’
小bug:
1.在setContentView(R.layou.#$@)中一直找不到对应的xml
原因:没有正确引用R.class,引用路径错误
2.java.lang.RuntimeException:Unable to instantiate activity ComponentInfo{com.example.activitytest/com.example.activitytest.MainActivity}: java.lang.ClassNotFoundException: Didn't find class "com.example.activitytest.MainActivity" on path: DexPathList[[zipfile "/data/app/com.example.activitytest-1.apk"],nativeLibraryDirectories=[/data/app-lib/com.example.activitytest-1, /system/lib]]
无法实例化activity,在对应路径找不到类
原因:AndroidManifest.xml默认的路径的com.example.activitytest.@#¥@#%,如果新建的包名不是这个,路径就不对
问题一:在新版SDK中,OnCreate()中的findViewById返回为NULL怎么解决
原因:新版SDK的layout文件不是存放在默认的(res/layout/activity_main.xml)文件中,而是存放在(res/layout/fragment_main.xml)文件中。所以要在fragment_main.xml去找对应的ID才会找到
解决方法一:findViewById方法放到OnStart方法中执行
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Button switcher = (Button) findViewById(R.id.switcher);
switcher.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
Toast.makeText(MainActivity.this, "你点了我,快跳第二个activity", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this, SecendActivity.class);
// 传入FirstActivity.this 作为上下文,传入SecondActivity.class作为目标活动
startActivity(intent);
}
});
}