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區(qū)別
- 顯示意圖更加安全一些,因為不能直接通過action、category、data等訪問到。(壓根沒指定) ,只能通過上下文來使用。所以一般用來開啟自己應用的界面。
- 隱式意圖一般開啟系統(tǒng)應用(電話撥號器 短信的發(fā)送器等等)的界面,用于應用和應用之間。
一個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);
// 設置子項點擊事件,跳轉到短信發(fā)送界面
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--向上一個活動傳遞數據
寫一個自定義短信發(fā)送的功能。
可以從聯(lián)系人中選擇號碼,可以選擇短信模板。點擊發(fā)送就會發(fā)送短信。
主布局
<?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="發(fā)送"/>
</RelativeLayout>
</LinearLayout>
點擊添加按鈕,就可跳轉到聯(lián)系人界面
聯(lián)系人布局,這里簡化為一個包含姓名和號碼的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封裝聯(lián)系人信息。
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;
}
}
然后是聯(lián)系人的子項布局
<?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>
聯(lián)系人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;
}
}
看下聯(lián)系人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;
// 初始化聯(lián)系人數據
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();
// 只需發(fā)送號碼就行
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("我在開會,請稍后聯(lián)系"+i);
smsList.add("我在吃飯,請稍后聯(lián)系"+i);
smsList.add("我在忙,請稍后聯(lián)系"+i);
smsList.add("我在工作,請稍后聯(lián)系"+i);
}
}
}
最后來看MainActivity,由于在Android6.0后發(fā)送短信也需要申請運行時權限。所以在一進應用就申請權限。
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();
// 如果短信內容過過多 發(fā)不出去。分條發(fā)送
List<String> divideMessages = smsManager.divideMessage(smsBody);
for (String div : divideMessages) {
// 發(fā)送短信數據,第一個參數是目的地,第三個參數是發(fā)送的String。其他參數先不用管
smsManager.sendTextMessage(number, null, div, null, null);
}
break;
default:
}
}
// 當ContactActivity銷毀,會回調上一個活動(本活動)。將那個活動的setResult發(fā)送來的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:
}
}
}
手動填寫
點擊發(fā)送,確實發(fā)送成功!
再看從聯(lián)系人中選擇,短信模板的使用
點擊某一個子項后,返回到主界面。此時聯(lián)系人和短信內容已填充好。
Activity生命周期
oncreate // 當Activity第一次啟動的時候調用
onDestroy // 當Activity銷毀的時候調用
onStrat() // 當Activity變成可見的時候調用
onStop() // 當activity 不可見的時候調用
onResume() //當activity可以獲取焦點的時候 當界面的按鈕可以被點擊了
onPause()// 當失去焦點的時候調用 當按鈕不了可以被點擊的時候調用
onRestart()//當界面重新啟動的時候調用
橫豎屏切換
橫豎屏切換,生命周期會變化。從onPause -> onStop -> onDestyoy -> onCreate -> onStrat -> onResume
,先銷毀,再重新create。
為了防止橫豎屏切換,生命周期會發(fā)生變化,可把Activity配置如下。在AndroidManifest.xml里面的activity標簽里寫
android:screenOrientation="portrait"
表示固定豎屏。
或者這樣寫
android:configChanges="orientation|keyboardHidden|screenSize"
任務棧
棧--先進后出
打開一個Activity叫進棧,關閉一個Activity出棧
我們操作的Activity永遠是任務棧的棧頂的Activity
說應用程序退出了,實際上是指任務棧清空了。
四種啟動模式
通過activity的android:launchMode
指定
standard(默認)
每啟動一個活動,就會處于棧頂。假設一個intent自己開啟自己,點擊了兩次共三個活動,則每次啟動都會創(chuàng)建新的實例,要點擊三次才能完全退出。
singleTop
還是上面的情況,自己開啟自己時候,點擊兩次,只會開啟一個活動(剛進入的拿個),此時返回一次就退出程序了。和上面不同的是,這個模式先檢查任務棧的棧頂,若棧頂已經存在這個要開啟的activity,不會重新的創(chuàng)建activity實例,而是復用已經存在的activity。保證棧頂如果存在,不會重復創(chuàng)建。
應用場景:瀏覽器的書簽
singleTask
singetask 單一任務棧,在當前任務棧里面只能有一個實例存在。
當開啟activity的時候,就去檢查在任務棧里面是否有實例已經存在,如果有實例存在就復用這個已經存在的activity,并且把這個activity上面的所有的別的activity都清空,復用這個已經存在的activity。保證整個任務棧里面只有一個實例存在。
舉個例子:A指定為singleTask,假如剛進入應用是活動A,點擊按鈕1進入活動B, 在B中點擊按鈕2, 回到活動A。回到A的過程,此時B在棧頂,A在B下方。發(fā)現A活動已經有實例存在,直接onRestart,同時把A上面的所有活動(這里只有B)銷毀,此時A處于棧頂,且棧頂只有一個A了。可以發(fā)現這樣節(jié)約資源。
應用場景:瀏覽器的activity。如果一個activity的創(chuàng)建需要占用大量的系統(tǒng)資源(cpu,內存)一般配置這個activity為singletask的啟動模式。
singleInstance
指定為singleInstance的活動,會為其開啟一個單獨的任務棧,不管哪個應用程序來訪問這個活動,都是用的同一個任務棧。如果你要保證一個activity在整個手機操作系統(tǒng)里面只有一個實例存在,使用singleInstance。
舉個例子:ABC三個活動,B指定為singleInstance,進入時為活動A,點擊進入B,在點擊進入C。此時若返回,不是C -> B -> A的順序,而是從C -> A -> B,因為A和C是默認模式,在同一個任務棧里面,而B是在單獨的任務棧里面,等當前的任務棧清空了,于是顯示另外一個返回棧,這時才顯示B。
應用場景: 來電頁面。
by @sunhaiyu
2017.5.7