Android App 开发学习第五天:基本程序单元Activity

Activity

  1. Activity概述
    Activity的4种状态:运行状态、暂停状态、停止状态、销毁状态。
    Activity的生命周期
    在这里插入图片描述

    1. 创建、配置、启动和关闭Activity
      创建Activity
      创建继承自Activity的Activity
public class MainActivity extends Activity
	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

启动ActivityActivity分为入口Activity和其他Activity在AndroidManifest.xml设置入口Activity通过startActivity()启动其他Activity

单击按钮启动其他Activity

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = this.findViewById(R.id.button1);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this,DetailActivity.class);
                startActivity(intent);
            }
        });
    }

	关闭Activity
	finish()方法
	刷新Activity
	onCreate(null)
  1. 多个Activity的使用
    使用Bundle在Activity之间交换数据
    什么是Bundle?
    能够存储要传输的数据

    将数据传输给另一个Activity
    过程:新建Bundle对象,将要存储的数据存入该对象中,如putCharSequence(,),将该对象通过Intent传输到其他Activity中,
    intent.putExtras(bundle),在其他Activity类中获取其Itent对象getItent()
    ,通过itent获取传输过来的Bundle对象getExtras(),获取其中存储的数据,bundle.get(),获取要显示的文本框组件,将数据设置进去setText()>

    调用另一个Activity并返回结果
    startActivityForResult(Intent intent,int requestCode),启动一个Activity,并设置请求码。
    过程:新建Intent对象,启动其他Activity(startActivityForResult),
    在另一个Activirt类中,获取itent对象getIntent(),新建Bundle对象,将数据设置设置进该对象中,如putInt(),将该对象通过itent传输,itent.putExtras(),设置返回结果setResult(resultCode,itent)。重写主Activity的 onActivityResult(int requestCode, int resultCode, @Nullable Intent data)方法,判断请求码和结果码是否为自己设置进去的值,若为true,则通过itent.getExtras()获取bundle对象,并通过设定的key值获取存储的数据,将其设置进需要的组件里。

    实现头像选择

activity_main.xml

    <ImageView
        android:id="@+id/imageview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:src="@drawable/icon1"
        />
    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="选择头像"
        android:layout_below="@id/imageview"
        android:gravity="center_horizontal"/>

activity_head.xml

<GridView
        android:id="@+id/gridview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="10dp"
        android:horizontalSpacing="3dp"
        android:verticalSpacing="3dp"/>

MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode ==0x11&&resultCode==0x11){
            Bundle bundle = data.getExtras();
            int imageId = bundle.getInt("imageId");
            ImageView imageView = findViewById(R.id.imageview);
            imageView.setImageResource(imageId);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button =  this.findViewById(R.id.btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, HeadActivitys.class);
                startActivityForResult(intent,0x11);
            }
        });

    }
}

HeadActivity

public class HeadActivitys extends AppCompatActivity {
    private  int[] picture = new int[]{R.drawable.icon1,R.drawable.icon2,R.drawable.icon1
            ,R.drawable.icon1,R.drawable.icon1,R.drawable.icon1};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_head_activitys);
        GridView gridView = this.findViewById(R.id.gridview);
        BaseAdapter adapter = new BaseAdapter() {

            @Override
            public int getCount() {
                return picture.length;
            }

            @Override
            public Object getItem(int position) {
                return position;
            }

            @Override
            public long getItemId(int position) {
                return position;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                ImageView imageView;
                if (convertView ==null){
                    imageView = new ImageView(HeadActivitys.this);
                    imageView.setAdjustViewBounds(true);
                    imageView.setMaxHeight(150);
                    imageView.setMaxWidth(158);
                    imageView.setPadding(5,5,5,5);
                }else {
                    imageView = (ImageView) convertView;
                }
                imageView.setImageResource(picture[position]);
                return imageView;
            }
        };
        gridView.setAdapter(adapter);
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent = getIntent();
                Bundle bundle = new Bundle();
                bundle.putInt("imageId",picture[position]);
                intent.putExtras(bundle);
                setResult(0x11,intent);
                finish();
            }
        });
    }
}
  1. 随时关闭所有Activity
    有时我们可能会打开了很多个Activity,突然来个这样的需求,在某个页面可以关掉 所有的Activity并退出程序!好吧,下面提供一个关闭所有Activity的方法, 就是用一个list集合来存储所有Activity!
    在这里插入图片描述
  2. 完全退出App的方法
/** 
 1. 退出应用程序 
 */  
public void AppExit(Context context) {  
    try {  
        ActivityCollector.finishAll();  
        ActivityManager activityMgr = (ActivityManager) context  
                .getSystemService(Context.ACTIVITY_SERVICE);  
        activityMgr.killBackgroundProcesses(context.getPackageName());  
        System.exit(0);  
    } catch (Exception ignored) {}  
}  
  1. 双击退出程序的两种方法:

1)定义一个变量,来标识是否退出

// 定义一个变量,来标识是否退出
private static boolean isExit = false;
Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        isExit = false;
    }
};

public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        if (!isExit) {
            isExit = true;
            Toast.makeText(getApplicationContext(), "再按一次退出程序",
                    Toast.LENGTH_SHORT).show();
            // 利用handler延迟发送更改状态信息
            mHandler.sendEmptyMessageDelayed(0, 2000);
        } else {
            exit(this);
        }
        return false;
    }
return super.onKeyDown(keyCode, event);}

2)保存点击时间:

//保存点击的时间
private long exitTime = 0;
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        if ((System.currentTimeMillis() - exitTime) > 2000) {
            Toast.makeText(getApplicationContext(), "再按一次退出程序",
                    Toast.LENGTH_SHORT).show();
            exitTime = System.currentTimeMillis();
        } else {
                        exit();
                      }
        return false;
    }
        return super.onKeyDown(keyCode, event);
}
  1. 为Activity设置过场动画

所谓的过场动画就是切换到另外的Activity时加上一些切换动画,比如淡入淡出,放大缩小,左右互推等! 当然,我们并不在这里详细讲解动画,后面有专门的章节来讲解这个,这里只教大家如何去加载动画,另外 给大家提供了一些比较常用的过渡动画,只要将相关动画文件添加到res/anim目录下,然后下述方法二选一 就可以实现Activity的切换动画了!

1)方法一:
在这里插入图片描述

2)方法二:
通过style进行配置,这个是全局的哦,就是所有的Activity都会加载这个动画!

实现代码如下:

①在style.xml中自定义style:

解释:

4个item分别代表:

Activity A跳转到Activity B时Activity B进入动画;
Activity A跳转到Activity B时Activity A退出动画;
Activity B返回Activity A时Activity A的进入动画
Activity B返回Activity A时ActivityB的退出动画
②然后修改下AppTheme:

③最后在appliction设置下:


好的,动画特效就这样duang一声设置好了~

3)其他
好的,除了上面两种方法以外,还可以使用TransitionManager来实现,但是需求版本是API 19以上的, 另外还有一种addOnPreDrawListener的转换动画,这个用起来还是有点麻烦的,可能不是适合初学者 这里也不讲

此图表明,当旋转,平移时,上下变化的起点为图片上部,左右变化的起点为图片右部。
图片上部为Y轴变化的起点,左部为X轴变化的起点

动画文件常用的属性
<scale>标签:缩放
fromXSCale=“1.0” toXScale=“0” 将X轴按比例压缩从1至0。
fromYSCale=“1.0” toYScale=“0” 将Y轴按比例压缩从1至0。
duration=“2000"效果持续时间
pivotX=“50%p” pivotY=“50%p"已屏幕的中心为坐标原点进行缩放
<translate>标签:平移
android:fromXDelta=“100%p” android:toXDelta=“0” 将整个页面从右向左推入
android:fromXDelta=“0” android:toXDelta=”-100%p” 将整个页面从右向左推出
android:fromXDelta=“0” android:toXDelta=“100%p” 将整个页面从左向右推出
android:fromXDelta="-100%p" android:toXDelta=“0” 将整个页面从左向左推入
android:fromYDelta=“100%p” android:toYDelta=“0” 将整个页面从下向上推入
android:fromYDelta=“0” android:toYDelta="-100%p" 将整个页面从下向上推出
android:fromYDelta="-100%p" android:toYDelta=“0” 将整个页面从上往下推入
android:fromYDelta=“0” android:toYDelta=“100%p” 将整个页面从上往下推出
duration=“2000"效果持续时间
<rotate>标签:旋转
android:fromDegrees=“0” android:toDegrees=”+360"
android:pivotX=“50%“android:pivotY=“50%” 以屏幕中心为坐标原点顺时针旋转360°
android:fromDegrees=”-360” android:toDegrees=“0”
android:pivotX=“50%“android:pivotY=“50%” 以屏幕中心为坐标原点顺时针旋转360°
android:fromDegrees=”+360” android:toDegrees=“0”
android:pivotX=“50%“android:pivotY=“50%” 以屏幕中心为坐标原点逆时针旋转360°
android:fromDegrees=“0” android:toDegrees=”-360”
android:pivotX="50%"android:pivotY=“50%” 以屏幕中心为坐标原点逆时针旋转360°
duration="2000"效果持续时间
<alpha>标签:透明度
android:fromAlpha=“1.0” android:toAlpha="0"从不透明到完全透明
duration="2000"效果持续时间

<set xmlns:android="http://schemas.android.com/apk/res/android"
	android:shareInterpolator="false">
	<scale android:interpolator="@android:anim/accelerate_decelerate_interpolator"
		android:fromXScale="0.0" android:toXScale="1.0" android:fromYScale="0.0"
		android:toYScale="1.0" android:pivotX="50%" android:pivotY="50%"
		android:duration="2000"></scale>
	<translate android:interpolator="@android:anim/accelerate_decelerate_interpolator"
		android:fromXDelta="120" android:toXDelta="30" android:fromYDelta="30"
		android:toYDelta="250" android:duration="2000" />
	<rotate android:interpolator="@android:anim/accelerate_decelerate_interpolator"
		android:fromDegrees="0" android:toDegrees="+355" android:pivotX="50%"
		android:pivotY="50%" android:duration="2000" />
	<alpha android:fromAlpha="1.0" android:toAlpha="0"
            android:duration="4000"/>
</set>
  1. 设置Activity全屏的方法:

1)代码隐藏ActionBar
在Activity的onCreate方法中调用getActionBar.hide();即可

2)通过requestWindowFeature设置
requestWindowFeature(Window.FEATURE_NO_TITLE); 该代码需要在setContentView ()之前调用,不然会报错!!!

注: 把 requestWindowFeature(Window.FEATURE_NO_TITLE);放在super.onCreate(savedInstanceState);前面就可以隐藏ActionBar而不报错。

3)通过AndroidManifest.xml的theme
在需要全屏的Activity的标签内设置 theme = @android:style/Theme.NoTitleBar.FullScreen

  1. 定义对话框风格的Activity

在某些情况下,我们可能需要将Activity设置成对话框风格的,Activity一般是占满全屏的, 而Dialog则是占据部分屏幕的!实现起来也很简单!

直接设置下Activity的theme:

android:theme="@android:style/Theme.Dialog" 这样就可以了,当然你可以再设置下标题,小图标!
//设置左上角小图标
requestWindowFeature(Window.FEATURE_LEFT_ICON);
setContentView(R.layout.main);
getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, android.R.drawable.ic_lion_icon);
//设置文字:
setTitle(R.string.actdialog_title);  //XML代码中设置:android:label="@string/activity_dialog"

要使得点击该对话框外的空白区域不会退出的方法
方法一: setFinishOnTouchOutside(false);
方法二:首先在View创建之前设置两个Flag,一个设置窗口为非模式的,这样除窗口外的内容就可以获得touch事件,然后设置窗口外部touch事件发生时的通知。最后重写onTouchEvent,监听窗口外的Touch事件。这样就可以在监听方法中自定义窗口外点击事件的响应,是否关闭窗口或者其他操作。

public class MyActivity extends Activity {

 @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Make us non-modal, so that others can receive touch events.
    getWindow().setFlags(LayoutParams.FLAG_NOT_TOUCH_MODAL, LayoutParams.FLAG_NOT_TOUCH_MODAL);

    // ...but notify us that it happened.
    getWindow().setFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);

    // Note that flag changes must happen *before* the content view is set.
    setContentView(R.layout.my_dialog_view);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    // If we've received a touch notification that the user has touched
    // outside the app, finish the activity.
    if (MotionEvent.ACTION_OUTSIDE == event.getAction()) {
      finish();
      return true;
    }

    // Delegate everything else to Activity.
    return super.onTouchEvent(event);
  }
}
  1. Activity,Task和Back Stack的一些概念

接着我们来了解Android中Activity的管理机制,这就涉及到了两个名词:Task和Back Stack了!

概念解析:

我们的APP一般都是由多个Activity构成的,而在Android中给我们提供了一个Task(任务)的概念, 就是将多个相关的Activity收集起来,然后进行Activity的跳转与返回!当然,这个Task只是一个 frameworker层的概念,而在Android中实现了Task的数据结构就是Back Stack(回退堆栈)! 相信大家对于栈这种数据结构并不陌生,Java中也有个Stack的集合类!栈具有如下特点:

后进先出(LIFO),常用操作入栈(push),出栈(pop),处于最顶部的叫栈顶,最底部叫栈底

而Android中的Stack Stack也具有上述特点,他是这样来管理Activity的:

当切换到新的Activity,那么该Activity会被压入栈中,成为栈顶! 而当用户点击Back	键,栈顶的Activity出栈,紧随其后的Activity来到栈顶!

我们来看下官方文档给出的一个流程图:
在这里插入图片描述

流程解析:
应用程序中存在A1,A2,A3三个activity,当用户在Launcher或Home Screen点击应用程序图标时, 启动主A1,接着A1开启A2,A2开启A3,这时栈中有三个Activity,并且这三个Activity默认在 同一个任务(Task)中,当用户按返回时,弹出A3,栈中只剩A1和A2,再按返回键, 弹出A2,栈中只剩A1,再继续按返回键,弹出A1,任务被移除,即程序退出!

接着在官方文档中又看到了另外两个图,处于好奇,我又看了下解释,然后跟群里的人讨论了下:
在这里插入图片描述

然后总结下了结论:

Task是Activity的集合,是一个概念,实际使用的Back Stack来存储Activity,可以有多个Task,但是 同一时刻只有一个栈在最前面,其他的都在后台!那栈是如何产生的呢?

答:当我们通过主屏幕,点击图标打开一个新的App,此时会创建一个新的Task!举个例子:
我们通过点击通信录APP的图标打开APP,这个时候会新建一个栈1,然后开始把新产生的Activity添加进来,可能我们在通讯录的APP中打开了短信APP的页面,但是此时不会新建一个栈,而是继续添加到栈1中,这是 Android推崇一种用户体验方式,即不同应用程序之间的切换能使用户感觉就像是同一个应用程序, 很连贯的用户体验,官方称其为seamless (无缝衔接)! ——————这个时候假如我们点击Home键,回到主屏幕,此时栈1进入后台,我们可能有下述两种操作:
1)点击菜单键(正方形那个按钮),点击打开刚刚的程序,然后栈1又回到前台了! 又或者我们点击主屏幕上通信录的图标,打开APP,此时也不会创建新的栈,栈1回到前台!
2)如果此时我们点击另一个图标打开一个新的APP,那么此时则会创建一个新的栈	2,栈2就会到前台, 而栈1继续呆在后台;
3) 后面也是这样...以此类推!
  1. Task的管理

1)文档翻译:
好的,继续走文档,从文档中的ManagingTasks开始,大概的翻译如下:

1)文档翻译
继续走文档,从文档中的ManagingTasks开始,翻译如下:

如上面所述,Android会将新成功启动的Activity添加到同一个Task中并且按照以"先进先出"方式管理多个Task 和Back Stack,用户就无需去担心Activites如何与Task任务进行交互又或者它们是如何存在于Back Stack中! 或许,你想改变这种正常的管理方式。比如,你希望你的某个Activity能够在一个新的Task中进行管理; 或者你只想对某个Activity进行实例化,又或者你想在用户离开任务时清理Task中除了根Activity所有Activities。你可以做这些事或者更多,只需要通过修改AndroidManifest.xml中 < activity >的相关属性值或者在代码中通过传递特殊标识的Intent给startActivity( )就可以轻松的实现 对Actvitiy的管理了。

< activity >中我们可以使用的属性如下:

taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
你能用的主要的Intent标志有:

FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP
好的,接下来逐个介绍这些怎么用:

2)taskAffinity和allowTaskReparenting
默认情况下,一个应用程序中的所有activity都有一个Affinity,这让它们属于同一个Task。 你可以理解为是否处于同一个Task的标志,然而,每个Activity可以通过 < activity>中的taskAffinity属性设置单独的Affinity。 不同应用程序中的Activity可以共享同一个Affinity,同一个应用程序中的不同Activity 也可以设置成不同的Affinity。 Affinity属性在2种情况下起作用:

1)当启动 activity的Intent对象包含FLAG_ACTIVITY_NEW_TASK标记: 当传递给startActivity()的Intent对象包含 FLAG_ACTIVITY_NEW_TASK标记时,系统会为需要启动的Activity寻找与当前Activity不同Task。如果要启动的 Activity的Affinity属性与当前所有的Task的Affinity属性都不相同,系统会新建一个带那个Affinity属性的Task,并将要启动的Activity压到新建的Task栈中;否则将Activity压入那个Affinity属性相同的栈中。

2)allowTaskReparenting属性设置为true 如果一个activity的allowTaskReparenting属性为true, 那么它可以从一个Task(Task1)移到另外一个有相同Affinity的Task(Task2)中(Task2带到前台时)。 如果一个.apk文件从用户角度来看包含了多个"应用程序",你可能需要对那些 Activity赋不同的Affinity值。

3)launchMode:
四个可选值,启动模式我们研究的核心,下面再详细讲! 他们分别是:standard(默认),singleTop,singleTask,singleInstance

4)清空栈
当用户长时间离开Task(当前task被转移到后台)时,系统会清除task中栈底Activity外的所有Activity 。这样,当用户返回到Task时,只留下那个task最初始的Activity了。我们可以通过修改下面这些属性来 改变这种行为!

alwaysRetainTaskState: 如果栈底Activity的这个属性被设置为true,上述的情况就不会发生。 Task中的所有activity将被长时间保存。

clearTaskOnLaunch 如果栈底activity的这个属性被设置为true,一旦用户离开Task, 则 Task栈中的Activity将被清空到只剩下栈底activity。这种情况刚好与 alwaysRetainTaskState相反。即使用户只是短暂地离开,task也会返回到初始状态 (只剩下栈底acitivty)。

finishOnTaskLaunch 与clearTaskOnLaunch相似,但它只对单独的activity操 作,而不是整个Task。它可以结束任何Activity,包括栈底的Activity。 当它设置为true时,当前的Activity只在当前会话期间作为Task的一部分存在, 当用户退出Activity再返回时,它将不存在。
  1. Activity的四种加载模式详解:

接下来我们来详细地讲解下四种加载模式: 他们分别是:standard(默认),singleTop,singleTask,singleInstance
先来看看总结图:

在这里插入图片描述

模式详解:

standard模式:
标准启动模式,也是activity的默认启动模式。在这种模式下启动的activity可以被多次实例化,即在同一个任务中可以存在多个activity的实例,每个实例都会处理一个Intent对象。如果Activity A的启动模式为standard,并且A已经启动,在A中再次启动Activity A,即调用startActivity(new Intent(this,A.class)),会在A的上面再次启动一个A的实例,即当前的桟中的状态为A–>A。

singleTop模式:
如果一个以singleTop模式启动的Activity的实例已经存在于任务栈的栈顶, 那么再启动这个Activity时,不会创建新的实例,而是重用位于栈顶的那个实例, 并且会调用该实例的onNewIntent()方法将Intent对象传递到这个实例中。 举例来说,如果A的启动模式为singleTop,并且A的一个实例已经存在于栈顶中, 那么再调用startActivity(new Intent(this,A.class))启动A时, 不会再次创建A的实例,而是重用原来的实例,并且调用原来实例的onNewIntent()方法。 这时任务栈中还是这有一个A的实例。如果以singleTop模式启动的activity的一个实例 已经存在与任务栈中,但是不在栈顶,那么它的行为和standard模式相同,也会创建多个实例。

singleTask模式:
只允许在系统中有一个Activity实例。如果系统中已经有了一个实例, 持有这个实例的任务将移动到顶部,同时intent将被通过onNewIntent()发送。 如果没有,则会创建一个新的Activity并置放在合适的任务中。

singleInstance模式
保证系统无论从哪个Task启动Activity都只会创建一个Activity实例,并将它加入新的Task栈顶 也就是说被该实例启动的其他activity会自动运行于另一个Task中。 当再次启动该activity的实例时,会重用已存在的任务和实例。并且会调用这个实例 的onNewIntent()方法,将Intent实例传递到该实例中。和singleTask相同, 同一时刻在系统中只会存在一个这样的Activity实例。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值