常用的布局

LinearLayout

Android 2.2开始fill_parent改名为match_parent ,从API Level为8开始我们可以直接用match_parent来代替fill_parent

orientation方向;vertical垂直;horizontal水平
gravity: 对齐方式,子控件相对于当前控件的对齐方式
layout_gravity:当前控件相对于父控件的对齐方式
margin:当前控件相对于四周的间距。
padding:当前控件中的子控件相对于当前控件四周的间距。

注意:在LinearLayout中,match_parent是充满父布局剩余空间,剩余空间,剩余空间。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

上面的例子中,TextView能显示出来,然后Button占据了剩余的全部空间。

RelativeLayout

RelativeLayout 第一个控件默认都是从左上角开始布局,要控制位置需要设置每个控件相对于其他控件的位置。注意上例中改为RelativeLayout,第二个控件会把第一个控件遮住。

layout_below:位于哪个控件的下方
layout_above:位于哪个控件的上方
layout_toLeftOf:指定当前控件位于哪个控件的左边
layout_alignRight: 两个控件的右边边缘对齐
layout_alignParentRight:当前控件基于父窗体的对其方式
layout_centerHorizontal:水平居中
layout_centerVertical:垂直居中
layout_centerInParent:位于父窗体的中间

举个RelativeLayout的例子

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#ff0000"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <LinearLayout
       android:layout_centerInParent="true"
        android:background="#00ff00"
        android:layout_width="200dp"
        android:layout_height="200dp"
        />
    <LinearLayout
        android:layout_centerInParent="true"

        android:background="#0000ff"
        android:layout_width="300dp"
        android:layout_height="100dp"

    />
    <ImageView
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"/>
</RelativeLayout>

FrameLayout

帧布局中的子控件默认放在左上角,是一层一层向上叠加的。适合简单布局。

下面是帧布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#ff0000"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_gravity="center"
        android:background="#00ff00"
        android:layout_width="200dp"
        android:layout_height="200dp"
        />
    <LinearLayout
        android:layout_gravity="center"
        android:background="#0000ff"
        android:layout_width="300dp"
        android:layout_height="100dp"

    />
    <ImageView
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"/>
</FrameLayout>

和上例RelativeLayout两个的布局都长这样

可以看到确实是一层一层往上覆盖的,并且RelativeLayout也有这样的特性。

logcat

最好自定义一个log工具类,指定level就可以过滤日志信息,保留我们需要的部分。比如指定level为WARN,则只会打印WARN和ERROR的信息。将level指定为NOTHING,任何日志都不会打印。

package Utils;

import android.util.Log;

public class LogUtils {
    public static final int VERBOSE = 1;
    public static final int DEBUG = 2;
    public static final int INFO = 3;
    public static final int WARN = 4;
    public static final int ERROR = 5;
    public static final int NOTHING = 6;
    public static int level = VERBOSE;
    // 琐碎的意义不大的信息,等级最低
    public static void v(String tag , String msg){
        if(level <= VERBOSE){
            Log.e(tag, msg);
        }
    }
    // debug级
    public static void d(String tag , String msg){
        if(level <= DEBUG)
            Log.w(tag, msg);
    }
    // info级别,打印比较重要的数据,比debug高一级
    public static void i(String tag , String msg){
        if(level <= INFO)
            Log.i(tag, msg);
    }
    // warn警告信息
    public static void w(String tag , String msg){
        if(level <= WARN)
            Log.d(tag, msg);
    }
    // error错误信息
    public static void e(String tag , String msg){
        if(level <= ERROR)
            Log.v(tag, msg);
    }
}

登录样例-记住用户名密码

使用文件存储的方式,主要使用java.io,很简陋不推荐。

Android自带的方法openFileOutput和openFileInout默认保存在/data/data/包名/files/文件夹下。只能当前程序使用,安全。

values下的strings.xml

<resources>
    <string name="app_name">LayoutTest</string>
    <string name="account">手机/邮箱</string>
    <string name="password">密码</string>
    <string name="remember">记住我</string>
    <string name="login">登录</string>
</resources>

activity_main布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp" >

    <ImageView
        android:id="@+id/head_pic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="40dp"
        android:src="@mipmap/ic_launcher_round"/>
    <EditText
        android:id="@+id/account"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/account"
        android:layout_marginTop="30dp"/>
    <EditText
        android:id="@+id/password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/password"
        />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <CheckBox
            android:layout_centerVertical="true"
            android:id="@+id/remember_me"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/remember"/>
        <Button
            android:id="@+id/login"
            android:layout_centerVertical="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="@string/login"
            android:layout_alignParentRight="true"/>
    </RelativeLayout>
</LinearLayout>

MainActivity

package com.example.layouttest;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class MainActivity extends AppCompatActivity {

    private EditText etAccount;
    private EditText etPassword;
    private CheckBox checkRemember;
    private Button buttonLogin;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // context很常用, 直接给mContext,以后都用它就行
        mContext = this;
        // 1. 获取控件
        etAccount = (EditText) findViewById(R.id.account);
        etPassword = (EditText) findViewById(R.id.password);
        checkRemember = (CheckBox) findViewById(R.id.remember_me);
        buttonLogin = (Button) findViewById(R.id.login);
        // 6. 下次进入页面从本地读取用户名密码,显示,checkbox状态还是选中状态
        String userinfo = getUserInfo();
        if (!TextUtils.isEmpty(userinfo)) {
            String username = userinfo.split("-")[0];
            String password = userinfo.split("-")[1];
            Log.d("pass", username);
            Log.d("pass", password);
            etAccount.setText(username);
            etPassword.setText(password);

            checkRemember.setChecked(true);
        }
        // 2. 设置登录按钮点击事件
        buttonLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                login();
            }
        });
    }

    private void login() {
        // 3. 获取用户名密码,checkbox是否选中
        String username = etAccount.getText().toString().trim();
        String password = etPassword.getText().toString().trim();
        boolean isRemember = checkRemember.isChecked();
        // 4. 判断用户名和密码是否为空,不为空请求服务器;为空就提示输入

        if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
            Toast.makeText(mContext, "请输入用户名或密码", Toast.LENGTH_SHORT).show();
            return;
        }

        // 5. 判断checkbox是否选中,选中则保存用户名密码到本地
        if (isRemember) {
            boolean result = saveUserInfo(username, password);
            if (result) {
                Toast.makeText(mContext, "保存成功", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(mContext, "保存失败", Toast.LENGTH_SHORT).show();

            }
        } else {
            FileOutputStream out = null;
            try {
                // 若用户没有勾选保存用户名密码,用一个同名的文件覆盖,但里面没有内容,下次进入,输入框就被清空了
                out =  openFileOutput("userinfo", MODE_PRIVATE);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        Intent intent = new Intent(mContext, WelconmeActivity.class);
        startActivity(intent);
        finish();
    }

    private boolean saveUserInfo(String username, String password) {
        String userInfo = username + "-" + password;
        FileOutputStream out = null;
        BufferedWriter writer = null;
        try {
            out = openFileOutput("userinfo", MODE_PRIVATE);
            // 将字节流包装为字符流再包装成缓冲流
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.write(userInfo);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                    e.printStackTrace();
            }
        }
        return false;
    }

    private String getUserInfo() {
        FileInputStream in = null;
        BufferedReader reader = null;
        StringBuilder content = new StringBuilder();
        try {
            in = openFileInput("userinfo");
            reader = new BufferedReader(new InputStreamReader(in));
            String line;
            while ((line=reader.readLine()) != null) {
                content.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                    e.printStackTrace();
            }
        }
        return content.toString();
    }
}

SharedPreferences存储

上面的存储方法不推荐,现使用SharedPreferences。

SharedPreferences是通过xml文件来做数据存储的。一般用来存放一些标记性的数据,一些设置信息。

用法如下

// 1.通过Context对象创建一个SharedPreference对象
//name:sharedpreference文件的名称    mode:文件的操作模式
SharedPreferences sharedPreferences = getSharedPreferences("userinfo", Context.MODE_PRIVATE);
// 2.通过sharedPreferences对象获取一个Editor对象
Editor editor = sharedPreferences.edit();
// 3.往Editor中添加数据
editor.putString("username", username);
editor.putString("password", password);
// 4.提交Editor对象
editor.apply(); // editor.commit();这个是有返回值的,但是返回值用处不大,推荐使用apply() 

    // *********使用sharedPreferences读取数据**********

// 1.通过Context对象创建一个SharedPreference对象
SharedPreferences sharedPreferences = context.getSharedPreferences("userinfo", Context.MODE_PRIVATE);
// 2.通过sharedPreference获取存放的数据
//key:存放数据时的key   defValue: 没找到key时候的默认值,根据业务需求来写
String username = sharedPreferences.getString("username", "");
String password = sharedPreferences.getString("password", "");

通过PreferenceManager可以获取一个默认的sharepreferences对象,默认以包名+ _shared_preferences.xml这样的名称命名。

SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
package com.example.layouttest;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private EditText etAccount;
    private EditText etPassword;
    private CheckBox checkRemember;
    private Button buttonLogin;
    private Context mContext;
    private SharedPreferences pref;
    private SharedPreferences.Editor editor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // context很常用, 直接给mContext,以后都用它就行
        mContext = this;

        // 获取SharedPreferences
        pref = PreferenceManager.getDefaultSharedPreferences(mContext);
        // 1. 获取控件
        etAccount = (EditText) findViewById(R.id.account);
        etPassword = (EditText) findViewById(R.id.password);
        checkRemember = (CheckBox) findViewById(R.id.remember_me);
        buttonLogin = (Button) findViewById(R.id.login);
        // 没有取到就默认为false
        boolean isRemember = pref.getBoolean("remember_password", false);
        // 6. 下次进入页面从本地读取用户名密码,显示,checkbox状态还是选中状态
        if (isRemember) {
            String username = pref.getString("username", "");
            String password = pref.getString("password", "");
            etAccount.setText(username);
            etPassword.setText(password);
            checkRemember.setChecked(true);
        }

        // 2. 设置登录按钮点击事件
        buttonLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                login();
            }
        });
    }
    private void login() {
        // 3. 获取用户名密码,checkbox是否选中
        String username = etAccount.getText().toString();
        String password = etPassword.getText().toString();
        // 4. 判断用户名和密码是否为空,不为空请求服务器;为空就提示输入

        if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
            Toast.makeText(mContext, "请输入用户名或密码", Toast.LENGTH_SHORT).show();
            return;
        }
        // 5. 判断checkbox是否选中,选中则保存用户名密码到本地
        editor = pref.edit();
        if (checkRemember.isChecked()) {
            editor.putString("username", username);
            editor.putString("password", password);
            editor.putBoolean("remember_password", true);

        } else {
            // 不勾选,下次进入不应该显示用户名密码,应该clear掉
            editor.clear();
        }
        // 不管checkbox有没有选中,都提交
        editor.apply();
        Intent intent = new Intent(mContext, WelconmeActivity.class);
        startActivity(intent);
        finish();
    }
}

SD卡

读写SD卡需要配置权限android.permission.WRITE_EXTERNAL_STORAGE

// 得到SD卡的路径
String path = Environment.getExternalStorageDirectory().getPath();
Log.d("storage", path); // 这里小米手机是 /storage/emulated/0
// SD卡的状态,是否挂载?
Log.d("storage", Environment.getExternalStorageState()); // 这里小米手机是 mounted,但不是实际的SD卡
//
File sdFile = Environment.getExternalStorageDirectory();
// File对象可以查看可用空间和已用空间
long usableSpace = sdFile.getUsableSpace();
long totalSpace = sdFile.getTotalSpace();
// 转换成用户能看懂的格式(M,G等)
String usableSpace_str = Formatter.formatFileSize(mContext, usableSpace);
String totalSpace_str = Formatter.formatFileSize(mContext, totalSpace);
Log.d("storage", usableSpace_str);
Log.d("storage", totalSpace_str);

/data/data/和/sdcard

  • /data/data/package-name/files: getFileDir().getPath();是一个应用程序的私有目录,只有当前应用程序有权限访问读写,其他应用无权限访问。一些安全性要求比较高的数据存放在该目录,一般用来存放size比较小的数据。
  • /sdcard: Enviroment.getExternalStorageDirectory().getPath();是一个外部存储目录,只要应用声明了<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>的一个权限,就可以访问读写sdcard目录;所以一般用来存放一些安全性不高的数据,文件size比较大的数据。

文件权限

linux下一个文件的权限由10位标示:

  • 第1位:文件的类型。
d:文件夹
l:快捷方式
 -:文件

其他9位每3位分为一组,每一组的3位按照rwx的顺序排列,没有该项权限就会"-"表示。如果用二进制表示,如果不是-就用1标示,是-用0标示;chmod指令赋权限。

  • 2-4: 该文件所属用户对本文件的权限。
  • 5-7:该文件所属用户组对本文件的权限。
  • 8-10:其他用户对该文件的权限。

如常用的chmod -R 777,777怎么来的?

rwx三个分别是1 1 1,转换成十进制位7,2-10一共三组。每组都赋予 全部权限,就是777

xml的生成

直接写

写一个备份短信(模拟)和恢复短信的功能。业务逻辑如下

a.备份
    1.封装短信数据到list中
    2.将list中的数据写到xml文件中。
b.恢复
    1.解析xml文件中短信数据,封装到list集合中
    2.将解析数据打印。
  1. 先写一个工具类SmsUtils来处理短信数据,实现备份和恢复的功能。
  2. 备份需要获取全部短信数据。写一个JavaBean封装短信数据,再将这些数据放入一个List里面(由SmsDao完成),再返回List给SmsUtils。

**一般JavaBean是使用私有的(private)的成员变量,使用get/set方法加以封装。Android里面用JavaBean,不要用get、set,因为get/set用到了反射机制,而android内存有限。所以成员变量设置为public,*可以用"."点运算符直接引用。**

package bean;

public class SmsBean {
    /*
    一般JavaBean是使用私有的(private)的成员变量,使用get/set方法加以封装。
    Android里面用JavaBean,不要用get、set,因为get/set用到了反射机制,而android内存有限。
    所以成员变量设置为public,可以用"."点运算符直接引用
     */
    public int id;
    public int number;
    public String date;
    public String msg;

   @Override
    public String toString() {
        return "SmsBean{" +
                "id=" + id +
                ", number=" + number +
                ", date='" + date + '\'' +
                ", msg='" + msg + '\'' +
                '}';
    }

}

假设这就是我们的所有短信数据,这里共五条,以列表的形式返回

package dao;

import java.util.ArrayList;
import java.util.List;

import bean.SmsBean;

public class SmsDao {
    public static List<SmsBean> getAllSms() {
        List<SmsBean> msgs = new ArrayList<>();
        for (int i = 10; i > 0; i--) {
            SmsBean msg = new SmsBean();
            msg.id = i;
            msg.msg = "请你吃饭,快出来!给你十秒 " + i;
            msg.number = 123456;
            msg.date = "2017/4/9";
            msgs.add(msg);
        }
        return msgs;
    }

}
package utils;

import android.content.Context;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.List;

import bean.SmsBean;
import dao.SmsDao;

public class SmsUtils {
    public static boolean backupSms(Context mContext) {
        // 获取所有sms
        List<SmsBean> allSms = SmsDao.getAllSms();

        //将数据以xml格式封装到一个StringBduilder中
        StringBuilder buffer = new StringBuilder();

        //封装一个声明头
        buffer.append("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>");
        //封装根节点
        buffer.append("<Smss>");
        //循环遍历list集合封装所有的短信
        for (SmsBean smsBean : allSms) {

            buffer.append("<Sms id = \"").append(smsBean.id).append("\">");

            buffer.append("<number>");
            buffer.append(smsBean.number);
            buffer.append("</number>");

            buffer.append("<msg>");
            buffer.append(smsBean.msg);
            buffer.append("</msg>");

            buffer.append("<date>");
            buffer.append(smsBean.date);
            buffer.append("</date>");

            buffer.append("</Sms>");

        }
        buffer.append("</Smss>");

        FileOutputStream out = null;
        BufferedWriter writer = null;
        // 需要文件名后缀,注意和SharedPreferences(不用后缀名,默认xml)区别开来
        try {
            out = mContext.openFileOutput("smss.xml", mContext.MODE_PRIVATE);
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.write(buffer.toString());
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                    e.printStackTrace();
            }
        }
        return false;
    }
    // TODO: 恢复短信
    public static void restoreSms() {
    }
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    tools:context="com.example.backrestore.MainActivity">

    <Button
        android:id="@+id/backup_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/backup"
        android:padding="16dp"
        />

    <Button
        android:id="@+id/restore_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/restore"
        android:layout_below="@id/backup_message"
        android:padding="16dp"/>

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="恢复的短信数据"
        android:layout_below="@id/restore_message"/>

    <TextView
        android:id="@+id/restore_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/text"/>
</RelativeLayout>
package com.example.backrestore;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import utils.SmsUtils;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Button btBackup;
    private Button btRestore;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        btBackup = (Button) findViewById(R.id.backup_message);
        btRestore = (Button) findViewById(R.id.restore_message);
        btBackup.setOnClickListener(this);
        btRestore.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.backup_message:
                boolean isBackup = SmsUtils.backupSms(mContext);
                if (isBackup) {
                    Toast.makeText(mContext, "备份成功", Toast.LENGTH_SHORT).show();
                } else  {
                    Toast.makeText(mContext, "备份失败", Toast.LENGTH_SHORT).show();

                }
                break;
            case R.id.restore_message:
                SmsUtils.restoreSms();
                break;
            default:
        }
    }
}

XmlSerializer序列化xml文件

backupSms()方法修改成即可

public static boolean backupSms_android(Context context){

        try {
            /* 使用Android的Xml包,可以直接Xml.newSerializer()和Xml.newPullParser()而不用
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlSerializer xmlSerializer = factory.newSerializer(); */

            //0.获取短信数据
            List<SmsBean> allSms = SmsDao.getAllSms();
            //1.通过Xml获取一个XmlSerializer对象
            XmlSerializer serializer = Xml.newSerializer();
            //2.设置XmlSerializer的一些参数,比如:设置xml写入到哪个文件中
            //os:xml文件写入流   encoding:流的编码
            serializer.setOutput(context.openFileOutput("backupsms2.xml", Context.MODE_PRIVATE), "utf-8");
            //3.序列化一个xml的声明头
            //encoding:xml文件的编码  standalone:是否独立
            serializer.startDocument("utf-8", true);
            //4.序列化一个根节点的开始节点
            //namespace:命名空间  name: 标签的名称。这里不指定就是null
            serializer.startTag(null, "Smss");
            //5.循环遍历list集合序列化一条条短信

            for (SmsBean smsBean : allSms) {
                serializer.startTag(null, "Sms");
                //name:属性的名称  value:属性值
                serializer.attribute(null, "id", smsBean.id+"");

                serializer.startTag(null, "number");
                //写一个标签的内容
                serializer.text(smsBean.number+"");
                serializer.endTag(null, "number");

                serializer.startTag(null, "msg");
                serializer.text(smsBean.msg);
                serializer.endTag(null, "msg");

                serializer.startTag(null, "date");
                serializer.text(smsBean.date);
                serializer.endTag(null, "date");

                serializer.endTag(null, "Sms");
            }

            //6.序列化一个根节点的结束节点
            serializer.endTag(null, "Smss");
            //7.将xml写入到文件中,完成xml的序列化,写入结束
            serializer.endDocument();
            return true;

        }catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

方便了不少,也不容易写错。

xml解析

解析方式

DOM Document Object Model 文档对象模型

核心思想: 把文档中所有内容都 封装成对象。

对象种类:

Document 整个(HTML,XML)文档
Element  文档中每一个标签都会被封装成Element对象
Attribute 标签上的每一个属性都会被封装成Attribute
Text     文档中,标签内的文本都会封装成Text对象
Common   注释,文档中的注释 ,会被封装成common.
  • Dom的优势:将文档结构(所有内容)都以对象的形式保留在了内存中。我们可以对内存中的(Dom树)进行增删改查操作并且操作很方便。
  • Dom的劣势:因为保留了全部文档内容,资源消耗比较大。

SAX解析思想

在读取xml文档时, 已经根据定义好的事件,对xml内容进行了筛选.解析完成后,内存中只保留了我们想要的内容。比较节约资源.。在资源比较匮乏的平台使用,比如手机。

缺点: 没有保留文档的结构, 无法进行增删改的操作。只能查询。

文档开始事件: startDocument
文档结束事件: endDocument
元素开始事件: startElement
元素结束事件: endElement
文本事件:    character

Pull解析

与Sax一样,都属于事件驱动的解析方式,相比Sax解析过程更加灵活。

  • SAX一旦开始解析就是从头读到尾,不解析完整个文档不会停.
  • pull解析较为灵活,是以事件为单位,手动向下继续.。如果获得到我们要找的内容,可以停止继续解析。

上面序列化后的xml大概长这样

<Smss>
    <Sms id="5">
        <numbe>123456</number>
        <msg>"请你吃饭,快出来!给你5秒 5"</msg>
        <date>"2017/4/10"</date>
    </Sms>

    <Sms id="5">
        <number>123456</number>
        <msg>"请你吃饭,快出来!给你5秒 5"</msg>
        <date>"2017/4/10"</date>
    </Sms>

    <Sms id="3">
        <number>123456</number>
        <msg>"请你吃饭,快出来!给你5秒 5"</msg>
        <date>"2017/4/10"</date>
    </Sms>
</Smss>

SmsUtils里面写恢复短信的方法

public static List<SmsBean> restoreSms(Context context) {
    List<SmsBean> smsBeanList = null;
    SmsBean sms = null;
    try {
        //1.通过Xml获取一个XmlPullParser对象
        XmlPullParser xmlPullParser = Xml.newPullParser();
        //2.设置XmlPullParser对象的参数,需要解析的是哪个xml文件,设置一个文件读取流
        xmlPullParser.setInput(context.openFileInput("smss.xml"), "utf-8");
        // 3. 获得当前事件类型
        int type = xmlPullParser.getEventType();
        // 4. 不是文档结尾就一直解析
        while (type != XmlPullParser.END_DOCUMENT) {
            // 获取当前元素/节点的名字
            String nodeName = xmlPullParser.getName();
            switch (type) {
                // 开始标签<abc>
                case XmlPullParser.START_TAG:
                    // 找到这个标签就新建列表
                    if (nodeName.equals("Smss")) {
                        smsBeanList = new ArrayList<>();
                        // 找到这个标签就新建sms
                    } else if (nodeName.equals("Sms")) {
                        sms = new SmsBean();
                        sms.id = Integer.parseInt(xmlPullParser.getAttributeValue(null, "id"));
                    } else if (nodeName.equals("number")) {
                        sms.number = Integer.parseInt(xmlPullParser.nextText());
                    } else if (nodeName.equals("msg")) {
                        sms.msg = xmlPullParser.nextText();
                    } else if (nodeName.equals("date")) {
                        sms.date = xmlPullParser.nextText();
                    }
                    break;

                // 结束标签</abc>
                case XmlPullParser.END_TAG:
                    // 如果结束标签是Sms则一条短信数据封装完毕封装
                    if (nodeName.equals("Sms")) {
                        smsBeanList.add(sms);
                    }
                    break;
                default:
            }
            // 下一个元素,返回事件类型
            type = xmlPullParser.next();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return smsBeanList;
}

再修改MainActivity,在case R.id.restore_message里处理逻辑

@Override
public void onClick(View v) {
    switch (v.getId()) {
    ...
        case R.id.restore_message:
            List<SmsBean> smss =  SmsUtils.restoreSms(mContext);
            if (smss != null) {
                restoreText.setText(smss.toString());
                Toast.makeText(mContext, "成功恢复"+smss.size()+"条短信", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(mContext, "恢复失败", Toast.LENGTH_SHORT).show();
            }

            break;
        default:
    }
}

看下结果,备份时候是5条。成功恢复!


by @sunhaiyu

2017.4.10

Android常用布局、文件存储与权限、XML的更多相关文章

  1. Android开发工程师文集-Fragment,适配器,轮播图,ScrollView,Gallery 图片浏览器,Android常用布局样式

    Android开发工程师文集-Fragment,适配器,轮播图,ScrollView,Gallery 图片浏览器,Android常用布局样式 Fragment FragmentManager frag ...

  2. Android常用布局和控件

    一.Android常用布局属性 1. LinearLayout的特有属性 android:orientation:设置布局排列方式   android:layout_weight:设置所占布局的权重  ...

  3. 如何把android中布局文件(.xml)与相关的类(.java)进行关联?

    eg:把一个布局文件名为page1.xml与MainActivity.java(工程自动生成)进行 1.在存放使用资源的res文件夹下的layout文件夹内新建一个XML布局文件,如命名为:page1 ...

  4. android studio布局文件/XML怎么代码补全

    android studio中的布局文件代码补全方式是打第一个字母就提示了,而java代码有时候要按快捷键. 布局文件的话呢,要写在标签开始处才提示,在标签闭合处有时候不提示,有时候在内容里也会有不提 ...

  5. android之外部文件存储和读取

    这次借用上次读写内部存储的代码,只是对将更换文件的读写路径即可.这里需要对获取SDcard的读写权限. 一.AndroidManifest.xml 这里增加了对外部存储设备的读写权限 <?xml ...

  6. Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起

    本文主要内容是讲解一个视图View或者一个ViewGroup对象是如何添加至应用程序窗口中的.下文中提到的窗口可泛指我们能看到的界面,包括一个Activity呈现的界面(我们可以将之理解为应用程序窗口 ...

  7. Xamarin.Android之布局文件智能提示问题

    一.前言 看到有人问关于xamarin.android的布局没智能提示问题(VS 2015),当然,写布局这东西没提示这是一件相对痛苦的事 ,所以这里就提供一个解决的方案! 二.解决方案 想要智能提示 ...

  8. Android 常用布局视图

    常用包 http://square.github.io/ EventBus Scroller 滚动 拖拽 # android.support.design.widget.CollapsingToolb ...

  9. Android中将布局文件转成bitmap

    在实践中发现,有些需要打印的小票高度小于屏幕的高度,而有些小票内容过多高度高于屏幕高度. 小于屏幕高度的布局文件转成bitmap较为容易,高于屏幕高度的布局文件转成长图bitmap较为复杂. 一.小于 ...

随机推荐

  1. Android Studio常用快捷键使用

    以下是我在编程中实际用上的Android Studio快捷键,基于Windows系统,在使用过程中会不断添加不断完善,OSX版本的在另外一篇博客 Ctrl+Alt+L 格式化代码,编写完成项目来一下, ...

  2. phpcms v9栏目列表调用每一篇文章内容方法

    {pc:content action="lists" catid="$catid" num="25" order="id DESC ...

  3. ecshop邮件订阅按“订阅”没反应

    原订阅邮件所使用的JS文件transport.js和JQuery冲突,会更改transport.js文件,用以下代码可同样实现订阅功能. <input type="text" ...

  4. Nodejs的模块系统以及require的机制

    一.简介 Nodejs 有一个简单的模块加载系统.在 Nodejs 中,文件和模块是一一对应的(每个文件被视为一个独立的模块),这个文件可能是 JavaScript 代码,JSON 或者编译过的C/C ...

  5. CSS 浅析position:relative/absolute定位方式

    ## 一.position:relative 相对定位 ## 分两种情况分析: · 无 position: relative: · 有 position: relative. 代码如下图: 显示效果如 ...

  6. PGI Compiler for OpenACC Output Syntax Highlighting

    PGI Compiler for OpenACC Output Syntax Highlighting When use the PGI compiler to compile codes with ...

  7. dubbo结构及通信简介

    一.导论 dubbo作为阿里开发优秀的rpc服务框架,现已广泛用于各大rpc项目之间的远程通信,虽然阿里现在已经没有维护dubbo的开发,但是其结构设计也是值得学习. 二.结构简介 这部分只是简单介绍 ...

  8. PouchDB 基础

    GUIDES http://pouchdb.com/guides/ 1.建立couchDB环境 下载并安装CouchDB: https://couchdb.apache.org/#download 测 ...

  9. Android 任何位置的可移动悬浮窗

    刚好要做这块的东西,所以网上翻了下资料,百度出来的基本上都是:默认起始位置左上角,还不能改动,一改动起始位置,第二次拖动就不正常了~~ 下面直接附上任意位置可拖动的源码(由于是demo写的比较乱): ...

  10. Linux配置LNMP环境(二)配置PHP

    前言:本教程安装的PHP版本php-5.6.30(官方最后更新日期2017-01-19),教程编写日期2017-07-02.本教程中的下载地址是在写教程的时候从官方复制的,时间过长可能会有变化. 安装 ...