1. 程式人生 > >Android開發筆記

Android開發筆記

Language 位置 put undle 服務類 pre android開發 基本數據 前置

第一章 頁面布局
    1.1 線性布局
        <LinearLayout android:layout_height="match_parent"
                android:orientation="vertical/horizontal"
    
    1.2 相對布局
        <RelativeLayout 
            <Button
                android:id="@+id/center"
                android:layout_width="wrap_content
" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="確定" /> android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:layout_toLeftOf
="@+id/center" android:layout_toRightOf="@+id/center" android:layout_above="@+id/center" android:layout_below="@+id/center" 1.3 其他布局 絕對布局(deprecated) 幀布局FrameLayout 1.4 屏幕適配 px: 像素點 分辨率 320*480 dpi: dot per inch 像素密度 每英寸的像素點 dp
|dip: (支持屏幕適配)density independent pixels 獨立密度 標準: 以160dip為基準,在該條件下 1dp=1px 安卓屏幕適配 http://blog.sunofbeaches.com/archives/196 android布局--Android fill_parent、wrap_content和match_parent的區別 三個屬性都用來適應視圖的水平或垂直大小,一個以視圖的內容或尺寸為基礎的布局比精確地指定視圖範圍更加方便。 1)fill_parent 設置一個構件的布局為fill_parent將強制性地使構件擴展,以填充布局單元內盡可能多的空間。這跟Windows控件的dockstyle屬性大體一致。設置一個頂部布局或控件為fill_parent將強制性讓它布滿整個屏幕。 2) wrap_content 設置一個視圖的尺寸為wrap_content將強制性地使視圖擴展以顯示全部內容。以TextView和ImageView控件為例,設置為wrap_content將完整顯示其內部的文本和圖像。布局元素將根據內容更改大小。設置一個視圖的尺寸為wrap_content大體等同於設置Windows控件的Autosize屬性為True。 3)match_parent Android2.2中match_parent和fill_parent是一個意思 .兩個參數意思一樣,match_parent更貼切,於是從2.2開始兩個詞都可以用。那麽如果考慮低版本的使用情況你就需要用fill_parent了 二 Activity 獲取當前項目文件及緩存位置(一般用於保存用戶數據) this.getFilesDir() /data/data/包名+項目名/files this.getCacheDir() /data/data/包名+項目名/cache 消息顯示: Toast.makeText(this,"String",Toast.LENGTH_LONG).show() 存取sd卡 開啟權限: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 獲取外接SD卡路徑 Environment.getExternalStorageDirectory() 獲取外接SD卡掛載狀態 Environment.getExternalStorageState() 保存數據到SharePreference: private SharedPreferences settings_info; settings_info = this.getSharedPreferences("settings_info", MODE_PRIVATE); // 獲取編輯器 開啟編輯模式 SharedPreferences.Editor edit = settings_info.edit(); // 保存變量 edit.putBoolean("state",isChecked); // 獲取變量 ,第二個參數是沒找到指定參數時賦予默認值 settings_info.getBoolean("state",false); // 提交修改 保存到SharePreference edit.commit(); Sqlite事務: 開啟事務 sqLiteDatabase.beginTransaction(); 設置成功事務: sqLiteDatabase.setTransactionSuccessful(); 結束提交事務: sqLiteDatabase.endTransaction(); 頁面跳轉&傳遞基本數據類型:(顯示意圖)intent ** Intent intent = new Intent(this,SecondActivity.class); intent.putExtra("account",tx_account); intent.putExtra("password",tx_password); startActivity(intent); Intent intent = new Intent(); //第一種 顯式意圖 // intent.setClassName("com.android.browser","com.android.browser.BrowserActivity"); // 第二種 顯式意圖 ComponentName componentName = new ComponentName("com.android.browser","com.android.browser.BrowserActivity"); intent.setComponent(componentName); startActivity(intent); 頁面跳轉&傳遞基本數據類型:(隱式意圖)intent Intent intent = new Intent(); intent.putExtra("account",tx_account); intent.putExtra("password",tx_password); ** intent.setAction("ustc.sse.LOGIN_INFO"); ** intent.addCategory(intent.CATEGORY_DEFAULT); ** intent.setPackage("包名") startActivity(intent); 獲取前置頁面傳遞的信息: 基本數據類型: Intent intent = getIntent(); String account = intent.getStringExtra("account"); String password = intent.getStringExtra("password"); 引用數據類型: 引用數據類型要實現Parcelable序列化接口(寫到內存)或者Serializable(寫到磁盤) intent.putExtra("user",user); User user = intent.getParcelableExtra("user"); 顯示跳轉第三方應用: 跳轉到瀏覽器: intent.setClassName("com.android.browser","com.android.browser.BrowserActivity"); 結束當前頁面: this.finish(); *** // 打電話給10086 // sdk 23以下只用該方法即可打電話 private void call(String mobile) { Intent intent = new Intent(); Uri uri = Uri.parse("tel:" + mobile); intent.setData(uri); // intent.setAction(Intent.ACTION_CALL); // intent.addCategory(Intent.CATEGORY_DEFAULT); // intent.setPackage("com.android.server.telecom"); // com.android.server.telecom/.components.UserCallActivity intent.setClassName("com.android.server.telecom","com.android.server.telecom.components.UserCallActivity"); startActivity(intent); } *** sdk26之後打電話需要動態申請權限: private static final int REQUEST_CODE_ASK_CALL_PHONE =123; public void onCall(String mobile) { if (Build.VERSION.SDK_INT >= 23) { int checkCallPhonePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE); if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.CALL_PHONE }, REQUEST_CODE_ASK_CALL_PHONE); return; } else { // 上面已經寫好的撥號方法 call(mobile); call(mobile); } } else { // 上面已經寫好的撥號方法 callDirectly(mobile); call(mobile); } } //動態權限申請後處理 @Override public void onRequestPermissionsResult(int requestCode, String[] permissions,int[] grantResults){ switch (requestCode) { case REQUEST_CODE_ASK_CALL_PHONE: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission Granted callDirectly(mobile); }else { // Permission Denied Toast.makeText(MainActivity.this,"CALL_PHONE Denied", Toast.LENGTH_SHORT) .show(); }break; default:super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } 調用系統應用發短信: private void doSendSMSTo(String phone, String content) { if(PhoneNumberUtils.isGlobalPhoneNumber(phone)){ Intent intent = new Intent(Intent.ACTION_SENDTO,Uri.parse("smsto:"+phone)); intent.putExtra("sms_body",content); startActivity(intent); } } Activity數據回傳:第一個頁面請求訪問第二頁面,第二頁面處理完返回到第一頁面 第一個頁面: 原來的startActivity(Intent)方法改為startActivityForResult 並且重寫onActivityResult(請求碼,返回結果碼,intent)--返回結果處理函數: startActivityForResult(intent, Constant.CHARGE_REQUEST_CODE); @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == Constant.CHARGE_REQUEST_CODE){ // 返回的結果碼為1說明充值成功 if (resultCode == 1){ tv_show.setText("恭喜您,充值成功,充值金額為"+data.getStringExtra("chargeAmount").toString()+"元!"); }else { // 否則充值失敗 tv_show.setText("充值失敗,請稍後再試!"); } } } 在第二頁面設置返回函數setResult(返回結果碼,Intent) setResult(Constant.CHARGE_SUCCESS_CODE,intent); 生命周期方法: onCreate() // 創建 onStart() // 可見,但沒獲得焦點 onResume() // 可見 且已獲得焦點 onPause() // 暫停,失去焦點,不可以操作 onStop() // 不可見 onDestroy() // 銷毀 橫豎屏切換時的生命周期方法: 先銷毀Activity,然後重新創建Activity 配置始終橫屏或豎屏:(適合遊戲開發,或是只有一種屏幕狀態的應用) <activity android:screenOrientation="landscape(橫屏)/portrait(豎屏)/全屏等" 設置不敏感: 發生橫豎屏等轉換時不會重新創建Activity (適合有兩種屏幕狀態的應用) <activity android:configChanges="keyboardHidden|screenSize|orientation" 任務棧: Activity四種啟動模式: 1 standard模式:(默認模式) 每次創建新的任務,並且放置於棧頂,點擊返回的時候,銷毀當前任務,移除棧頂 創建多少個,就要點擊多少次返回鍵退出 2 singletop模式: 如果棧頂已經是當前任務,則不會創建新的任務 應用場景: 瀏覽器的書簽 應用的通知推送等 3 singleTask模式: 如果要創建的任務在任務棧中不存在,則創建新任務,放在棧頂 如果要創建的任務已經存在了,就會把這個任務之上的任務全部從棧中移除, 使得當前任務成為最頂部的任務 如果棧頂已經是要創建的任務時,不會再創建新的任務 使用場景: 任務占用資源比較大的任務 4 singleInstance模式: 前三種啟動模式,都是在同一個任務棧裏的,但是singleInstance是獨立一個任務棧 並且如果已有該任務棧,則不會創建新的任務棧 應用場景: 在整個系統中只有唯一一個實例,比如Launcher,有道詞典的取詞 三 廣播 編寫廣播接收者(收音機) 繼承BroadcastReceiver 重寫onReceive(Context,Intent)方法,在方法裏添加廣播處理 註冊廣播(動態註冊) 在onCreate()生命周期裏註冊 1 創建意圖過濾器 2 添加要監聽的廣播action 3 創建廣播接收者,一般設置為成員變量 便於取消註冊,釋放資源 4 註冊廣播接收者 獲取電量等信息 BatteryManager 註冊廣播(靜態註冊): 編寫接收者 在AndroidManifest裏添加權限並註冊receiver: <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <receiver android:name=".BootCompleteReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver> 監聽應用的安裝與卸載 <receiver android:name=".AppStateReceiver"> <intent-filter> <action android:name="android.intent.action.PACKAGE_ADDED"/> <action android:name="android.intent.action.PACKAGE_REMOVED"/> <data android:scheme="package"/> </intent-filter> </receiver> 發送廣播: public void sendMsg(View view){ String content = mEt_msgContent.getText().toString(); Intent intent = new Intent(); intent.setAction("ustc.sse.broadcastdemo.SEND_MSG"); intent.putExtra("content",content); sendBroadcast(intent); } 接收廣播: @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); String msg = intent.getStringExtra("content"); Log.d(Tag ,"action ==" + action + " msg: " + msg); } 發送有序廣播: public void sendOrderBrdcst(View view){ Intent intent = new Intent(); intent.setAction("ustc.sse.orderbroadcastdemo.SEND_ORDER_BROADCAST"); sendOrderedBroadcast(intent,null); } 接收廣播: 配置接收廣播的優先級 priority <receiver android:name=".HighLevelReceiver"> <intent-filter android:priority="1000"> <action android:name="ustc.sse.orderbroadcastdemo.SEND_ORDER_BROADCAST"></action> </intent-filter> </receiver> 接收廣播:(終止廣播) public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.d(Tag ,"high action == " + action); abortBroadcast() // 終止廣播 } 修改廣播的內容: getResultExtras(true)& setResultExtras(resultBundle) public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Bundle resultBundle = getResultExtras(true); String strContent = resultBundle.getString("strContent"); resultBundle.putString("strContent","我是被High接收者修改過的廣播內容..."); setResultExtras(resultBundle); } 廣播的權限: <uses-permission> ---- 所擁有的權限 我要訪問其他應用時需要用到 <permission> --- 其他應用要訪問我時需要擁有的權限 發送的廣播誰能接收: 1 聲明權限 如果要接收廣播的應用沒有獲取該權限<uses-permission>就無法接收到廣播 <permission android:name="ustc.sse.orderbroadcastdemo.ORDER_PERMISSION"/> 2 指定權限 Manifest.permission.ORDER_PERMISSION sendOrderedBroadcast(intent,Manifest.permission.ORDER_PERMISSION,null,null, Activity.RESULT_OK,null,bundle); 誰能給我發廣播: 1 聲明權限: <permission android:name="ustc.sse.orderbroadcastdemo.WHO_CAN_SEND_2_ME"/> 2 receiver裏面聲明給改接收者發廣播需要的權限: <receiver android:permission="ustc.sse.orderbroadcastdemo.WHO_CAN_SEND_2_ME"> 四 服務: 服務的生命周期: onCreate() onStartCommand(); onStart() -- 廢棄 onDestroy() onBind() 手動啟動/關閉服務: 編寫服務類繼承android.app.Service, 在服務類裏編寫生命周期方法 開啟/停止服務: Intent intent = new Intent(); intent.setClass(this, FirstService.class); startService(intent)/stopService(intent); 服務由系統創建: 綁定服務: 1 編寫服務類繼承android.app.Service 重寫onBind(); 2 在服務類裏編寫內部類CommunicateBinder繼承Binder,實現自定義功能的接口 3 在onBind()方法裏返回內部類CommunicateBinder的實例 4 編寫Activity 綁定服務&解綁服務 綁定服務: public void bindServiceClick(View view){ Intent intent = new Intent(); intent.setClass(this,MyService.class); mIsServiceBinded = bindService(intent,mServiceConnection,BIND_AUTO_CREATE); } private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { /*if (service instanceof MyService.CommunicateBinder) { mCommunicateBinder = (MyService.CommunicateBinder) service; }*/ if (service instanceof IServiceControl) { // 在這裏獲取返回的內部類CommunicateBinder對象 mCommunicateBinder = (IServiceControl) service; } } @Override public void onServiceDisconnected(ComponentName name) { Log.d(Tag ,"onServiceDisconnected()..."); } }; 解綁服務: public void unbindServiceClick(View view){ Log.d(Tag ,mIsServiceBinded.toString()); if (mIsServiceBinded ){ mIsServiceBinded = false; unbindService(mServiceConnection); } } 調用接口中自定義的接口方法: public void callServiceMethod(View view){ // mCommunicateBinder.callInnerMethod(); mCommunicateBinder.callSeviceInnerMethod(); } 兩種服務比較: 開啟服務能保證服務一直在運行,後臺也能運行,但不能跟服務通訊 onCreate—>onStartCommand—->onDestroy 綁定服務不能保證服務一直運行,不能在後臺運行,但可以跟服務通訊 onCreate—->onBind—>onUnbind—->onDestroy 混合兩種開啟方式: 通過startService,再去bindService,如果沒有解綁,那麽是停止不了服務的 Activity之間通過intent實現通信,intent-filter就是用來註冊Activity,Service和Broadcast Receiver 使Android知道那個應用程序(或組件)能用來響應intent請求使其可以在一片數據上執行那個動作。為了註冊一個應用程序組件為intent處理者,在其組件的manifest節點中添加一個intent-fillter標簽。 <Service exported="true/false" 表示是否允許第三方應用使用該服務> 跨進程通訊AIDL:(android interface definition language 安卓接口定義語言) 劃出一個公共的內存空間,把要通訊的數據,通過這個空間來交流即可!而這個空間呢,就是binder了! 步驟: (應用內) 新建一個AIDL接口,創建接口方法--->make Project --->編寫java類繼承AIDL接口名.Stub(),重寫接口方法--->onBind()裏返回該類即可 interface NormalUserAction { void saveMoney(in float money); float getMoney(); void loanMoney(float money); } public class NormalUserAIDLActionImpl extends NormalUserAction.Stub onBind(){ return new NormalUserAIDLActionImpl(); } AIDL 的編寫主要為以下三部分: 創建 AIDL 創建要操作的實體類,實現 Parcelable 接口,以便序列化/反序列化 新建 aidl 文件夾,在其中創建接口 aidl 文件以及實體類的映射 aidl 文件 Make project ,生成 Binder 的 Java 文件 服務端 創建 Service,在其中創建上面生成的 Binder 對象實例,實現接口定義的方法 在 onBind() 中返回 客戶端 實現 ServiceConnection 接口,在其中拿到 AIDL 類 bindService() 調用 AIDL 類中定義好的操作請求 音樂播放器: 獲取權限: AccessUtils.verifyStoragePermissions(this); 子線程不可以更新主線程UI,但是Android裏有兩個控件可以用子線程去更新: progressBar & surfaceView 正則表達式: Pattern mPattern = Pattern.compile("[a-zA-Z0-9]+"); Matcher mMatcher = mPattern.matcher(str_account); if (mMatcher.matches()){ Toast.makeText(this,"匹配成功",Toast.LENGTH_SHORT).show(); } Handler: 1 使用 Handler 的 post() 方法更新 UI 2 使用 Handler 的 sendMessage() 方法更新 UI 3 使用 runOnUiThread() 方法更新 UI 4 使用 View 的 post() 方法更新 UI 5 子線程中創建 Handler(handler1)發送消息,在子線程中的Handler(handler1) 中處理,然後發送給主線程(mHandler) 去更新 UI 6 在子線程中更新 UI (對的,你沒看錯,就是在子線程中更新 UI ) 7 消除使用 Handler 的過程中出現內存泄漏

Android開發筆記