Android筆記
IDE:Android Studio
文章目錄
新建工程
詳解項目中的文件資源
- build
這個目錄和外層的build 目錄類似,主要也是包含了一些在編譯時自動生成的文件,不過它裏面的內容會更多更雜,我們不需要過多關心。 - libs
如果你的項目中使用到了第三方jar 包,就需要把這些jar 包都放在libs 目錄下,放在這個目錄下的jar 包都會被自動添加到構建路徑裏去。 - androidTest
此處是用來編寫Android Test 測試用例的,可以對項目進行一些自動化測試。 - java
毫無疑問, java 目錄是放置我們所有Java 代碼的地方,展開該目錄,你將看到我們剛纔創建的HelloWorldActivity 文件就在裏面。 - res
這個目錄下的內容就有點多了。簡單點說就是你在項目中使用到的所有圖片、佈局、字符串等資源都要存放在這個目錄下。當然這個目錄下還有很多子目錄,圖片放在drawable 目錄下,佈局放在layout 目錄下,字符串放在values 目錄下,所以你不用擔心會把整個m 目錄弄得亂糟糟的。 - AndroidManifest.xml
這是你整個Android 項目的配置文件,你在程序中定義的所有四大組件都需要在這個文件裏註冊,另外還可以在這個文件中給應用程序添加權限聲明。由於這個文件以後會經常用到,我們用到的時候再做詳細說明。 - test
此處是用來編寫Unit Test 測試用例的,是對項目進行自動化測試的另一種方式。 - gitignore
這個文件用於將叩p 模塊內的指定的目錄或文件排除在版本控制之外,作用和外層的.gitignore 文件類似。 - app.iml
IntelliJ IDEA 項目自動生成的文件,我們不需要關心或修改這個文件中的內容。 - build.gradle
這是app 模塊的gradle 構建腳本,這個文件中會指定很多項目構建相關的配置,我們稍後將會詳細分析gradle 構建腳本中的具體內容。 - proguard-rules.pro
這個文件用於指定項目代碼的混淆規則,當代碼開發完成後打成安裝包文件,如果不希望代碼被別人破解,通常會將代碼進行混淆,從而讓破解者難以閱讀。
詳解res目錄文件資源
所有以drawable開頭的文件夾都是用來放圖片的;
所有以mipmap開頭的文件夾都是用來放應用圖標的
所有以values 開頭的文件夾都是用來放字符串、樣式、顏色等配置的;
layout 文件夾是用來放佈局文件的;
之所以有這麼多mipmap開頭的文件夾,其實主要是爲了讓程序能夠更好地兼容各種設備。drawable 文件夾也是相同的道理,應該創建drawable-hdpi 、drawable-xhdpi 、drawable-xxhdpi 等文件夾。在製作程序的時候最好能夠給同一張圖片提供幾個不同分辨率的版本,分別放在這些文件夾下,然後當程序運行的時候,會自動根據當前運行設備分辨率的高低選擇加載哪個文件夾下的圖片。當然這只是理想情況,更多的時候美工只會提供給我們一份圖片,這時你就把所有圖片都放在drawable-xxhdpi 文件夾下就好了。
引用res目錄下的資源
在代碼中通過R.string.hello_world 可以獲得該宇符串的引用。
在XML 中通過@string/hello_world 可以獲得該字符串的引用。
其中string 部分是可以替換的,如果是引用的圖片資源就可以替換成drawable ,如果是引用的應用圖標就可以替換成mipmap ,如果是引用的佈局文件
就可以替換成layout ,以此類推。
在活動中使用toast
通過findViewByid ()方法獲取到在佈局文件中定義的元素,返回一個View 對象,需要向下轉型將它轉成Button 對象。
通過調用setOnClicklistener()方法爲按鈕註冊一個監聽器,點擊按鈕時就會執行監聽器中的onClick()方法。
通過靜態方法mnakeText()創建出一個Toast 對象,然後調用show()將Toast 顯示出來就可以了。
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "Toast test", Toast.LENGTH_SHORT).show();
}
});
#### 在活動中使用menu
首先在res 目錄下新建→個menu 文件夾,右擊res 目錄→New→Directory ,輸入文件夾名menu ,點擊OK。接着在這個文件夾下再新建一個名叫main 的菜單文件,右擊menu 文件夾→New→Menu resource file。
在main.xml 中添加如下代碼,其中標籤用來創建具體的一個菜單項:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/add_item"
android:title="Add"/>
<item
android:id="@+id/remove_item"
android:title="Remove"/>
</menu>
接着重新回到FirstActivity 中來重寫onCreateOptionsMenu()、onOptionsItemSelected()方法,重寫方法可以使用Ctrl+O 快捷鍵
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.add_item:
Toast.makeText(this, "ADD", Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(this, "REMOVE", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
return true;
}
銷燬當前活動
按一下Back()鍵,或使用:
finish();
Java中的Context上下文
簡單理解Context
“環境”、“容器”,承上啓下。當前對象在程序中所處的一個環境,一個與系統交互的過程。比如微信聊天,此時的“環境”是指聊天的界面以及相關的數據請求與傳輸,Context在加載資源、啓動Activity、獲取系統服務、創建View等操作都要參與。
在編程術語中,它是對當前工作單元的行爲有任何影響的較大的周邊部分。例如所使用的運行環境、環境變量、實例變量、局部變量、其他類的狀態、當前環境的狀態等等。
在一些API中,你會在接口/類中看到這個名稱,例如Servlet的ServletContext、JSF的FacesContext、Spring的ApplicationContext、Android的Context、JNDI的InitialContext等等。它們通常都遵循Facade模式,該模式讓最終用戶不需要了解環境細節抽象到單個接口/類中。
源碼中的Context
Context提供了關於應用環境全局信息的接口。它是一個抽象類,它的執行被Android系統所提供。它允許獲取以應用爲特徵的資源和類型,是一個統領一些資源(應用程序環境變量等)的上下文。就是說,它描述一個應用程序環境的信息(即上下文);是一個抽象類,Android提供了該抽象類的具體實現類;通過它我們可以獲取應用程序的資源和類(包括應用級別操作,如啓動Activity,發廣播,接受Intent等)。既然上面Context是一個抽象類,那麼肯定有他的實現類,我們在Context的源碼中通過IDE可以查看到他的子類最終可以得到如下關係圖:
Context能幹什麼
Context到底可以實現哪些功能呢?這個就實在是太多了,彈出Toast、啓動Activity、啓動Service、發送廣播、操作數據庫等等都需要用到Context。
Context作用域
Intent的使用
使用顯示Intent
首先構建出了一個Intent ,傳人FirstActivity.this 作爲上下文,傳入SecondActivity.class 作爲目標活動,這樣我們的“意圖”就非常明顯了,即在FirstActivity 這個活動的基礎上打開SecondActivity 這個活動。然後通過startActivity()方法來執行這個Intent。
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
}
使用隱式Intent
通過在標籤下配置的內容,可以指定當前活動能夠響應的action 和category ,打開AndroidManifest.xml ,添加如下代碼:
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="com.sxf.activitytest.ACTION_START"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
在標籤中我們指明瞭當前活動可以響應com.sxf.activitytest.ACTION_START 這個action ,而標籤則包含了一些附加信息,更精確地指明瞭當前的活動能夠響應的Intent 中還可能帶有的category 。只有和中的內容同時能夠匹配上Intent 中指定的action 和category 時,這個活動才能響應該Intent 。
修改 FirstActivity 中按鈕的點擊事件,代碼如下所示:
@Override
public void onClick(View v) {
Intent intent = new Intent("com.sxf.activitytest.ACTION_START");
startActivity(intent);
}
android.intent.category.DEFAULT 是-種默認的 category ,在調用 startActivity ()方法的時候會自動將這個 category 添加到 Intent 中。
每個 Intent 中只能指定一個 action ,但卻能指定多個category:
intent.addCategory("com.sxf.activitytest.MY_CATEGORY1");
intent.addCategory("com.sxf.activitytest.MY_CATEGORY2");
更多隱式Intent
使用隱式Intent ,不僅可以啓動自己程序內的活動,還可以啓動其他程序的活動,這使得Android 多個應用程序之間的功能共享成爲了可能。
調用瀏覽器打開網頁
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
}
Intent.ACTION VIEW ,這是一個Android 系統內置的動作,其常量值爲android.intent.action.VIEW。然後通過Uri.parse ()方法,將一個網址字符串解析成一個Uri 對象,再調用Intent 的setData()方法將這個Uri 對象傳遞進去。
setData()接收一個Uri 對象,主要用於指定當前Intent 正在操作的數據,而這些數據通常都是以字符串的形式傳入到Uri.parse ()方法中解析產生的。
還可以在標籤中再配置一個標籤,用於更精確地
指定當前活動能夠響應什麼類型的數據。標籤中主要可以配置以下內容:
- android:scheme。用於指定數據的協議部分,如http、geo、tel
- android:host。用於指定數據的主機名部分,如www.baidu.com 部分
- android:port。 用於指定數據的端口部分,一般緊隨在主機名之後
- android: path。用於指定主機名和端口之後的部分,如一段網址中跟在域名之後的內容
- android:mimeType。用於指定可以處理的數據類型,允許使用通配符的方式進行指定
只有標籤中指定的內容和Intent 中攜帶的Data 完全一致時,當前活動才能夠響應該Intent。
撥打電話
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
}
其他常用Intent
1 ,web瀏覽器
Uri uri = Uri. parse ( "http://www.baidu.com" );
returnIt = new Intent (Intent . ACTION_VIEW , uri );
2,地圖
Uri mapUri = Uri. parse ( "geo:38.899533,-77.036476" );
returnIt = new Intent (Intent . ACTION_VIEW , mapUri);
3,調撥打電話界面
Uri telUri = Uri. parse ( "tel:100861" );
returnIt = new Intent (Intent . ACTION_DIAL , telUri);
4,直接撥打電話
Uri callUri = Uri. parse ( "tel:100861" );
returnIt = new Intent (Intent . ACTION_CALL , callUri);
5,卸載
Uri uninstallUri = Uri. fromParts ( "package" , " xxx " , null );
returnIt = new Intent (Intent . ACTION_DELETE , uninstallUri);
6,安裝
Uri installUri = Uri. fromParts ( "package" , " xxx " , null );
returnIt = new Intent (Intent . ACTION_PACKAGE_ADDED , installUri);
7,播放
Uri playUri = Uri. parse ( "file:///sdcard/download/everything.mp3" );
returnIt = new Intent (Intent . ACTION_VIEW , playUri);
8,調用發郵件
Uri emailUri = Uri. parse ( "mailto:[email protected]" );
returnIt = new Intent (Intent . ACTION_SENDTO , emailUri);
9,發郵件
returnIt = new Intent (Intent . ACTION_SEND );
String[] tos = { “[email protected]" };
String[] ccs = { “[email protected]" };
returnIt .putExtra(Intent . EXTRA_EMAIL , tos);
returnIt .putExtra(Intent . EXTRA_CC , ccs);
returnIt .putExtra(Intent . EXTRA_TEXT , "body" );
returnIt .putExtra(Intent . EXTRA_SUBJECT , "subject" );
returnIt .setType( "message/rfc882" );
Intent . createChooser ( returnIt , "Choose Email Client" );
10,發短信
Uri smsUri = Uri. parse ( "tel:100861" );
returnIt = new Intent (Intent . ACTION_VIEW , smsUri);
returnIt.putExtra( "sms_body" , "shenrenkui" );
returnIt.setType( "vnd.android -dir/mms-sms" );
11,直接發郵件
Uri smsToUri = Uri. parse ( "smsto://100861" );
returnIt = new Intent (Intent . ACTION_SENDTO , smsToUri);
returnIt.putExtra( "sms_body" , “test" );
12,發彩信
Ur i mmsUri = Uri. parse ( "content://media/external/images/media/23" );
returnIt = new Intent (Intent . ACTION_SEND );
returnIt.putExtra( "sms_body" , “test" );
returnIt.putExtra(Intent . EXTRA_STREAM , mmsUri);
returnIt.setType( "image/png" );
向下一個活動傳遞數據
Intent 中提供了一系列putExtra()方法的重載,可以把我們想要傳遞的數據暫存在Intent 中,啓動了另一個活動後,只需要把這些數據再從Intent中取出即可。
MainActivity.java:
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, Main2Activity.class);
intent.putExtra("extradata", "this is extra data");
startActivity(intent);
}
Main2Activity.java:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Intent intent = getIntent();
String data = intent.getStringExtra("extradata");
System.out.println(data);
}
通過getIntent()方法獲取到用於啓動SecondActivity 的Intent ,然後調用
getStringExtra()方法,傳入相應的鍵值,就可以得到傳遞的數據。如果傳遞的是整型數據,則使用getintExtra方法;如果傳遞的是布爾型數據,則使用getBooleanExtra()方法,以此類推。
向上一個活動傳遞數據
Activity 中還有一個startActivityForResult()方法也是用於啓動活動的,但這個方法期望在活動銷燬的時候能夠返回一個結果給上一個活動。方法接收兩個參數,第一個參數還是Intent ,第二個參數是請求碼(只要是一個唯一值就可以),用於在之後的回調中判斷數據的來源。
MainActivity.java:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainlayout);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, Main2Activity.class);
startActivityForResult(intent, 1);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
String resdata = data.getStringExtra("data_return");
System.out.println(resdata);
}
break;
default:
break;
}
}
Main2Activity.java:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Button button = (Button) findViewById(R.id.button2);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("data_return", "this is return data test");
setResult(RESULT_OK, intent);
finish();
}
});
}
通過按下Back鍵向上傳遞
重寫onBackPressed ()方法來解決這個問題,當用戶按下Back 鍵,就會去執行onBackPressed ()方法中的代碼:
Main2Activity.java:
@Override
protected void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("data_return", "this is return data test");
setResult(RESULT_OK, intent);
finish();
}
活動的生存期
Activity 類中定義了7 個回調方法,覆蓋了活動生命週期的每一個環節。以下7 個方法中除了onRestart()方法,其他都是兩兩相對的。
onCreate()。這每個活動中我們都重寫了這個方法,它會在活動第一次被創建的時候調用。你應該在這個方法中完成活動的初始化操作,比如說加載佈局、綁定事件等。
onStart()。這個方法在活動由不可見變爲可見的時候調用,對資源進行加載。
onResume()。這個方法在活動準備好和用戶進行交互的時候調用。此時的活動一定位於返回棧的棧頂,並且處於運行狀態。
onPause()。這個方法在系統準備去啓動或者恢復另一個活動的時候調用。我們通常會在這個方法中將一些消耗CPU 的資源釋放掉,以及保存一些關鍵數據,但這個方法的執行速度一定要快,不然會影響到新的棧頂活動的使用。
onStop()。這個方法在活動完全不可見的時候調用,對資源進行釋放。它和onPause()方法的主要區別在於,如果啓動的新活動是一個對話框式的活動,那麼onPause()方法會得到執行,而onStop()方法並不會執行。
onDestroy()。這個方法在活動被銷燬之前調用,之後活動的狀態將變爲銷燬狀態。
onRestart()。這個方法在活動由停止狀態變爲運行狀態之前調用,也就是活動被重新啓動了。
活動被回收前的數據保存與恢復
**onSaveInstanceState()**回調方法可以保證在活動被回收之前一定會被調用,因此我們可以通過這個方法來解決活動被回收時臨時數據得不到保存的問題。
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("key", "value");
}
使用的onCreate()方法其實也有一個Bundle 類型的參數。這個參數在一般情況下都是null,但是如果在活動被系統回收之前有通過onSaveinstanceState()方法來保存數據的話,這個參數就會帶有之前所保存的全部數據。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null){
String value = savedInstanceState.getString("key");
}
}
創建自定義控件
1、新建xxx.xml文件,完成佈局,這就是自定義的一個控件了;
2、在需要調用的yyy.xml文件中,使用以下語句引入進來:
<include layout="@layout/xxx" />
(3、在MainActivity.java中將系統自帶的標題欄隱藏:)
ActionBar actionbar = getSupportActionBar();
if (actionbar != null) {
actionbar.hide();
}
4、新建一個類,用於註冊事件響應:
public class Title extends LinearLayout {
public Title(Context context, AttributeSet attributeSet){
super(context, attributeSet);
LayoutInflater.from(context).inflate(R.layout.title, this);
Button buttonback = findViewById(R.id.buttonBack);
buttonback.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
((Activity)getContext()).finish();
}
});
}
}
首先重寫了LinearLayout 中帶有兩個參數的構造函數,在佈局中引人TitleLayout 控件就會調用這個構造函數。然後在構造函數中需要對標題欄佈局進行動態加載,這就要藉助LayoutInflater來實現了。通過Layoutinflater的from( )方法可以構建出一個LayoutInflater對象,然後調用inflate()方法就可以動態加載一個布 局文件,inflate()方法接收兩個參數,第一個參數是要加載的佈局文件的id,這裏我們傳入R.layoutitle, 第二個參數是給加載好的佈局再添加一個父佈局,這裏我們想要指定爲TitleLayout,於是直接傳人this。
5、在需要調用的yyy.xml文件中,使用以下語句引入進來:
<com.sxf.myapplication.Title
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
報錯整理
1、General error during semantic analysis: Unsupported class file major version 57
可以去File->Project Structuer中查看當前導入的項目中使用的SDK版本是否與之前項目匹配的相同,如果不相同修改爲相同版本即可
2、NoClassDefFoundError: Failed resolution of: Landroidx/localbroadcastmanager/content/LocalBroadcastManager
在app目錄下的build.gradle中添加:implementation ‘com.android.support:localbroadcastmanager:29.0.0’