一、排坑
android:showAsAction报错
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_settings" android:orderInCategory="100" app:showAsAction="never" android:title="@string/action_settings"/> </menu>
捕获异常
catch (Exception e) { // TODO Auto-generated catch block Log.e(TAG, e.getMessage()); }
No such file or directory
private String path= Environment.getExternalStorageDirectory()+"/ahmk/";
private String filename=path+"info.txt";
File dir = new File(path);
File file = new File(filename);
if(!dir.exists()){
dir.mkdirs();
}
java.lang.SecurityException: MODE_WORLD_READABLE no longer supported
Starting from N(N is for Nougat), attempting to use this mode will throw a SecurityException.
Assets文件夹
InputStream is = getAssets().open("weather.xml");
二、常见布局
相对布局
RelativeLayout
- 组件默认左对齐、顶部对齐
-
设置组件在指定组件的右边
android:layout_toRightOf="@id/tv1"
-
设置在指定组件的下边
android:layout_below="@id/tv1"
-
设置右对齐父元素
android:layout_alignParentRight="true"
-
设置与指定组件右对齐
android:layout_alignRight="@id/tv1"
线性布局
LinearLayout
-
指定各个节点的排列方向
android:layout_alignRight="@id/tv1"
-
设置右对齐
android:layout_gravity="right"
- 当竖直布局时,只能左右对齐和水平居中,顶部底部对齐竖直居中无效
- 当水平布局时,只能顶部底部对齐和竖直居中
- 使用match_parent时注意不要把其他组件顶出去
-
线性布局非常重要的一个属性:权重
android:layout_gravity="right"
- 权重设置的是按比例分配剩余的空间
帧布局
FrameLayout
- 默认组件都是左对齐和顶部对齐,每个组件相当于一个div
-
可以更改对齐方式
android:layout_gravity="right"
- 不能相对于其他组件布局
表格布局
TableLayout
- 每个节点是一行,它的每个子节点是一列
-
表格布局中的节点可以不设置宽高,因为设置了也无效
- 根节点的子节点宽为匹配父元素,高为包裹内容
- 节点的子节点宽为包裹内容,高为包裹内容
- 以上默认属性无法修改
-
根节点中可以设置以下属性,表示让第1列拉伸填满屏幕宽度的剩余空间
android:layout_gravity="right"
绝对布局
AbsoluteLayout
-
直接指定组件的x、y坐标
android:layout_x="144dp" android:layout_y="154dp"
三、logcat
- 日志信息总共分为5个等级
- verbose
- debug
- info
- warn
- error
- 定义过滤器方便查看
- System.out.print输出的日志级别是info,tag是System.out
-
Android提供的日志输出api
Log.v(TAG, "加油吧,童鞋们"); Log.d(TAG, "加油吧,童鞋们"); Log.i(TAG, "加油吧,童鞋们"); Log.w(TAG, "加油吧,童鞋们"); Log.e(TAG, "加油吧,童鞋们");
四、内部存储空间中读写文件
- Ram内存:运行内存,相当于电脑的内存
- Rom内存:内部存储空间,相当于电脑的硬盘
- sd卡:外部存储空间,相当于电脑的移动硬盘
- 应用只能在自己的包名目录下创建文件,不能到别人家去创建
使用路径api读写文件
- getFilesDir()得到的file对象的路径是data/data/com.itheima.rwinrom2/files
- 存放在这个路径下的文件,只要你不删,它就一直在
-
getCacheDir()得到的file对象的路径是data/data/com.itheima.rwinrom2/cache
- 存放在这个路径下的文件,当内存不足时,有可能被删除
-
系统管理应用界面的清除缓存,会清除cache文件夹下的东西,清除数据,会清除整个包名目录下的东西
public class MainActivity extends Activity { private EditText et_name; private EditText et_pass; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); et_name = (EditText) findViewById(R.id.et_name); et_pass = (EditText) findViewById(R.id.et_pass); readAccount(); } public void readAccount(){ File file = new File(getCacheDir(), "info.txt"); //File file = new File("data/data/com.example.index42.rwinrom/info.txt"); if(file.exists()){ try { FileInputStream fis = new FileInputStream(file); //把字节流转换成字符流 BufferedReader br = new BufferedReader(new InputStreamReader(fis)); //读取txt文件里的用户名和密码 String text = br.readLine(); String[] s = text.split("##"); et_name.setText(s[0]); et_pass.setText(s[1]); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void login(View v){ String name = et_name.getText().toString(); String pass = et_pass.getText().toString(); CheckBox cb = (CheckBox) findViewById(R.id.cb); //判断选框是否被勾选 if(cb.isChecked()){ //data/data/com.itheima.rwinrom:这就是内部存储空间的路径 File file = new File(getCacheDir(), "info.txt"); //File file = new File("data/data/com.example.index42.rwinrom/info.txt"); FileOutputStream fos; try { fos = new FileOutputStream(file); fos.write((name + "##" + pass).getBytes()); fos.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } //创建并显示吐司对话框 Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show(); } }
查看data/data内文件
五、外部存储读写数据
4.4系统及以上的手机将机身存储存储(手机自身带的存储叫做机身存储)在概念上分成了”内部存储internal” 和”外部存储external” 两部分。
public class MainActivity extends Activity { private EditText et_name; private EditText et_pass; private static final String TAG = "MainActivity"; private String path= Environment.getExternalStorageDirectory()+"/ahmk/"; private String filename=path+"info.txt"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); et_name = (EditText) findViewById(R.id.et_name); et_pass = (EditText) findViewById(R.id.et_pass); if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED ){ //申请权限 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},1); }else { //把动作告诉系统 readAccount(); } } public void readAccount(){ if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ File file = new File(filename); if(file.exists()){ try { FileInputStream fis = new FileInputStream(file); //把字节流转换成字符流 BufferedReader br = new BufferedReader(new InputStreamReader(fis)); //读取txt文件里的用户名和密码 String text = br.readLine(); String[] s = text.split("##"); et_name.setText(s[0]); et_pass.setText(s[1]); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public void login(View v){ CheckBox cb = (CheckBox) findViewById(R.id.cb); //判断选框是否被勾选 if(cb.isChecked()){ if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){ //申请权限 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},2); }else { //把动作告诉系统 writeAccount(); } } //创建并显示吐司对话框 Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show(); } public void writeAccount(){ //MEDIA_UNKNOWN:不能识别sd卡 //MEDIA_REMOVED:没有sd卡 //MEDIA_UNMOUNTED:sd卡存在但是没有挂载 //MEDIA_CHECKING:sd卡正在准备 //MEDIA_MOUNTED:sd卡已经挂载,可用 if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ //返回一个File对象,其路径是sd卡的真实路径 //File file = new File(Environment.getExternalStorageDirectory(), "ahmk/info.txt"); File dir = new File(path); File file = new File(filename); try { if(!dir.exists()){ dir.mkdirs(); } // if (!file.exists()) { // file.createNewFile(); // } // FileWriter fw = new FileWriter(file); // BufferedWriter output = new BufferedWriter(fw); // String name = et_name.getText().toString(); // String pass = et_pass.getText().toString(); // output.write(name + "##" + pass); // output.close(); FileOutputStream fos = new FileOutputStream(file); String name = et_name.getText().toString(); String pass = et_pass.getText().toString(); fos.write((name + "##" + pass).getBytes()); fos.close(); } catch (Exception e) { // TODO Auto-generated catch block Log.e(TAG, e.getMessage()); } } else{ Toast.makeText(this, "sd卡不可用哟亲么么哒", Toast.LENGTH_SHORT).show(); } } public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case 1: //权限回调 if (grantResults[0]==PackageManager.PERMISSION_GRANTED&&grantResults[1]==PackageManager.PERMISSION_GRANTED){ readAccount(); }else { //提示用户权限未被授予 Log.e("MainActivity","未授予权限"); } break; case 2: if (grantResults[0]==PackageManager.PERMISSION_GRANTED&&grantResults[1]==PackageManager.PERMISSION_GRANTED){ writeAccount(); }else { //提示用户权限未被授予 Log.e("MainActivity","未授予权限"); } break; } } }
使用FileOutputStream:
File foutput = new File(file_location_string); FileOutputStream fos = new FileOutputStream(foutput); BufferedWriter output = new BufferedWriter(new OutputStreamWriter(fos)); output.write("Buffered Content");
output.close();
使用FileWriter:
FileWriter fstream = new FileWriter(file_location_string); BufferedWriter output = new BufferedWriter(fstream); output.write("Buffered Content");
output.close();
根据Java的接口规范:
FileOutputStream是用于写入原始字节流比如图片流数据。如果是要写入字符流,则应该考虑使用FileWriter。
六、获取sd卡剩余容量
public class MainActivity extends Activity { @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); File path = Environment.getExternalStorageDirectory(); StatFs stat = new StatFs(path.getPath()); long blockSize; long totalBlocks; long availableBlocks; //获取当前系统版本的等级 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){ blockSize = stat.getBlockSizeLong(); totalBlocks = stat.getBlockCountLong(); availableBlocks = stat.getAvailableBlocksLong(); } else{ blockSize = stat.getBlockSize(); totalBlocks = stat.getBlockCount(); availableBlocks = stat.getAvailableBlocks(); } TextView tv = (TextView) findViewById(R.id.tv); tv.setText(formatSize(availableBlocks * blockSize)); } private String formatSize(long size) { return Formatter.formatFileSize(this, size); } }
七、文件的访问权限
- 在Android中,每一个应用是一个独立的用户
- drwxrwxrwx
- 第1位:d表示文件夹,-表示文件
- 第2-4位:rwx,表示这个文件的拥有者用户(owner)对该文件的权限
- r:读
- w:写
- x:执行
- 第5-7位:rwx,表示跟文件拥有者用户同组的用户(grouper)对该文件的权限
- 第8-10位:rwx,表示其他用户组的用户(other)对该文件的权限
openFileOutput的四种模式
Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
八、SharedPreference
Android的四大数据存储方式之一“SharedPreference”,其他三个分别是SQLite、Content Provider 和 File
一般使用SharedPreference来存储应用程序的配置信息。它一般存储在应用程序的私有存储区,文件权限是私有的。也就是说只能供写入者读取。它使用键/值(NVP机制)来存储数据。支持的数据类型(boolean、int、float、long和String)。它存储在应用程序的私有目录下(data/data/包名 /shared_prefs/)自定义的XML文件中。
取数据
public void readAccount2(){ SharedPreferences sp = getSharedPreferences("info", MODE_PRIVATE); String name = sp.getString("name", ""); String pass = sp.getString("pass", ""); et_name.setText(name); et_pass.setText(pass); }
写数据
public void login2(View v){ String name = et_name.getText().toString(); String pass = et_pass.getText().toString(); CheckBox cb = (CheckBox) findViewById(R.id.cb); //判断选框是否被勾选 if(cb.isChecked()){ //使用sharedPreference来保存用户名和密码 //路径在data/data/com.itheima.sharedpreference/share_ SharedPreferences sp = getSharedPreferences("info", MODE_PRIVATE); //拿到sp的编辑器 SharedPreferences.Editor ed = sp.edit(); ed.putString("name", name); ed.putString("pass", pass); //提交 ed.commit(); } //创建并显示吐司对话框 Toast.makeText(this, "登录成功", 0).show(); }
九、生成XML文件备份短信
public class MainActivity extends Activity { List<Message> smsList; private String path= Environment.getExternalStorageDirectory()+"/ahmk/"; private String filename=path+"sms.xml"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //虚拟10条短信 smsList = new ArrayList<Message>(); for(int i = 0; i < 10; i++){ Message sms = new Message("小志好棒" + i, System.currentTimeMillis() + "", "138"+i+i, "1"); smsList.add(sms); } } public void click(View v){ if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){ //申请权限 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1); }else { //把动作告诉系统 save(); } } public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode){ case 1: //发短信权限回调 if (grantResults[0]==PackageManager.PERMISSION_GRANTED){ save(); }else { //提示用户权限未被授予 Log.d("MainActivity","未授予发短信权限"); } break; } } public void save(){ //在内存中把xml备份短信的格式拼接出来 StringBuffer sb = new StringBuffer(); sb.append("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"); sb.append("<messages>"); for (Message sms : smsList) { sb.append("<sms>"); sb.append("<body>"); sb.append(sms.getBody()); sb.append("</body>"); sb.append("<date>"); sb.append(sms.getDate()); sb.append("</date>"); sb.append("<type>"); sb.append(sms.getType()); sb.append("</type>"); sb.append("<address>"); sb.append(sms.getAddress()); sb.append("</address>"); sb.append("</sms>"); } sb.append("</messages>"); File file = new File(filename); try { FileOutputStream fos = new FileOutputStream(file); fos.write(sb.toString().getBytes()); fos.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
public class Message { private String body; private String date; private String address; private String type; public String getBody() { return body; } public void setBody(String body) { this.body = body; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getType() { return type; } public void setType(String type) { this.type = type; } public Message(String body, String date, String address, String type) { super(); this.body = body; this.date = date; this.address = address; this.type = type; } }
使用XMl序列化器生成xml文件
public void save2(){ //使用xml序列化器生成xml文件 //1.拿到序列化器对象 XmlSerializer xs = Xml.newSerializer(); //2.初始化 File file = new File(filename); try { FileOutputStream fos = new FileOutputStream(file); //enconding:指定用什么编码生成xml文件 xs.setOutput(fos, "utf-8"); //3.开始生成xml文件 //enconding:指定头结点中的enconding属性的值 xs.startDocument("utf-8", true); xs.startTag(null, "message"); for (Message sms : smsList) { xs.startTag(null, "sms"); xs.startTag(null, "body"); xs.text(sms.getBody() + "<body>"); xs.endTag(null, "body"); xs.startTag(null, "date"); xs.text(sms.getDate()); xs.endTag(null, "date"); xs.startTag(null, "type"); xs.text(sms.getType()); xs.endTag(null, "type"); xs.startTag(null, "address"); xs.text(sms.getAddress()); xs.endTag(null, "address"); xs.endTag(null, "sms"); } xs.endTag(null, "message"); //告诉序列化器,文件生成完毕 xs.endDocument(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
十、pull解析xml文件
事件类型主要有五种
- START_DOCUMENT:xml头的事件类型
- END_DOCUMENT:xml尾的事件类型
- START_TAG:开始节点的事件类型
- END_TAG:结束节点的事件类型
- TEXT:文本节点的事件类型
public class MainActivity extends Activity { List<City> cityList; private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v){ //拿到pull解析器对象 XmlPullParser xp = Xml.newPullParser(); //初始化 try { //获取到src文件夹下的资源文件 InputStream is = getAssets().open("weather.xml"); //InputStream is = getClassLoader().getResourceAsStream("weather.xml"); xp.setInput(is, "utf-8"); //获取当前节点的事件类型,通过事件类型的判断,我们可以知道当前节点是什么节点,从而确定我们应该做什么操作 int type = xp.getEventType(); City city = null; while(type != XmlPullParser.END_DOCUMENT){ //根据节点的类型,要做不同的操作 switch (type) { case XmlPullParser.START_TAG: // 获取当前节点的名字 if("weather".equals(xp.getName())){ //创建city集合对象,用于存放city的javabean cityList = new ArrayList<City>(); } else if("city".equals(xp.getName())){ //创建city的javabean对象 city = new City(); } else if("name".equals(xp.getName())){ // 获取当前节点的下一个节点的文本 String name = xp.nextText(); city.setName(name); } else if("temp".equals(xp.getName())){ // 获取当前节点的下一个节点的文本 String temp = xp.nextText(); city.setTemp(temp); } else if("pm".equals(xp.getName())){ // 获取当前节点的下一个节点的文本 String pm = xp.nextText(); city.setPm(pm); } break; case XmlPullParser.END_TAG: if("city".equals(xp.getName())){ //把city的javabean放入集合中 cityList.add(city); } break; } //把指针移动到下一个节点,并返回该节点的事件类型 type = xp.next(); } for (City c : cityList) { Log.e(TAG, c.toString()); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
public class City { private String name; private String temp; private String pm; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTemp() { return temp; } public void setTemp(String temp) { this.temp = temp; } public String getPm() { return pm; } public void setPm(String pm) { this.pm = pm; } @Override public String toString() { return "City [name=" + name + ", temp=" + temp + ", pm=" + pm + "]"; } }
十一、Preferenece
Preference直译为偏好,博友建议翻译为首选项。一些配置数据,一些我们上次点击选择的内容,我们希望在下次应用调起的时候依旧有效,无须用户再一次进行配置或选择。Android提供preference这个键值对的方式来处理这样的情况,自己主动保存这些数据,并立时生效,Android提供一种类似layout的方式来进行Prefernce的布局。
public class MainActivity extends PreferenceActivity { @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main); //通过资源id指定要把哪个preference的内容显示至界面 addPreferencesFromResource(R.xml.pref); Preference pf = findPreference("pre"); pf.setSummary("这是修改过后的摘要:小志和b哥不能说的故事"); } }
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <Preference android:key="pre" android:title="这是大标题" android:summary="这是摘要" /> </PreferenceScreen>