Android中的Activity

时间:2022-06-19 19:50:09

Android四大组件

  • 活动(Activity)
  • 广播接收者(BroadCastReceiver)
  • 服务(Service)
  • 内容提供者(Contentprovider)

Activity

先来看AndroidManifest.xml文件,新建工程后默认是下面的样子。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.activitytest">
<!--  android:icon应用程序的图标,android:label应用程序的名称 -->
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

application 标签表示整个应用,可以看到里面有这么几行

<activity android:name=".MainActivity">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />

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

每一个Activity都需要在里面注册,<activity android:name=".MainActivity"></activity>必须是完整路径,因为package属性已经指定过包名了,这里用.表示包名就行了。<intent-filter>标签可选。里面的action指定为MAIN,同时category指定为LAUNCHER,该注册的活动作为应用程序进入后,显示的第一个Activity

android.intent.action.MAIN与android.intent.category.LAUNCHER

如果存在多个activity都声明了android.intent.action.MAIN与android.intent.category.LAUNCHER会出现什么情况呢?将会有多个图标显示在桌面上。

如果这两个属性只指定一个呢?

  • 第一种情况:有MAIN,无LAUNCHER,程序列表中无图标。原因:android.intent.category.LAUNCHER决定应用程序是否显示在程序列表里
  • 第二种情况:无MAIN,有LAUNCHER,程序列表中无图标。原因:android.intent.action.MAIN决定应用程序最先启动的Activity,如果没有Main,则不知启动哪个Activity,故也不会有图标出现

android.intent.action.MAIN与android.intent.category.LAUNCHER必须同时指定才有意义。而且一般只有一个活动需要注册为默认启动。(没有应用是有两个图标可进入同一个应用的)

Intent

隐式Intent

通过指定一组action、data、category等

我想启动另外一个Activity,在这个Activity中进行了如下设置

<activity android:name=".Main2Activity">
  <intent-filter>
    <action android:name="com.example.activitytest.ACTION_START"/>
     <action android:name="com.example.activitytest.ACTION_XXX"/>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.MY_CATEGORY" />
    <data android:mimeType="aa/bb" />
    <data android:scheme="https" />
  </intent-filter>
</activity>

可以看到我们自定义了action、category、和data。我们需要匹配以上属性,才能启动这个Activity。注意,action和category可以指定多个,与其中任意一个匹配就可以。但data则不是匹配一个就行。比如上面匹配了上面的mimeType也是不能启动活动的。

另外,一个活动想要被隐式启动,必须加上<category android:name="android.intent.category.DEFAULT" />, 除了第一个被启动的活动指定为LAUNCHER,若是不加这句,即使匹配了其他所有category,也不会被启动。intent.addCategory这句只要匹配任何一个category就行,如果不指定category,在startActivity时默认使用android.intent.category.DEFAULT

在MainActivity中,写如下代码

// intent.setAction("com.example.activitytest.ACTION_START");
// 下面这句相当于上面那句,匹配了action
Intent intent = new Intent("com.example.activitytest.ACTION_START");
// setData和setType会清除另外一个设置的内容,要想同时设置这俩,使用setDataAndType
//        intent.setData(Uri.parse("https:" + "Www.baidu.com"));
//        intent.setType("aa/bb");
// 匹配了data里面的scheme和mimeType
intent.setDataAndType(Uri.parse("https:" + "Www.baidu.com"), "aa/bb");
// 匹配了category,至此xml配置的所有都匹配完成。才可以启动活动,不然会报错
intent.addCategory("android.intent.category.MY_CATEGORY"); // 如果不指定category也是可以的,startActivity时候默认传入android.intent.category.DEFAULT
startActivity(intent);

如果一个活动配置了多个<intent-filter>呢?那么只需匹配成功任意一个intent-filter里面的全部属性就可以了

显式Intent

通过指定packageContext和类名.class,一般是当前的Activity和类名.class

显式Intent比较便捷,xml只需配置<activity android:name=".Main2Activity"></activity>

// 这句相当于下面的setClass
Intent intent = new Intent(MainActivity.this, Main2Activity.class);
startActivity(intent);

这样就可以启动新的Activity。

显式和隐式Intent区别

  • 显示意图更加安全一些,因为不能直接通过action、category、data等访问到。(压根没指定) ,只能通过上下文来使用。所以一般用来开启自己应用的界面。
  • 隐式意图一般开启系统应用(电话拨号器 短信的发送器等等)的界面,用于应用和应用之间。

一个Intent小例子

输入姓名选择性别,分析今日运势。

主布局,这里选择性别用到了单选框。RadioButton的使用必须搭配着RadioGroup才能实现一个组只能选中一个。想指定某一项默认选中时,那个子项必须填写id属性,才能实现互斥,不然会造成多个单选框被选中。一般说来,每个子项都应该设置id

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.radiogrouptest.MainActivity"
    android:layout_margin="16dp">

    <EditText
        android:id="@+id/et_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入您的姓名" />

    <RadioGroup
        android:orientation="horizontal"
        android:id="@+id/rg_gender"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        >
        <!-- 要实现事先就默认选中一个,该单选框必须有id属性。一般可以将所有RadioButton都设置id -->
        <!-- 多个单选框如果同时指定了id,且设置了checked为true,虽然多个单选框设置默认选中,但他们是互斥的,只以最后一个设置了true的单选框为默认选中 -->
        <!-- 关键就是,一定要设置id -->
        <RadioButton
            android:id="@+id/rb_male"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="男"
            android:checked="true"
            />

        <RadioButton
            android:id="@+id/rb_female"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="女"
            android:checked="true"

            />
    </RadioGroup>

    <Button
        android:id="@+id/bt_guess"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="点我查看今日运势"/>

</LinearLayout>
 

MainActivity

package com.example.radiogrouptest;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private EditText editText;
    private RadioGroup radioGroup;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;

        editText = (EditText) findViewById(R.id.et_name);
        radioGroup = (RadioGroup) findViewById(R.id.rg_gender);
        Button btGuess = (Button) findViewById(R.id.bt_guess);

        btGuess.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 获得输入的名字
                String name = editText.getText().toString().trim();
                if (TextUtils.isEmpty(name)) {
                    Toast.makeText(mContext, "您还没有输入名字", Toast.LENGTH_SHORT).show();
                    // 用户输入了名字才进行下面的处理
                } else {
                    // 得到被选中的哪个单选框的id,默认选中“男”
                    int sexId = radioGroup.getCheckedRadioButtonId();
                    String sex;
                    switch (sexId) {
                        case R.id.rb_male:
                            sex = "男";
                            break;
                        case R.id.rb_female:
                            sex = "女";
                            break;
                        default:
                            sex = "NONE";
                    }

                    Intent intent = new Intent(mContext, Main2Activity.class);
                    // 向下一个活动传递数据
                    intent.putExtra("name", name);
                    intent.putExtra("gender", sex);
                    startActivity(intent);
                }
            }
        });
    }
}
 

另外一个Activity的界面,很简单,都是TextView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.radiogrouptest.Main2Activity"
    android:layout_margin="16dp">

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/tv_gender"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/tv_guess"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>
 

另外一个Activity

package com.example.radiogrouptest;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Main2Activity extends AppCompatActivity {

    private List<String> guessList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        TextView tvName = (TextView) findViewById(R.id.tv_name);
        TextView tvGender = (TextView) findViewById(R.id.tv_gender);
        TextView tvGuess = (TextView) findViewById(R.id.tv_guess);

        initList();
        Random random = new Random();
        // 生成[0, 4),不含4。随机选取一个。
        int index = random.nextInt(4);
        String randomOne = guessList.get(index);

        Intent intent = getIntent();
        String name = intent.getStringExtra("name");
        String sex = intent.getStringExtra("gender");
        tvName.setText(name);
        tvGender.setText(sex);
        tvGuess.setText(randomOne);
    }

    private void initList() {
        guessList.add("你今天给心爱的女生表白会成功");
        guessList.add("你今天和基友们待在一起会更好");
        guessList.add("你今天最好不要出门");
        guessList.add("你今天外出会有好运");
    }
}

Android中的Activity

Android中的Activity

Intent--向下一个活动传递数据

用ListView展示一些备用的短信文本,点击则跳转到短信界面,内容已经填充在sms的body里面了。

先看主布局,就一个listview

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.sendmessagetest.MainActivity">

    <ListView
        android:id="@+id/lv_sms"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </ListView>

</LinearLayout>
 

再看子项布局,只有一个textview用于显示短信文本

<?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"
    >

    <TextView
        android:id="@+id/sms_item"
        android:textSize="20sp"
        android:textColor="#000"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

写个Bean封装短信数据

package com.example.sendmessagetest.bean;

public class MySms {
    public String message;

    public MySms(String message) {
        this.message = message;
    }
}
 

再来看适配器,使用了ArrayAdapter,因为构造方法中传入了List所以也不用重写getCount()方法了。

package com.example.sendmessagetest.adapter;

import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import com.example.sendmessagetest.R;
import com.example.sendmessagetest.bean.MySms;

import java.util.List;

public class SmsArrayAdapter extends ArrayAdapter<MySms> {

    private int resourceId;

    public SmsArrayAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<MySms> objects) {
        super(context, resource, objects);
        resourceId = resource;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        MySms sms = getItem(position);
        View view;
        if(convertView != null){//判断converView是否为空,不为空重新使用
            view = convertView;
        }else{
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        }

        TextView textView = (TextView) view.findViewById(R.id.sms_item);
        textView.setText(sms.message);

        return view;
    }
}

最后来看MainActivity

package com.example.sendmessagetest;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;

import com.example.sendmessagetest.adapter.SmsArrayAdapter;
import com.example.sendmessagetest.bean.MySms;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private Context mContext;
    private List<MySms> smsList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mContext = this;
        // 添加短信数据
        initSms();
        ListView listView = (ListView) findViewById(R.id.lv_sms);
        SmsArrayAdapter arrayAdapter = new SmsArrayAdapter(mContext, R.layout.item, smsList);
        listView.setAdapter(arrayAdapter);
        // 设置子项点击事件,跳转到短信发送界面
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                MySms sms = smsList.get(position);

                /* 过滤器的属性,分别匹配成功就好
                <intent-filter>
                   <action android:name="android.intent.action.SEND" />
                   <category android:name="android.intent.category.DEFAULT" />
                   <data android:mimeType="text/plain" />
               </intent-filter>
               */
                Intent intent = new Intent("android.intent.action.SEND");
                intent.addCategory("android.intent.category.DEFAULT");
                intent.setType("text/plain");
                // 源码里面写了getStringExtra("sms_body"),填充到短信内容主体
                intent.putExtra("sms_body", sms.message);
                startActivity(intent);
            }
        });
    }

    private void initSms() {
        for (int i = 0; i < 10; i++) {
            MySms sms1 = new MySms("走出去玩!");
            smsList.add(sms1);

            MySms sms2 = new MySms("吃饭了没,今晚吃什么?");
            smsList.add(sms2);

            MySms sms3 = new MySms("这周去旅行吧.");
            smsList.add(sms3);

            MySms sms4 = new MySms("我过去找你,开始准备饭菜吧。");
            smsList.add(sms4);
        }
    }
}
 

Android中的Activity

点击任意一个

Android中的Activity

弹出分享界面,不清楚为何会弹出分享界面然后手动选择。选择其他都不能获取到资源,这里只能选择短信,因为intent-filter里写死了。就是不明白为何多此一举,不直接进入短信界面。跟着教程写的,教程是Android4.x。测试的真机是小米5S。

Android中的Activity

上面的界面,选择短信。进来了,可以看到。内容确实是传给了sms_body的。

Intent--向上一个活动传递数据

写一个自定义短信发送的功能。

可以从联系人中选择号码,可以选择短信模板。点击发送就会发送短信。

主布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    tools:context="com.example.smssenddemo.MainActivity">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:id="@+id/et_contact"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="输入手机号"/>
        <Button
            android:id="@+id/bt_select"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="添加"/>
    </LinearLayout>

    <EditText
        android:id="@+id/et_sms"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:gravity="top|start"
        android:hint="在这里输入你的短信内容" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <Button
            android:id="@+id/bt_add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="选择短信模板" />
        <Button
            android:id="@+id/bt_send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:text="发送"/>
    </RelativeLayout>

</LinearLayout>
 

点击添加按钮,就可跳转到联系人界面

联系人布局,这里简化为一个包含姓名和号码的listview。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.smssenddemo.ContactActivity">
    <ListView
        android:id="@+id/lv_contact"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </ListView>
</LinearLayout>

新建bean封装联系人信息。

package com.example.smssenddemo.bean;

public class MyContact {

    public String name;
    public String phone;

    public MyContact(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }
}
 

然后是联系人的子项布局

<?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">
    <TextView
        android:id="@+id/tv_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#000"
        android:layout_weight="1"/>
    <TextView
        android:id="@+id/tv_phone"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#000"
        android:layout_weight="1"/>

</LinearLayout>

联系人listview的适配器

package com.example.smssenddemo.adapter;

import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import com.example.smssenddemo.R;
import com.example.smssenddemo.bean.MyContact;

import java.util.List;

public class MyArrayAdapter extends ArrayAdapter<MyContact> {

    private int resourceId;

    public MyArrayAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<MyContact> objects) {
        super(context, resource, objects);
        resourceId = resource;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        View view;
        if (convertView != null) {
            view = convertView;
        } else {
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        }

        TextView tvName = (TextView) view.findViewById(R.id.tv_name);
        TextView tvPhone = (TextView) view.findViewById(R.id.tv_phone);

        MyContact contact = getItem(position);
        tvName.setText(contact.name);
        tvPhone.setText(contact.phone);

        return view;
    }
}
 

看下联系人Activity,点击了某一项就会销毁活动。返回数据给MainActivity

package com.example.smssenddemo;

import android.content.Context;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;

import com.example.smssenddemo.adapter.MyArrayAdapter;
import com.example.smssenddemo.bean.MyContact;

import java.util.ArrayList;
import java.util.List;

public class ContactActivity extends AppCompatActivity {

    private List<MyContact> contactList = new ArrayList<>();
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact);
        mContext = this;
        // 初始化联系人数据
        initContacts();
        ListView listView = (ListView) findViewById(R.id.lv_contact);
        MyArrayAdapter adapter = new MyArrayAdapter(mContext, R.layout.item, contactList);
        listView.setAdapter(adapter);
        // 子项点击,点击后finish,将被点击子项的数据传给上一个活动
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                MyContact contact = contactList.get(position);
                Intent intent = new Intent();
                // 只需发送号码就行
                intent.putExtra("phone", contact.phone);
                setResult(RESULT_OK, intent);
                // 传递过去后,销毁该活动
                finish();
            }
        });

    }

    private void initContacts() {
        for (int i = 0; i < 30; i++) {
            MyContact contact = new MyContact("小明"+i, "139901234"+i);
            contactList.add(contact);
        }
    }
}
 

接着写短信模板的Activity,还是使用ListView和ArrayAdapter,由于子项布局很简单,只有一个TextView,所以这里也不用继承了,直接使用已有的ArrayAdapter的构造方法。可以传入一个TextView的资源id,以及一个List对象,list对象里面的所有内容会显示到指定的TextView上。注意,该构造方法是适用于仅仅有一个TextView的情况。这个例子刚好符合

ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext, R.layout.sms_item, R.id.tv_sms_body, smsList);
 

SmsBodyActivity

package com.example.smssenddemo;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;

public class SmsBodyActivity extends AppCompatActivity {

    private Context mContext;
    private List<String> smsList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sms_body);
        mContext = this;

        initSms();
        ListView listView = (ListView) findViewById(R.id.lv_sms_body);

        // 不指定textViewId则将整个子项布局(虽然是LinearLayout)转型为TextView,由于这里LinearLayout里只有一个TextView。可以这样写
//        ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext, R.layout.sms_item, smsList);
        // 如果子项布局控件很多,不止有一个TextView或者还有其他控件。就需要指定将List里的内容设置到哪个TextView里面了。这个构造方法只适用于只有一个TextView的简单情况
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext, R.layout.sms_item, R.id.tv_sms_body, smsList);
        listView.setAdapter(adapter);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                String smsBody = smsList.get(position);
                Intent intent = new Intent();
                intent.putExtra("sms_body", smsBody);
                setResult(RESULT_OK, intent);
                // 传递过去后,销毁该活动
                finish();
            }
        });
    }

    private void initSms() {
        for (int i = 0; i < 15; i++) {
            smsList.add("我在开会,请稍后联系"+i);
            smsList.add("我在吃饭,请稍后联系"+i);
            smsList.add("我在忙,请稍后联系"+i);
            smsList.add("我在工作,请稍后联系"+i);
        }
    }
}
 

最后来看MainActivity,由于在Android6.0后发送短信也需要申请运行时权限。所以在一进应用就申请权限。

package com.example.smssenddemo;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.util.List;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText etName;
    private EditText etSmsBody;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;

        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.SEND_SMS}, 1);
        }

        etName = (EditText) findViewById(R.id.et_contact);
        etSmsBody = (EditText) findViewById(R.id.et_sms);
        Button btSelectContact = (Button) findViewById(R.id.bt_select);
        Button btSend = (Button) findViewById(R.id.bt_send);
        Button btAdd = (Button) findViewById(R.id.bt_add);

        btSelectContact.setOnClickListener(this);
        btSend.setOnClickListener(this);
        btAdd.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_select:
                Intent intent = new Intent(mContext, ContactActivity.class);
                //  这个方法期望一个活动销毁后能返回给上个活动。当上个活动销毁,会调用本活动的onActivity
                startActivityForResult(intent, 1);
                break;

            case R.id.bt_add:
                Intent intent2 = new Intent(mContext, SmsBodyActivity.class);
                startActivityForResult(intent2, 2);
                break;

            case R.id.bt_send:
                String number = etName.getText().toString().trim();
                String smsBody = etSmsBody.getText().toString().trim();
                // 获取SmsManager实例
                SmsManager smsManager = SmsManager.getDefault();
//                如果短信内容过过多 发不出去。分条发送
                List<String> divideMessages = smsManager.divideMessage(smsBody);
                for (String div : divideMessages) {
                    // 发送短信数据,第一个参数是目的地,第三个参数是发送的String。其他参数先不用管
                    smsManager.sendTextMessage(number, null, div, null, null);
                }
                break;
            default:
        }
    }

    // 当ContactActivity销毁,会回调上一个活动(本活动)。将那个活动的setResult发送来的intent传给本活动
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            // 上面startActivityForResult(intent, 1);
            case 1:
                if (resultCode == RESULT_OK) {
                    String phone = data.getStringExtra("phone");
                    etName.setText(phone);
                }
                break;
            case 2:
                if (resultCode == RESULT_OK) {
                    String smsBody = data.getStringExtra("sms_body");
                    etSmsBody.setText(smsBody);
                }
                break;
            default:
        }
    }
}
 

手动填写

Android中的Activity

点击发送,确实发送成功!

Android中的Activity

再看从联系人中选择,短信模板的使用

Android中的Activity

Android中的Activity

点击某一个子项后,返回到主界面。此时联系人和短信内容已填充好。

Android中的Activity

Activity生命周期

oncreate //  当Activity第一次启动的时候调用
onDestroy // 当Activity销毁的时候调用
onStrat() // 当Activity变成可见的时候调用
onStop() //  当activity 不可见的时候调用
onResume() //当activity可以获取焦点的时候  当界面的按钮可以被点击了
onPause()//  当失去焦点的时候调用 当按钮不了可以被点击的时候调用
onRestart()//当界面重新启动的时候调用

横竖屏切换

横竖屏切换,生命周期会变化。从onPause -> onStop -> onDestyoy -> onCreate -> onStrat -> onResume先销毁,再重新create。

为了防止横竖屏切换,生命周期会发生变化,可把Activity配置如下。在AndroidManifest.xml里面的activity标签里写

android:screenOrientation="portrait" 表示固定竖屏。

或者这样写

android:configChanges="orientation|keyboardHidden|screenSize"

任务栈

栈--先进后出

打开一个Activity叫进栈,关闭一个Activity出栈

我们操作的Activity永远是任务栈的栈顶的Activity

说应用程序退出了,实际上是指任务栈清空了。

四种启动模式

通过activity的android:launchMode指定

standard(默认)

每启动一个活动,就会处于栈顶。假设一个intent自己开启自己,点击了两次共三个活动,则每次启动都会创建新的实例,要点击三次才能完全退出。

singleTop

还是上面的情况,自己开启自己时候,点击两次,只会开启一个活动(刚进入的拿个),此时返回一次就退出程序了。和上面不同的是,这个模式先检查任务栈的栈顶,若栈顶已经存在这个要开启的activity,不会重新的创建activity实例,而是复用已经存在的activity。保证栈顶如果存在,不会重复创建。

应用场景:浏览器的书签

singleTask

singetask 单一任务栈,在当前任务栈里面只能有一个实例存在。

当开启activity的时候,就去检查在任务栈里面是否有实例已经存在,如果有实例存在就复用这个已经存在的activity,并且把这个activity上面的所有的别的activity都清空,复用这个已经存在的activity。保证整个任务栈里面只有一个实例存在。

举个例子:A指定为singleTask,假如刚进入应用是活动A,点击按钮1进入活动B, 在B中点击按钮2, 回到活动A。回到A的过程,此时B在栈顶,A在B下方。发现A活动已经有实例存在,直接onRestart,同时把A上面的所有活动(这里只有B)销毁,此时A处于栈顶,且栈顶只有一个A了。可以发现这样节约资源。

应用场景:浏览器的activity。如果一个activity的创建需要占用大量的系统资源(cpu,内存)一般配置这个activity为singletask的启动模式。

singleInstance

指定为singleInstance的活动,会为其开启一个单独的任务栈,不管哪个应用程序来访问这个活动,都是用的同一个任务栈。如果你要保证一个activity在整个手机操作系统里面只有一个实例存在,使用singleInstance。

举个例子:ABC三个活动,B指定为singleInstance,进入时为活动A,点击进入B,在点击进入C。此时若返回,不是C -> B -> A的顺序,而是从C -> A -> B,因为A和C是默认模式,在同一个任务栈里面,而B是在单独的任务栈里面,等当前的任务栈清空了,于是显示另外一个返回栈,这时才显示B。

应用场景: 来电页面。


by @sunhaiyu

2017.5.7