• Android第一行代码(第二版)读书笔记1


    第二章 探究活动

    2.2 活动的基本用法

    手工建立Activity

    新建项目时选择No Activity,在res文件夹下新建layout.在新建Activity选择Empty Activity.不要勾选Generate layout file,后续使用手工关联layout

    • 建立Resource Layout
    • 建立Activity类(Empty Activity),重写onCreate方法,并调用Layout
    • 在AndroidManifest中注册Activity
      AndroidManifest中内容如下:
     <activity android:name=".FirstActivity"
        android:label="This is the FirstActivity">
         <intent-filter>
             <action android:name="android.intent.action.MAIN"></action>
             <category android:name="android.intent.category.LAUNCHER"></category>
         </intent-filter>
     </activity>
    

    FirstActivity.java如下

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

    布局略

    Toast

    Toast是Android系统中的提醒方式,程序可以将短小的消息通知给用户并在一段时间后自动消失.弹出Toast的方式如下,修改FirstActivity.java

    public class FirstActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.first_layout);
            Button button1 = findViewById(R.id.button1);
            button1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(FirstActivity.this,"You click Button 1",Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
    

    如上所示,findViewById()返回button对象,通过setOnClickListener()注册一个监听器,并执行onClick方法.
    Toast通过makeText方法创建Toast对象,并调用show()显示出来.makeText方法的三个参数分别为:当前的Context;消息内容;显示时间的长短.

    在res下新建Android Resource Directory,选择menu,并其名称为menu.在menu下新建Menu Resource File,起名为main.xml.在main.xml中定义Menu的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>
        <item
            android:id="@+id/remove_item" android:title="Remove"></item>
    </menu>
    

    在FirstActivity.java中重写onCreateOptionsMenu()方法,

      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
           getMenuInflater().inflate(R.menu.main,menu);
    //        return super.onCreateOptionsMenu(menu);
           return true;
       }
    

    通过getMenuInfater()方法可以获取MenuInflater对象,通过inflate方法给当前资源创建菜单.inflate方法中第一个参数是获取资源文件中的menu定义,第二个参数指定菜单项目将添加到哪个menu对象中.返回true标识允许菜单被显示出来.
    初始化菜单后,需要为每个菜单定义响应事件,此时需要重写onOptionsItemSelected()方法,在FirstActivity中重写此方法,代码如下:

      @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;
       }
    

    此时重新编译后,点击右上角菜单即可看到响应结果.完整的FirstActivity.java代码如下:

    public class FirstActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.first_layout);
            Button button1 = findViewById(R.id.button1);
            button1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(FirstActivity.this,"You click Button 1",Toast.LENGTH_SHORT).show();
                }
            });
        }
    
        @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;
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.main,menu);
    //        return super.onCreateOptionsMenu(menu);
            return true;
        }
    }
    

    销毁活动

    在终端可以使用Back键来销毁当前活动,也可以使用Activity类中的finish()方法来销毁活动.只需要在监听事件或其他需要的地方调用finish()方法即可.在FirstActivity.java中添加一个按钮并监听click,调用finish().在布局中增加一个Button,id为button1Finish.
    在onCreate中增加代码如下:

     Button button1Finish = findViewById(R.id.button1Finish);
     button1Finish.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               finish();
               Log.d("FirstActivity","Finish--");
           }
       });
    

    2.3 使用Intent在活动间穿梭

    显式Intent

    Intent 是一个消息传递对象,您可以用来从其他应用组件请求操作.尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:

    • 启动 Activity
    • 启动服务
    • 广播传递

    Intent大致可分两类:显示Intent和隐式Intent.

    按照之前的FirstActivity方式建立SecondActivity.java及响应的布局文件,布局文件起名为second_layout.注意不要勾选Launcher Activity选项.替换second_layout.xml文件内容如下:

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

    此时androidManifest文件中会自动注册SecondActivity,修改FirstActivity中的button1的监听,如下

     @Override
     public void onClick(View view) {
          Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
          startActivity(intent);
      }
    

    这里首先构建出了一个Intent,传入FirstActivity.this作为上下文,传入SecondActivity.class作为活动目标,通过startActivity来执行Intent.

    隐式Intent

    隐式需要在androidManifest中对应的activity定义intent-filter标签内指定响应的action与category,首先在SecondActivity的activity标签中声明一个action和category,如下所示:

     <activity android:name=".SecondActivity">
         <intent-filter>
             <action android:name="com.example.secondeActivity.ACTION_START1"></action>
             <category android:name="android.intent.category.DEFAULT"></category>
         </intent-filter>
     </activity>
    

    修改FirstActivity中Button的监听,如下

    button1.setOnClickListener(new View.OnClickListener() {
      @Override
         public void onClick(View view) {
             Intent intent1 = new Intent("com.example.secondeActivity.ACTION_START1");
             startActivity(intent1);
         }
     });
    

    在使用隐式Intent时,必须action与category同时匹配上才会响应,由于android.intent.category.DEFAULT时一种默认的category,所以上面例子中在调用时没有指定category依然能够运行.

    修改FirstActivity中Button的监听,如下

     button1.setOnClickListener(new View.OnClickListener() {
        @Override
         public void onClick(View view) {
             Intent intent1 = new Intent("com.example.secondeActivity.ACTION_START1");
             intent1.addCategory("com.example.activityIntentCategory.Category1");
             startActivity(intent1);
         }
     });
    

    此时因为在androidManifest中SecondActivity的activity没有指定com.example.activityIntentCategory.Category1,所以此时代码运行会报错,提示No Activity found to handle Intent.修改SecondActivity的activity中的intent-filter如下:

    <activity android:name=".SecondActivity">
      <intent-filter>
            <action android:name="com.example.secondeActivity.ACTION_START1"></action>
            <category android:name="android.intent.category.DEFAULT"></category>
            <category android:name="com.example.activityIntentCategory.Category1"></category>
        </intent-filter>
    </activity>
    

    此时在进行调用就一切正常了

    更多隐式Intent

    在Intent中启动系统浏览器.在SecondActivity.java的Button中增加监听,调用系统的浏览器,代码如下:

     Button button2 = findViewById(R.id.button2);
     button2.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               Intent intent = new Intent(Intent.ACTION_VIEW);
               intent.setData(Uri.parse("http://baidu.com"));
               startActivity(intent);
           }
       });
    

    这里的Intent.ACTION_VIEW是安卓的内置动作,对应的常量如下

     public static final String ACTION_VIEW = "android.intent.action.VIEW";
    

    Uri.parse("http://baidu.com")将网址的字符串解析成Uri对象,通过setData方法传递.也可以在intent-filter标签中配置一个data标签,用来更精确的指定当前活动能够响应什么类型数据.data标签主要配置的内容如下:

    • android:scheme .用来指定数据的协议
    • android:host .用来指定数据的主机部分,如www.xxx.xx
    • android:prod .用来指定数据的端口
    • android:path .用来指定主机名和端口之后的内容
    • android:mimeType .用来指定处理的数据类型,可以使用通配符
    <data android:scheme="string"
              android:host="string"
              android:port="string"
              android:path="string"
              android:pathPattern="string"
              android:pathPrefix="string"
              android:mimeType="string" />
    

    需要注意的是向 Intent 过滤器添加数据规范.该规范可以是只有数据类型(mimeType 属性),可以是只有 URI,也可以是既有数据类型又有 URI.URI 由其各个部分的单独属性指定:

    <scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]
    

    用于指定网址格式的以下属性是可选的,但也相互依赖:

    • 如果没有为 Intent 过滤器指定 scheme,则系统会忽略其他所有 URI 属性.
    • 如果没有为过滤器指定 host,则系统会忽略 port 属性以及所有路径属性.

    比如,如果需要对网址的port过滤,则必须有sheme与host属性的配置.

    建立一个ThirdActivity.java来测试指定http类型是数据响应.新建Empty Activity起名为ThirdActivity,对应生成的布局起名为third_layout.其中布局内容如下:

    <?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"
        android:orientation="vertical">
        <Button
            android:id="@+id/button3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="BUTTON 3" />
    </LinearLayout>
    

    AndroidManifest.xml中修改ThirdActivity的注册信息,内容如下:

     <activity android:name=".ThridActivity">
        <intent-filter tools:ignore="AppLinkUrlError">
             <action android:name="android.intent.action.VIEW"></action>
             <category android:name="android.intent.category.DEFAULT"></category>
             <data android:scheme="http"/>
         </intent-filter>
     </activity>
    

    这里配置了intent-filter,指定的Intent能够响应的action为Intent.ACTION_VIEW,并指定默认的category.在配置此处是,开发工具(Android Studio提示需要增加tools:ignore="AppLinkUrlError")

    此时重新运行程序,系统自动弹出列表,显示能够响应这个Intent(Intent.ACTION_VIEW)的所有程序.所以.如果选择使用浏览器打开,则此时会默认启动系统内置浏览器.如果选择使用ThirdActivity来响应Intent,虽然可以响应这个活动,但并不会加载任何网页.在上述配置中,可以将data标签中android:scheme的值就改为ftp,此时ThirdActivity则不会响应这个Intent.

    测试网址的过滤,将AndroidManifest.xml中修改ThirdActivity的注册信息,内容如下:

      <activity android:name=".ThridActivity">
         <intent-filter tools:ignore="AppLinkUrlError">
               <action android:name="android.intent.action.VIEW"></action>
               <category android:name="android.intent.category.DEFAULT"></category>
               <data android:scheme="http" android:host="www.baidu.com" android:port="9999"/>
           </intent-filter>
       </activity>
    

    在SecondActivity.java中将button修改如下:

     button2.setOnClickListener(new View.OnClickListener() {
      @Override
           public void onClick(View view) {
               Intent intent = new Intent(Intent.ACTION_VIEW);
               intent.setData(Uri.parse("http://baidu.com:80"));
               startActivity(intent);
           }
       });
    

    此时,ThirdActivity不会对此Intent进行响应.

    调用拨号的Intent示例,按钮监听部分如下:

     button.setOnClickListener(new View.OnClickListener() {
        @Override
         public void onClick(View view) {
             Intent intent = new Intent(Intent.ACTION_DIAL);
             intent.setData(Uri.parse("tel:10086"));
             startActivity(intent);
         }
     });
    

    通用常见的Intent在官网文档中有具体的描述

    向下一个活动传递数据

    在Intent中提供了一些列的putExtra()方法的重载,可以将传递的数据放到Intent中.修改FirstActivity中Button的监听方法,使用Intent显示的方式传递数据.代码如下:

     button1.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
           String data = "Hello";
           Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
           intent.putExtra("extra_data",data);
           startActivity(intent);
       }
    });
    

    在SecondActivity中使用getIntent()方法来获取Intent并将传递的值取出来,代码如下:

     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.second_layout);
         Intent intent = getIntent();
         String data = intent.getStringExtra("extra_data");
         Log.d("SecondActivity",data);
     }
    

    返回数据给上一个活动

    在Activity中使用startActivityForResult()方法可以在活动销毁时返回一个结果给上一个活动,此方法接受两个参数,第一个参数是Intent,第二个参数是请求码(requestCode),修改FirstActivity中的监听,使用startActivityForResult方法来启动SecondActivity,代码如下:

     button1.setOnClickListener(new View.OnClickListener() {
       @Override
        public void onClick(View view) {
    //      下一个活动返回数据
            Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
            startActivityForResult(intent, 1);
        }
    });
    

    在SecondActivity中,修改监听方法,创建一个新的Intent并设置返回值,代码如下:

    button2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent1  = new Intent();
            intent1.putExtra("data_return","Hello FirstActivity This is Second Activity");
            setResult(RESULT_OK,intent1);
            finish();
        }
    });
    

    上面代码中,先创建了一个Intent来传递数据,然后调用setResult方法来返回处理结果,setResult()方法的第一个参数用于向上返回处理结果,一般使用RESULT_OK或RESULT_CANCELED这两个值,第二个参数是将Intent传递回去,最后调用finish方法销毁活动.
    在FirstActivity中,需要重写onActivityResult方法来获取返回的数据,代码如下:

     @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case 1:
                if (resultCode == RESULT_OK) {
                    String returnData = data.getStringExtra("data_return");
                    Log.d("FirstActivity", returnData);
                }
    
                break;
            default:
        }
    }
    

    onActivityResult中的三个参数,其中requestCode为startActivityForResult()方法带过来的唯一键,resultCode为SecondActivity中setResult方法设置的返回值,Intent为SecondActivity中返回的Intent.此时如果用户使用返回按钮来结束活动时,需要在SecondActivity中重写onBackPassed()方法来解决此问题,并通过onBackPassed()方法中来返回数据,修改SecondActivity中代码,增加onBackPassed方法:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case 1:
                if (resultCode == RESULT_OK) {
                    String returnData = data.getStringExtra("data_return");
                    Log.d("FirstActivity", returnData);
                }
                break;
            default:
        }
    }
    

    2.4 活动的生命周期

    2.4.1 返回栈

    Android使用任务Task来管理活动,一个任务是一组存放在栈里的活动集合,这个集合叫做返回栈.档一个活动入栈后,调用finish方法后最先出栈.

    2.4.2 活动状态

    • 运行状态:此时活动处于栈顶
    • 暂停状态:活动不处于栈顶,当内存极低情况下,系统才会考虑回收
    • 停滞状态:活动不处于栈顶,完全不可见,进入停止状态,可能被系统回收
    • 销毁状态:活动出栈,系统回收

    2.4.3 活动的生命周期

    • onCreate() 活动第一次被创建时候调用,所有的初始化操作在此方法内完成
    • onStart() 活动由不可见到可见时被调用
    • onResume() 方法在活动准备好和用户进行交互时候调用,此时活动一定处于返回栈的栈顶,并处于运行状态
    • onPause() 方法在系统准备去启动或者恢复另一个活动时候调用.通常在这个方法中将一些消耗CPU的资源释放掉并保留一些关键数据,但此方法执行速度要快,不然会影响新的栈顶的活动.
    • onStop() 方法在活动完全不可见时候调用,和onPause()方法的区别是如果启动的新活动是一个对话框式活动,那么onPause()方法会被调用,而onStop()方法不会被执行
    • onDestroy() 方法在活动被销毁前调用,之后活动变为销毁状态
    • onRestart() 方法在活动由停止变为运行之前调用,也就是活动被重新启用了
      以上7个方法中,除了onRestart()方法,其他的都是两两相对的,可分为3种生存期:
    • 完全生存期.活动在onCreate()和onDestroy()之间经历的
    • 可见生存期.活动在onStart()和onStop()之间经历的
    • 前台生存期.活动在onResume()和onPause()之间经历的

    2.4.5 活动被回收

    onSaveInstanceState()方法会携带一个Bundle类型的参数,Bundle提供一列的方法保存数据,比如在MainActivity中重写此方法,将参数通过Bundle的putString()方法将值保存.MainActivity中代码如下

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        String tempData = "Temp Data";
        outState.putString("temp_key",tempData);
    }
    

    在onCreate时获取如下:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState!=null){
            String temp = savedInstanceState.getString("temp_key");
            Log.d(TAG,temp);
        }
    }
    

    2.6 活动的最佳实践

    参考书中例子,完成BaseActivity类及ActivityCollector类,实现获取当前活动及销毁所有活动.
    BaseActivity.java

    public class BaseActivity extends AppCompatActivity {
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d("BaseActivity",getClass().getSimpleName());
            ActivityCollector.addActivity(this);
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            ActivityCollector.removeActivity(this);
        }
    }
    

    ActivityCollector.java

    public class ActivityCollector {
        public static List<Activity> activityList = new ArrayList<>();
    
        public static void addActivity(Activity activity) {
            activityList.add(activity);
        }
    
        public static void removeActivity(Activity activity) {
            activityList.remove(activity);
        }
    
        public static void finishAll() {
            for (Activity activity : activityList) {
                if (!activity.isFinishing()) {
                    activity.finish();
                }
            }
        }
    }
    

    个人测试的Demo地址均放在了github上地址链接

  • 相关阅读:
    关于发现宇宙微波背景(CMB)辐射的一则趣闻
    windows 8,关闭随意窗体都提示“已停止工作”的解决的方法
    非洲小孩
    Android自己定义控件背景及其Drawable以实现扁平化
    POJ2533:Longest Ordered Subsequence
    iOS Dev (63) 怎样在 TableView 滚动时收起键盘?
    自己用c语言做的日历
    time .h 的用法
    动态规划--目标和问题
    Linux shell编程学习笔记---第八章
  • 原文地址:https://www.cnblogs.com/GYoungBean/p/13204049.html
Copyright © 2020-2023  润新知