图片 5

Android开发中巧用Activity和Fragment

1.Activity的生命周期

1)多个Activity组成Activity栈,当前活动位于栈顶。我们先来看看各种Activity基类的类图:

图片 1

当Activity类定义出来之后,这个Activity何时被实例化、它所包含的方法何时被调用,这些都不是由开发者所决定的,都应该由Android系统来决定。

下面我们来看一下Activity的生命周期:

图片 2

各个生命周期

  • onCreate()
    创建activity时调用, 在此方法中做一些findView,
    变量的初始化,intent的解析等。
  • onStart()
    界面可见状态时回调。
  • onResume()
    获取到了焦点时,回调
  • onPause()
    失去焦点时回调。不宜做太多逻辑处理, 避免影响要启动的activity的时间
  • onStop()
    不可见时回调
  • onDestroy()
    销毁activity时回调
  • onRestart()
    当该activity处于onStop状态,然后又被操作
    让其转变为可见状态,即由完全不可谏状态转变为可见状态时,
    会先执行onRestart()方法,再执行onStart()。
    注意:即使在onRestart()方法中调用finish()方法,
    还是会在随后调用onStart()方法

2.Activity的用法

1)启动、关闭Activity

// 首先需要创建启动的Activity对应的Intent
Intent intent = new Intent(MainActivity.this, TwoActivity.class);

// 启动Activity
startActivity(Intent intent);
startActivityForResult(Intent intent, int requestCode); // requestCode:请求码
//startActivityForResult方法以指定的请求码启动Activity,并通过重写onActivityResult方法获取返回的结果。

// 关闭Activity
finish();
finishActivity(int requestCode);
// finishActivity方法结束以startActivityForResult方法启动的Activity。

2)启动其他Activity并返回结果

当前Activity重写onActivityResult(int requestCode, int resultCode, Intent
intent)
requestCode:请求码(指出该方法是从哪个请求的结果触发的)
resultCode:Activity返回的结果码(指出返回的数据来自于哪个新的Activity)
被启动的Activity需要调用setResult()方法设置处理结果。

实例:

在当前Activity中重写onActivityResult方法

public class MainActivity extends Activity {
    Button bn;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // 获取界面上的组件
        ...
        // 绑定事件监听器
        bn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, TwoActivity.class);
                startActivityForResult(intent, 0); // 0是请求码,用于标识该请求
            }
        });
    }
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        // 处理特定的结果
        if (requestCode == 0 && resultCode == 0) {
            // 取出Intent里的Extras数据
            Bundle data = intent.getExtras();
            // 取出Bundle中的数据
            String result = data.getString("test");
            Toast.makeText(getApplicationContext(), result, 0).show();
        }
    }
}

然后在被启动的TwoActivity中调用setResult()方法设置处理结果

// 获取启动该Activity之前的Activity对应的Intent
Intent intent = getIntent();
intent.putExtra("test", "test");
// 设置该SelectActivity的结果码,并设置结束之后退回的Activity
SelectCityActivity.this.setResult(0, intent);
// 结束TwoActivity
TwoActivity.this.finish();

状态的保存与恢复

  • onSaveInstanceState(Bundle outState)
    此方法会在切换
    状态时,而要切换后的状态很可能被动的回收掉时,为了恢复时能重现之前的现场,做的一些状态保存操作。注意activity中的布局view设置了id的才会保存和恢复。没有设置id,无法恢复
    此方法一定在onStop方法前调用, 但和onPause方法的前后顺序不确定
  • onRestoreInstacneState(Bundle savedInstanceState)
    在此方法中做恢复状态。恢复状态的逻辑也可以写在onCreate方法中,
    但在onCreate方法中做恢复状态,要判断参数 是否为空, 不为空
    表示之前有保存状态。
    而在onRestoreInstanceState方法中不需要判断参数!= null。

3.使用Bundle在Activity之间交换数据

Intent提供了多个重载的方法来“携带”额外的数据,如下:

intent.putExtras(Bundle data); // 向Intent放入数据包
intent.putExtras(String name, Xxx value); // 向Intent中按key-value对的形式放入数据
intent.getExtras(); // 取出Intent中携带的数据包
intent.getXxxExtras(String name); //从Intent中按key取出指定类型的数据

上面方法中Bundle就是一个简单的数据携带包,Intent主要通过Bundle对象来携带数据,Bundle包含多个方法来存取数据:

Bundle bundle = new Bundle(); // 首先创建一个Bundle对象
bundle.putXxx(String key, Xxx data); // 向Bundle中放入数据
bundle.putSerializable(String key, Serializable data); // 向Bundle中放入一个可序列化的对象(即实现了java.io.Serializable接口)
bundle.getXxx(String key); // 从Bundle中取出数据
bundle.getSerializable(String key); // 从Bundle中取出一个可序列化的对象

一些比较有用的方法

  • onPostCreate(Bundle savedInstanceState)
    此方法在activity彻底跑起来后调用,
    因此可以用来获取view的宽高。等一些时机敏感的操作。不过此方法一般不建议重写。此方法是在onResume前调用的。

4.Activity的四种加载模式

配置AndroidManifest.xml中Activity时,可指定android:launchMode属性用于配置该Activity的加载模式,该属性支持4个属性值:
standard:标准模式;
singleTop:栈顶单例模式;
singleTask:栈内单例模式(如果目标Activity已经存在、但没有位于栈顶,系统会把位于该Activity上面的所有Activity移出Task栈,从而使目标Activity转入栈顶);
singleInstance:全局单例模式(新创建Activity将放入新栈,一个栈只包含一个Activity,如果目标Activity已经存在,系统会把该Activity所在Task转到前台显示出来)。

onActivityResult

当调用startActivityForResult(intent,
requestCode)方法启动另一个Activity时,
另一个Activity返回到此activity会携带一个Intent,以及requestCode,
resultCode
onActivityResult(int requestCode, int resultCode, Intent data)

5.Fragment的生命周期

Fragment是AndroiD3.0引入的新API,Fragment代表Activity子模块(Activity片段),Fragment必须嵌入到Activity中使用,Fragment的生命周期受它所在Activity的生命周期的控制。

Fragment可调用getActivity()方法获取它所在Activity;
Activity可调用FragmentManager的findFragmentById()或findFragmentByTag()方法获取Fragment;
在Activity运行过程中,可调用FragmentManager的add()、remove()、replace()方法动态的添加、删除和替换Fragment。

1)我们先来看看各种Fragment基类的类图:

图片 3

2)下面我们来看一下Fragment的生命周期,并和Activity的生命周期做对比:

图片 4 图片 5

setResult

setResult(int resultCode, Intent intent) || setResult(int resultCode)。
设置返回结果和数据, 后一个返回值 数据为null

6.Fragment的用法

1)创建Fragment

创建Fragment通常要实现如下三个方法:
onCreate()、onCreateView()、onPause()

为了控制Fragment显示的组件,通常需要重写onCreateView()方法,该方法返回的View将作为该Fragment显示的View组件。

// 重写该方法,该方法返回的View将作为Fragment显示的组件
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // 加载/res/layout/目录下的fragment.xml布局文件
    View view = inflater.inflate(R.layout.fragment, container, false);
    TextView name = (TextView)view.findViewById(R.id.name));
    ...
    return view;
}

2)将Fragment添加到Activity

将Fragment添加到Activity有如下两种方式:

第1种:在布局文件中使用<fragment.../>元素添加Fragment,<fragment.../>元素的android:name属性指定Fragment的实现类。

第2种:在Java代码中通过FragmentTransaction对象的add()方法来添加Fragment。
Activity的getFragmentManager()方法可返回FragmentManager,FragmentManager对象的beginTransaction()方法即可开启并返回FragmentTransaction对象。

3)如何在Activity中动态的添加、更新、以及删除Fragment呢?

首先需要在MainActivity布局文件中添加FrameLayout(设置id为fl),然后简单创建一个两个Fragment(MyFragment和TwoFragment)如下:

public class MyFragment extends Fragment   {  
    @Override  
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
        return inflater.inflate(R.layout.fragment_my, container, false);  
    }  
}

public class TwoFragment extends Fragment   {  
    @Override  
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
        return inflater.inflate(R.layout.fragment_two, container, false);  
    }  
}

接下来就可以在MainActivity中动态的添加、更新、以及删除Fragment了,MainActivity中调用的方法如下:

// 设置默认的Fragment
FragmentManager fm = getFragmentManager();  
FragmentTransaction transaction = fm.beginTransaction();  
myFragment = new MyFragment();  
transaction.replace(R.id.fl, myFragment);  
transaction.commit();

假设点击某按钮更新Fragment,该按钮点击事件如下:

@Override  
public void onClick(View v) {  
    FragmentManager fm = getFragmentManager();  
    // 开启Fragment事务  
    FragmentTransaction transaction = fm.beginTransaction();  
    twoFragment = new TwoFragment();  
    // 使用当前Fragment的布局替代fl的控件  
    transaction.replace(R.id.fl, twoFragment);  
    // transaction.addToBackStack();  // 将事物添加到back栈,允许用户按BACK按键返回到替换Fragment之前的状态
    // 事务提交  
    transaction.commit();  
}

隐式启动注意事项

必须有action,必须有设置<category android:name="android.intent.category.DEFAULT"/>因为startActivity会默认设置category为default。
匹配activity的规则是,满足Activity的设置的任意一条intent-filter。何谓满足了该intent-filter?就是下面的规则:该intent-filter里,其中多个action和多个data满足一个就行,category必须全部满足。

4种启动模式

  • standard
    标准启动模式, 每一次启动都在任务栈中新生成一个Activity实例置于栈顶
  • singleTop
    如果任务栈顶就是要启动的Activity同类实例。则不生成新实例,而是调用栈顶的Activity的onNewIntent()方法
  • singleTask
    一个任务栈中已经存在要启动的Activity的实例时,
    会将其上的activity全部finish掉, 然后调用onNewIntent()方法
  • singleInstance
    如果系统中已经存在一个拥有该Activity实例的任务栈,
    则直接调用该实例的onNewIntent方法。 否则,会单独新开一个任务栈,
    生成该Activity的实例。

值得注意的坑

当要启动的activity的launchMode为singleTask或者singleInstance时,如果是使用startActivityForResult启动,则会立马执行onActivityResult方法,且收到一个
RESULT_CANCEL的resultCode。背后的原理是两个activity不在同一个任务栈中导致。
因此需要采用广播 或者发送事件的方式代替此方法!

onConfigurationChanged

尤其要注意屏幕旋转问题时的处理。

发表评论

电子邮件地址不会被公开。 必填项已用*标注