第一章 页面布局 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 的过程中出现内存泄漏