一、排坑
android:showAsAction报错
<menu xmlns:android="http:///apk/res/android"
xmlns:app="http:///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(.et_name);
et_pass = (EditText) findViewById(.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(.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(.et_name);
et_pass = (EditText) findViewById(.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(.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(.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(.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) {
= name;
}
public String getTemp() {
return temp;
}
public void setTemp(String temp) {
this.temp = temp;
}
public String getPm() {
return pm;
}
public void setPm(String 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:///apk/res/android" >
<Preference
android:key="pre"
android:title="这是大标题"
android:summary="这是摘要"
/>
</PreferenceScreen>