zoukankan      html  css  js  c++  java
  • Android02——Activity

    Activity

    创建空白activity和layout

    创建活动

    public class SecondActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    }
    

    设置布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <Button
        android:id="@+id/button_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="1"/>
    
    </LinearLayout>
    

    如果需要在xml中引用一个id就使用@id/id_name这种语法。而如果你需要在xml中定义一个id则需要使用@+id/id_name这种语法。

    • 加+表示定义
    • 不加+表示引用

    match_parent表示当前元素和父元素一样宽。

    wrap_content表示当前元素的高度只要能刚好包含里面的内容就行。

    在活动中加载布局

    public class SecondActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.sencond_layout);
        }
    }
    

    项目中添加任何资源都会在R文件中生成一个相应的资源id,因此我们刚才创建的sencond_layout.xml布局的id现在应该是已经添加到R文件中了。

    AndroidManfiest文件中注册

    On SDK version 23 and up, your app data will be automatically backed up and restored on app install. Consider adding the attribute android:fullBackupContent to specify an @xml resource which configures which files to backup.

    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
    

    在manfiest中设置为主程序。

    在活动中使用toast

    toast是Android系统中一种消dao息框类型,系统自带Toast采用的是队列的方式, 等当前Toast消失后, 下一个Toast才能显示出来;原因是Toast的管理是在队列中,点击一次,就会产生一个新的Toast,要等这个队列中的Toast处理完,这个显示Toast的任务才算结束。 so~ 我们可以把Toast改成单例模式,没有Toast再新建它,这样也就解决了连续点击Toast,一直在显示的问题。

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.sencond_layout);
            Button button1 =(Button) findViewById(R.id.button_1);
            button1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(SecondActivity.this, "You click button 1", Toast.LENGTH_SHORT).show();
                }
            });
        }
    

    首先通过findViewById()方法获取布局文件中定义的元素,返回的是一个View对象。然后重写button的setOnCLickListener接口的方法。这其实是在为按钮注册一个监听器。

    Toast用于很简单:直接通过静态方法makeTest()传入三个参数,上下文,toast显示的文本内容和显示时长。

    • 首先在res中创建一个menu文件夹

    • 在menu文件夹中创建一个空menu的xml(可以叫main.xml)

    • 在xml中定义添加(@+id)两个item

      <?xml version="1.0" encoding="utf-8"?>
      <menu xmlns:android="http://schemas.android.com/apk/res/android">
          <item android:id="@+id/add_item"
          android:title="Add"/>
          <item android:id="@+id/remove_item"
              android:title="Remove"/>
      
      </menu>
      
    • 在主活动中显示出这个memu

          @Override
          public boolean onCreateOptionsMenu(Menu menu) {
              getMenuInflater().inflate(R.menu.main,menu);
              return super.onCreateOptionsMenu(menu);
          }
      
    • 同时在这个活动类中重写选择方法:

          @Override
          public boolean onOptionsItemSelected(@NonNull MenuItem item) {
              switch (item.getItemId()) {
                  case R.id.add_item:
                      Toast.makeText(this, " You click add", Toast.LENGTH_SHORT).show();
                      break;
                  case R.id.remove_item:
                      Toast.makeText(this, "You click remove", Toast.LENGTH_SHORT).show();
                      break;
                  default:
              }
      //        return super.onOptionsItemSelected(item);
              return true;
          }
      

    销毁一个活动

    通常销毁一个活动的方法就是按back键。当然你也可以通过代码来销毁活动。=>Activity类提供了一个finish()方法。

    Intent

    intent大致可以分为两种:显式Intent和隐式Intent

    显式intent

            button1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(SecondActivity.this, "You click button 1", Toast.LENGTH_SHORT).show();
                    Intent intent = new Intent(SecondActivity.this,FirstActivity.class);
                    startActivity(intent);
    //                finish(); // 自动销毁
                }
            });
    

    当点击按钮1,则会切换到第二个activity去。切换方法是通过创建一个Intent对象,两个参数, 当前活动上下文(content)和目标活动。

    隐式intent

    通过更为抽象的action和category等信息,让系统自己分析这个intent并帮助我们找出合适的activity去启动。

    打开Androidmanifest.xml添加如下代码:

            <activity android:name=".FirstActivity">
                <intent-filter>
                    <action android:name="com.ssozh.activitytest.ACTION_START"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                </intent-filter>
            </activity>
    

    其中的intent-filter 添加了action和category的tag。其中category是默认的。

    Intent intent = new Intent("com.ssozh.activitytest.ACTION_START");
    // 在调用下面方法的时候会自动将android.intent.category.DEFAULT这个category添加到intent中。
    startActivity(intent);
    

    可以看到Intent绑定了action找到了Androidmanifest.xml中相同的activity。同时 startActivity会自动将category添加到这个intent中。

    上面的代码表明,我们想要启动能够响应com.ssozh.activitytest.ACTION_START这个action的活动。

    而如果想给intent提供一个具体的category,则通过对象方法addCategory来实现。

    intent.addCategory("com.ssozh.activitystest.MY_CATEGORY");
    

    同时添加到Androidmanifest.xml中:

                <intent-filter>
                    <action android:name="com.ssozh.activitytest.ACTION_START"/>
    <!--                可以添加多个category?-->
                    <category android:name="android.intent.category.DEFAULT"/>
                    <category android:name="com.ssozh.activitystest.MY_CATEGORY"/>
                </intent-filter>
    

    其他intent用法

    使用隐式intent不仅可以启动自己程序的activity,还可以启动其他程序的活动,这使得Android多个app之间的功能共享称为了可能。比如你想展示一个网页:

            button2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(FirstActivity.this,"打开网页",Toast.LENGTH_SHORT).show();
                    Intent intent =  new Intent(Intent.ACTION_VIEW);
                    intent.setData(Uri.parse("http://www.baidu.com"));
                    startActivity(intent);
                }
            });
    

    首先指定了action的是Intent.ACTION_VIEW,这个是Android系统内置的动作,其常量值为Android.intent.action.VIEW。然后通过Uri.parse方法,将网址字符串解析成一个uri对象,再调用intent的setData()方法将这个uri对象传递出去。

    setData()方法是接收一个uri对象,主要用于指定当前intent正在操作的数据,而这些数据通常都是以字符串的形式传入到URI.parse()方法中去解析的。

    与此相对应,还可以在<intent-filter>tag中再配置一个<data>tag,更加精确的指定当前活动能够响应什么类型的数据。其可以配置以下属性:

    • android:scheme。协议,除了http,还有geo表示地理位置、tel表示拨打电话。
    • androd:host。主机名
    • android:port。端口
    • android:path。路径
    • Android:mineType:用于指定可以处理的数据类型,允许使用通配符的形式进行指定。
    public void onClick(View v) {
        Toast.makeText(FirstActivity.this,"打开网页",Toast.LENGTH_SHORT).show();
        Intent intent =  new Intent(Intent.ACTION_DIAL);
        intent.setData(Uri.parse("tel:10068"));
        startActivity(intent);
    }
    });
    

    首先指定了intent的action是ACTION_DIAL,这又是一个Android系统的内置动作。然后在data部分指定了协议是tel,号码是10086。

    向下一个活动传递数据

    Intent提供了一个方法putExtra(),可以吧我们想要传递的数据暂存在Intent中,启动了另一个活动后,只需要把这些数据再从Intent中取出来就可以了。

    @Override
    public void onClick(View v) {
        Toast.makeText(SecondActivity.this, "You click button 1", Toast.LENGTH_SHORT).show();
        // 显式调用activity
        //                Intent intent = new Intent(SecondActivity.this,FirstActivity.class);
        Intent intent = new Intent("com.ssozh.activitytest.ACTION_START");
        intent.addCategory("com.ssozh.activitystest.MY_CATEGORY");
    
        // 把Second 传递给First
        String data = "second Activity send data to First One.";
        intent.putExtra("Extra Data",data);
    
    
        // 在调用下面方法的时候会自动将android.intent.category.DEFAULT这个category添加到intent中。
        startActivity(intent);
        //                finish(); // 自动销毁
    }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_first);
            Button button2 =(Button) findViewById(R.id.button_2);
    
            // 获取从Second Activity 传递过来的data并使用toast显示出来
            Intent intent = getIntent();
            String data = intent.getStringExtra("Extra Data");
            Log.d("FirstActivity",data);
            Toast.makeText(FirstActivity.this, data,Toast.LENGTH_SHORT).show();
        }
    

    首先可以通过getIntent()方法获取到用于启动SecondActivity的Intent,然后调用getStringExtra()方法,根据key找到value即可。而如果传递是的整形数据,则使用getIntExtra()方法,以此类推。

    返回数据给上一个活动[没懂]

    很显然 传递给下一个活动是通过intent的。而返回给上一个活动则是通过back来完成的。

    但是activity中还有一个startActivityForResult()方法。他接受两个参数 第一个是intent第二个是请求吗,用于在之后的回调中判断数据的来源。我们还是来实战一下,修改FirstActivity中按钮的点击事件,代码如下所示:

    activity的生命周期

    返回栈

    Android是使用task来管理activity的,一个task就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack)。如果我们启动一个新的活动,那么他就会在返回栈中入栈,而当我们按下Back或者finish的时候,处于栈顶的activity就会出栈。

    活动的状态

    每个活动在其生命周期中最多可能会有4个状态。

    • 运行状态:位于栈顶的activity。系统最不愿意回收的就是运行状态的activity。因为这会给用户带来非常差的体验
    • 暂停状态:当一个活动不再位于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。比如 一个dialog站在栈顶,因为可见,系统是不愿意回收的
    • 停止状态:当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是不可靠,可能会被系统回收。
    • 销毁状态:当一个活动从返回栈中移除后就变成了销毁状态。系统会最倾向于回收处于这种状态的活动,从而保证手机内存充足。

    活动的生存期

    activity类定义了7个回调方法,覆盖了生命周期的每个环节:

    • onCreate():活动第一次被创建时调用。这个方法完成初始化操作,包括加载布局、绑定事件等等。
    • onStart():这个方法由不可见变成可见的时候调用。
    • onResume():这个方法在活动准备好和用户进行交互的时候调用。【一定位于栈顶】
    • onPause():这个方法在系统准备去启动或者回复另外一个activity的时候调用。【这个方法执行一定要快、不然会影响新的activity的使用】
    • onStop():这个方法在activity完全不可见的时候调用。和上面的主要区别在于,如果新的activity是一个对话框式的activity,那么onPause方法会执行,而onStop不会。
    • onDestroy():销毁前调用,之后activity变成销毁状态
    • onRestart():这个方法在activity由停止状态变为运行状态之前调用,也就是activity被重启了。

    以上7个方法除了onRestart之外,其他都是两两相对的,这样又可以将activity分为以下是3种生存期:

    • 完整生存期。Activity在onCreate和onDestory之间经历的。其中包含初始化和释放内存的操作。
    • 可见生存期。Activity在onstart和onStop之间经历的。对用户总是可见的,
    • 前台生存期。在onResume和onPause之间经历的。activity是可以和用户进行交互的。

    Activity被回收缓存数据怎么办?

    存在这么一种情况,现在有两个activityA 和B,当在A的基础上启动了B,A进入了停止状态,这个时候如果系统内存不足,就会回收A,然后用户按下back键返回了A,其实还是会正常显示A的,只不过这时并不会执行onRestart而是执行onCreate方法,因为A在这种情况下会被重新创建一次。

    这里有一个重要问题:Activity A中是可能存在临时数据和状态的。所以我们必须解决onCreate缺失缓存数据的问题。因此Activity中提供了一个onSaveInstanceState()回调方法,这个方法可以保证在activity被回收之前一定会被调用,因此可以通过这个方法来解决这个问题。

        @Override
        protected void onSaveInstanceState(@NonNull Bundle outState) {
            super.onSaveInstanceState(outState);
            String tempData = "Something you just typed";
            outState.putString("dataKey", tempData);
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d(TAG,"onCreate");
            setContentView(R.layout.activity_main);
            /**
             * 假设MainActivity被回收
             * 则通过savedInstanceState判断有没有缓存数据
             */
    
            if(savedInstanceState!=null){
                String tempData = savedInstanceState.getString("data_key");
                Log.d(TAG,tempData);
            }
        // ...
        }
    

    很容易发现Bundle保存和取出数据和Intent十分相似。另外,Intent可以结合Bundle一起用于传递数据。首先可以把需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里。到了目标Activity之后,先从Intent中取出Bundle,再从Bundle,再从Bundle中一一取出数据。

    另外,当手机的屏幕发生旋转的死火海,Activity也会经历一个重新粗行间的过程,因而在这种情况下,activity中的数据也会丢失。但是存在更加优雅的解决放哪,在13.2学习。

    Activity的启动模式

    启动模式是一个全新的概念,包括四种:standard、singleTop、singleTask、和singleInstance,可以在AndroidManifest.xml中给<activity>tag指定android:lauchMode属性来选择启动模式。

    standard

    standard是默认启动模式。在standard模式下,每当启动一个新的activity,他就会在返回栈中入栈,并处于栈顶的位置。对于使用standard模式的activity,系统不会在乎这个activity是否已经在返回栈中存在,每次启动都会创建一个该activity的新实例。因此,你点了很多button后,如果退出则需要按很多back。

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d(TAG, this.toString());
            setContentView(R.layout.activity_normal);
            Button button1 =(Button) findViewById(R.id.button1);
            // set的时候 new一个Listener
            button1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // NormalActivity的基础上启动NormalActivity。
                    Intent intent = new Intent(NormalActivity.this, NormalActivity.class);
                    startActivity(intent);
                }
            });
    
        }
    

    singleTop

    实际上在有些情况下,standard并不合理。activity明明已经在栈顶了,为什么再次启动的时候还要创建一个新的activity实例呢?

    所以,可以根据自己的需求修改启动模式,比如singleTop,在启动activity时如果发现返回栈的栈顶已经是该activity,则认为可以直接使用他,不会再创建新的activity。

    设置方法是在manifest.xml

            <activity android:name=".NormalActivity"
                android:launchMode="singleTop" />
    

    这个时候对于栈顶的Activity就只会创建一个实例,对于button这种intent自己调用自己的行为,就不会再创建新的实例了,这个时候也只需要按一下back就可以返回了。

    singleTask

    使用singleTop模式可以很好地解决重复创建栈顶activity的问题,而对于没有处于栈顶的实例还是会创建多个。而当activity的启动模式指定为singleTask,每次启动该activity时,系统首先会在返回栈中检测是否存在该activity的实例,如果发现已经存在则直接使用该实例,并把在这个activity之上的所有其他activity统统出栈,如果没有发现就会创建一个新的activity实例。

    singleInstance【没懂】

    singleInstance模式应该算是4种启动模式中最特殊最复杂的一个了。不同于以上三种模式,指定为singleInstance模式的activity会启动一个新的返回栈来管理这个activity(其实如果singleTask模式下指定了不同的taskAffinity,也会启动一个新的返回栈)。这样做的意义是为了解决多个应用程序共享activity实例的问题。

    Activity的最佳实践

    知晓当前是在哪个活动

    创建一个BaseActivity,然后让所有的activity继承这个BaseActivity。同时修改BaseActiivity的代码如下:

    /**
     * 1. 直接创建一个java class 然后继承extends AppCompatActivity
     * 2. 打印getSimpleName
     * 3. 得知当前运行的activity
     */
    public class BaseActivity extends AppCompatActivity {
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            Log.d("BaseActivity",getClass().getSimpleName());
        }
    }
    
    

    随时随地退出程序

    只需要一个专用的集合对所有的activity进行管理就可以了。

    新建一个单例类,ActivityController作为活动管理器,代码如下:

    import android.app.Activity;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 添加一个Android工具类,用于管理活动, 注意这个是用于管理Android的工具类,
     * 虽然是个java程序但是必须创建在app中而不是javalib中。
     * 另外很显然这个类应该是个单例模式
     * 双重检测的单例模式
     */
    public class ActivityController {
        private static volatile ActivityController singleton;
    
        private final List<Activity> activityList;
        public static ActivityController createActivityController(){
            if(singleton == null){
                synchronized (ActivityController.class){
                    if(singleton == null){
                        singleton =  new ActivityController();
                    }
                }
    
            }
            return singleton;
        }
    
    
        private ActivityController(){
            activityList = new ArrayList<>();
        }
    
    
        public  void addActivity(Activity activity){
            activityList.add(activity);
        }
    
        public  void removeActivity(Activity activity){
            activityList.remove(activity);
        }
    
        public  void finishAll(){
            for(Activity activity:activityList){
                if(!activity.isFinishing()){
                    activity.finish();
                }
            }
        }
    }
    

    启动activity的最佳写法

    启动activity的方法具体步骤:首先通过Intent构建出当前的"意图",然后调用startActivity或startActivityForResult方法将activity启动起来,如果有数据需要在activity之间传递,也可以借助Intent。而如果直接在onCreate中写如下代码:

    Intent intent = new Intent(context, SecondActivity.class);
    intent.putExtra("param1",data1);
    intent.putExtra("param2",data2);
    startActivity(intent);
    

    实际上,这样写会在对接的时候出现问题,我们应该对其进行封装成静态方法,用于说明启动活动需要的数据:

        /**
         *
         * 这个时候别人调用,就可以直接通过
         * NormalActivity.actionStart(context,param1,param2)直接启动本activity了
         */
        public static void actionStart(Context context, String data1, String data2){
            Intent intent = new Intent(context,NormalActivity.class);
            intent.putExtra("param1",data1);
            intent.putExtra("param2",data2);
            context.startActivity(intent);
        }
    }
    
  • 相关阅读:
    Easyui 表格底部加合计
    jQuery设置checkbox 为选中状态
    HTML 列表中的dl,dt,dd,ul,li,ol区别
    jQuery的toggle事件
    EasyUI 的日期控件单击文本框显示日历
    HTML设置span宽度
    JQuery获取与设置select
    生命周期
    钩子函数
    组件
  • 原文地址:https://www.cnblogs.com/SsoZhNO-1/p/13971343.html
Copyright © 2011-2022 走看看