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("你今天外出会有好运");
}
}
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);
}
}
}
点击任意一个
弹出分享界面,不清楚为何会弹出分享界面然后手动选择。选择其他都不能获取到资源,这里只能选择短信,因为intent-filter里写死了。就是不明白为何多此一举,不直接进入短信界面。跟着教程写的,教程是Android4.x。测试的真机是小米5S。
上面的界面,选择短信。进来了,可以看到。内容确实是传给了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:
}
}
}
手动填写
点击发送,确实发送成功!
再看从联系人中选择,短信模板的使用
点击某一个子项后,返回到主界面。此时联系人和短信内容已填充好。
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