本系列学习笔记第6章

前言

打算把android基本知识点写一个系列,旨在把android基础书,例如《Android 第一行代码 第2版》、《爱上android》、《疯狂android讲义》等书的一些知识点记录一下,会持续更新内容,为了方便自己复习,也希望可以帮助到大家!

目录

1、持久化技术简介2、文件存储    2.1 手机内存存储        2.1.1 将数据存储到文件中        2.1.2 从文件中读取数据    2.2 SD卡存储        2.2.1 检测SD卡是否可用        2.2.2 共有和私有的文件路径        2.2.3 SD卡存储参考工具类        2.2.4 查询剩余空间    3、SharedPreferences存储    3.1 将数据存储到SharedPreferences中    3.2 从SharedPreferences中读取数据    3.3 实现记住密码功能4、SQLite存储    4.1 创建数据库    4.2 升级数据库    4.3 添加数据    4.4 更新数据    4.5 删除数据    4.6 查询数据    4.7 使用SQL操作数据库    4.8 对事务的认识和基本使用5、使用LitePal操作数据库    5.1 LitePal简介    5.2 配置LitePal    5.3 创建和升级数据库    5.4 使用LitePal添加数据    5.5 使用LitePal更新数据    5.6 使用LitePal删除数据    5.7 使用LitePal查询数据

1、持久化技术简介

数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不会扔失。保存在内存中的数据是处于瞬时状态的,而保存在存储设备中的数据是处于持久状态的,持久化技术则提供了一种机制可以让数据在瞬时状态和持久化状态之间进行转换。

android中的数据存储主要分为5大部分:
1) 文件存储
2) SharedPreference存储
3)数据库存储
4)ContentProvider存储
5)网络存储

2、文件存储

文本存储是android中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有的数据都是原封不动的保存到文件当中,因而它比较适用于存储一下比较简单的文本数据或二进制数据。

2.1 手机内存存储

常用的api如下:


image.png
2.1.1 将数据存储到文件中

activity_main.xml文件

<?xml version="1.0" encoding="utf-8"?>    

MainActivity.java文件

public class MainActivity extends AppCompatActivity {    private EditText etSave;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        etSave = (EditText) findViewById(R.id.et_save);    }    @Override    protected void onDestroy() {        super.onDestroy();        String inputText = etSave.getText().toString();        save(inputText);    }    private void save(String data){         FileOutputStream fos = null;         BufferedWriter writer = null;        try {            //写入file目录            fos = openFileOutput("data", Context.MODE_PRIVATE);            //写入缓存目录//            fos = new FileOutputStream(new File(getCacheDir(),"cache.txt"));            writer = new BufferedWriter(new OutputStreamWriter(fos));            writer.write(data);        } catch (IOException e) {            e.printStackTrace();        }finally {            try {                if (writer != null){                    writer.close();                }            }catch (IOException e){                e.printStackTrace();            }        }    }}
image.png image.png image.png
image.png
2.1.2 从文件中读取数据
public class MainActivity extends AppCompatActivity {    private EditText etSave;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        etSave = (EditText) findViewById(R.id.et_save);        String inputText = load();        if (!TextUtils.isEmpty(inputText)){            etSave.setText(inputText);            etSave.setSelection(inputText.length());            Toast.makeText(this, "数据读取成功", Toast.LENGTH_SHORT).show();        }    }    @Override    protected void onDestroy() {        super.onDestroy();        String inputText = etSave.getText().toString();        save(inputText);    }    private void save(String data){         FileOutputStream fos = null;         BufferedWriter writer = null;        try {            //写入file目录            fos = openFileOutput("data", Context.MODE_PRIVATE);            //写入缓存目录//            fos = new FileOutputStream(new File(getCacheDir(),"cache.txt"));            writer = new BufferedWriter(new OutputStreamWriter(fos));            writer.write(data);        } catch (IOException e) {            e.printStackTrace();        }finally {            try {                if (writer != null){                    writer.close();                }            }catch (IOException e){                e.printStackTrace();            }        }    }    private String load(){        FileInputStream in = null;        BufferedReader reader = null;        StringBuffer sb = new StringBuffer();        try {            in = openFileInput("data");            reader = new BufferedReader(new InputStreamReader(in));            String line = "";            while((line = reader.readLine()) != null){                sb.append(line);            }        } catch (IOException e) {            e.printStackTrace();        }finally {            if (reader != null){                try {                    reader.close();                }catch (IOException e){                    e.printStackTrace();                }            }        }        return sb.toString();    }}
image.png
2.2 SD卡存储
image.png image.png
2.2.1 检测SD卡是否可用
    /**     * 检查SD卡是否可写     * @return     */    public static boolean isExternalStorageWritable(){        String state = Environment.getExternalStorageState();        if (Environment.MEDIA_MOUNTED.equals(state)){            return true;        }        return false;    }    /**     * 检查SD卡是否可读     * @return     */    public static boolean isExternalStorageReadable(){        String state = Environment.getExternalStorageState();        if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)){            return true;        }        return false;    }}
2.2.2 共有和私有的文件路径
image.png
image.png
    private File getAlbumStorageDir(String albumName){        //获取保存图片的文件夹        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),albumName);        if (!file.mkdir()){            Log.d(TAG,"文件夹不存在");            //如果文件不存在的话,就创建文件            try {                file.createNewFile();            } catch (IOException e) {                e.printStackTrace();            }        }        return file;    }
image.png
image.png
    private File getAlbumStorageDir(Context context,String albumName){        //获取保存图片的文件夹        File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES),albumName);        if (!file.mkdir()){            Log.d(TAG,"文件夹不存在");            //如果文件不存在的话,就创建文件            try {                file.createNewFile();            } catch (IOException e) {                e.printStackTrace();            }        }        return file;    }
image.png image.png
    compile 'com.tbruyelle.rxpermissions:rxpermissions:0.7.0@aar'    compile 'io.reactivex:rxjava:1.1.6' //需要引入Rxjava
<?xml version="1.0" encoding="utf-8"?>        
public class MainActivity extends AppCompatActivity {    private EditText etSave;    private PrintStream printStream;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        etSave = (EditText) findViewById(R.id.et_save);      }    @Override    protected void onDestroy() {        super.onDestroy();         }       public void saveTo(View view){        if (ExternalStorageUtils.isExternalStorageWritable()){            final String content = etSave.getText().toString();            Toast.makeText(this, "SD卡可用", Toast.LENGTH_SHORT).show();            RxPermissions.getInstance(this)                    //申请权限                    .request(Manifest.permission.WRITE_EXTERNAL_STORAGE)                    .subscribe(new Action1() {                        @Override                        public void call(Boolean aBoolean) {                            //权限如果通过了,就保存到SD卡中                            if (aBoolean){                                saveFileToSDCard(content);                            }                        }                    });        }    }    /**     * 保存内容的SD卡中     */    private void saveFileToSDCard(String content){        File file = new File(Environment.getExternalStorageDirectory(),"cache.text");        FileOutputStream fos = null;        try {            fos = new FileOutputStream(file);            printStream = new PrintStream(fos);            printStream.print(content);        }catch (IOException e){            e.printStackTrace();        }finally {            try {                if (printStream != null){                    printStream.close();                }            }catch (Exception e){                e.printStackTrace();            }        }    }}
2.2.3 SD卡存储参考工具类
image.png
public class FileUtils {    // 缓存的文件    public static final String CACHE_DIR = "cache";    // 保存图片的路径    public static final String ICON_DIR = "icon";    // 下载文件的路径    public static final String DOWNLOAD_DIR = "download";    // 保存到SD卡的根目录    private static final String ROOT_DIR = "likeDev";    public static String getCacheDir(Context context) {        return getDir(context,CACHE_DIR);    }    public static String getIconDir(Context context) {        return getDir(context,ICON_DIR);    }    public static String getDownloadDir(Context context) {        return getDir(context,DOWNLOAD_DIR);    }    public static String getDir(Context context,String name) {        StringBuilder sb = new StringBuilder();        if (isSDCardAvailable()) {            sb.append(getExternalStoragePath());        } else {            sb.append(getCachePath(context));        }        sb.append(name);        sb.append(File.separator);        String path = sb.toString();        if (createDirs(path)) {            return path;        } else {            return null;        }    }    // 返回SD卡路径    private static String getExternalStoragePath() {        return Environment.getExternalStorageDirectory().getAbsolutePath() +                File.separator +                ROOT_DIR +                File.separator;    }    /**     * 判断sd卡是否挂载     */    public static boolean isSDCardAvailable() {        return Environment.MEDIA_MOUNTED.equals(Environment                .getExternalStorageState());    }    // 创建文件夹    private static boolean createDirs(String path) {        File file = new File(path);        return !(!file.exists() || !file.isDirectory()) || file.mkdirs();    }    private static String getCachePath(Context context) {        File f = context.getCacheDir();        return f.getAbsolutePath() + File.separator;    }}
2.2.4 查询剩余空间

getFreeSpace():获取剩余空间
getTotalSpace() : 获取总空间

        //获取剩余空间        long freeSpace = Environment.getExternalStorageDirectory().getFreeSpace();//        android.text.format.Formatter        String formatFileSize = Formatter.formatFileSize(this, freeSpace);

3、SharedPreferences存储

不同于文件的存储方式,SharedPreferences是用键值对的方式存储数据

3.1 将数据存储到SharedPreferences中

Android中提供了3种方式来获取SharedPreferences()方法
1)Context类中的getSharedPreferences(String name, int mode)方法
第一个参数是:指定的SharePreferences文件的名称
第二个参数是:用于指定操作模式,目前只有MODE_PRIVATE

2)Activity类中的getPreferences(int mode)方法
参数只有操作模式:MODE_PRIVATE

3)PreferenceManager.getDefaultSharedPreferences(Context context)

使用SharedPreferences文件存储数据的步骤:
1)调用SharedPreferences对象的edit()方法来获取SharedPreferences,Editor对象
2)向SharedPreferences,Editor对象中添加数据,例如添加一个String类型的数据,就使用PutString()
3)apply()方法提交(线程不安全,效率高)或者commit()方法提交(线程安全,效率低)

基本使用如下:

        SharedPreferences preferences = getSharedPreferences("data", MODE_PRIVATE);        SharedPreferences.Editor editor = preferences.edit();        editor.putString("name","zlc");        editor.putInt("age",28);        editor.putBoolean("married",false);        editor.apply();
3.2 从SharedPreferences中读取数据
        SharedPreferences sp = getSharedPreferences("data", MODE_PRIVATE);        String name = sp.getString("name", "");        int age = sp.getInt("age", 0);        boolean married = sp.getBoolean("married", false);
3.3 实现记住密码功能
public class ActivityCollector {    public static List activities = new ArrayList<>();    public static void addActivity(Activity activity) {        activities.add(activity);    }    public static void removeActivity(Activity activity) {        activities.remove(activity);    }    public static void finishAll() {        for (Activity activity : activities) {            if (!activity.isFinishing()) {                activity.finish();            }        }    }}
public class BaseActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        ActivityCollector.addActivity(this);    }    @Override    protected void onResume() {        super.onResume();    }    @Override    protected void onPause() {        super.onPause();    }    @Override    protected void onDestroy() {        super.onDestroy();        ActivityCollector.removeActivity(this);    }}
<?xml version="1.0" encoding="utf-8"?>                                                                                
public class LoginActivity extends BaseActivity {    private SharedPreferences pref;    private SharedPreferences.Editor editor;    private EditText accountEdit;    private EditText passwordEdit;    private Button login;    private CheckBox rememberPass;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_login);        pref = PreferenceManager.getDefaultSharedPreferences(this);        accountEdit = (EditText) findViewById(R.id.account);        passwordEdit = (EditText) findViewById(R.id.password);        rememberPass = (CheckBox) findViewById(R.id.remember_pass);        login = (Button) findViewById(R.id.login);        boolean isRemember = pref.getBoolean("remember_password", false);        if (isRemember) {            // 将账号和密码都设置到文本框中            String account = pref.getString("account", "");            String password = pref.getString("password", "");            accountEdit.setText(account);            passwordEdit.setText(password);            rememberPass.setChecked(true);        }        login.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                String account = accountEdit.getText().toString();                String password = passwordEdit.getText().toString();                // 如果账号是admin且密码是123456,就认为登录成功                if (account.equals("admin") && password.equals("123456")) {                    editor = pref.edit();                    if (rememberPass.isChecked()) { // 检查复选框是否被选中                        editor.putBoolean("remember_password", true);                        editor.putString("account", account);                        editor.putString("password", password);                    } else {                        editor.clear();                    }                    editor.apply();                    Intent intent = new Intent(LoginActivity.this, MainActivity.class);                    startActivity(intent);                    finish();                } else {                    Toast.makeText(LoginActivity.this, "account or password is invalid",                            Toast.LENGTH_SHORT).show();                }            }        });    }}

4、SQLite存储

基本数据类型:
integer: 整形
real : 浮点型
text : 文本类型
blob : 二进制类型

4.1 创建数据库

activity_main.xml文件

<?xml version="1.0" encoding="utf-8"?>    

MyDatabaseHelper.java文件

public class MyDatabaseHelper extends SQLiteOpenHelper {    private static final String CREATE_BOOK = "create table Book ("            +"id integer primary key autoincrement, "            +"author text, "            +"price real, "            +"pages integer, "            +"name text)"            ;    private Context mContext;    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {        super(context, name, factory, version);        mContext = context;    }    @Override    public void onCreate(SQLiteDatabase db) {        db.execSQL(CREATE_BOOK);        Toast.makeText(mContext, "数据库创建成功!", Toast.LENGTH_SHORT).show();    }    @Override    public void onUpgrade(SQLiteDatabase db, int i, int i1) {    }}

MainActivity .java文件

public class MainActivity extends AppCompatActivity {    private MyDatabaseHelper dbHelper;    private Button btnCreate;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        btnCreate = (Button) findViewById(R.id.btn_create);        dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);        btnCreate.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                dbHelper.getWritableDatabase();            }        });    }}
image.png

按照如下的adb指令即可以查看数据库是否创建成功


image.png image.png

或者使用AndroidStudio也可以执行查询的命令,参考例子如下:


image.png image.png
4.2 升级数据库

MyDatabaseHelper.java文件

public class MyDatabaseHelper extends SQLiteOpenHelper {    private static final String CREATE_BOOK = "create table Book ("            +"id integer primary key autoincrement, "            +"author text, "            +"price real, "            +"pages integer, "            +"name text)"            ;    private static final String CREATE_CATEGORY = "create table Category ("            +"id integer primary key autoincrement, "            +"category_name text, "            +"category_code integer)"            ;    private Context mContext;    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {        super(context, name, factory, version);        mContext = context;    }    @Override    public void onCreate(SQLiteDatabase db) {        db.execSQL(CREATE_BOOK);        db.execSQL(CREATE_CATEGORY);        Toast.makeText(mContext, "数据库创建成功!", Toast.LENGTH_SHORT).show();    }    @Override    public void onUpgrade(SQLiteDatabase db, int i, int i1) {        db.execSQL("drop table if exists Book");        db.execSQL("drop table if exists Category");        onCreate(db);    }}
public class MainActivity extends AppCompatActivity {    private MyDatabaseHelper dbHelper;    private Button btnCreate;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        btnCreate = (Button) findViewById(R.id.btn_create);        dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);        btnCreate.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                dbHelper.getWritableDatabase();            }        });    }}
image.png
4.3 添加数据

activity_main.xml文件

<?xml version="1.0" encoding="utf-8"?>    

MyDatabaseHelper .java文件

public class MyDatabaseHelper extends SQLiteOpenHelper {    private static final String CREATE_BOOK = "create table Book ("            +"id integer primary key autoincrement, "            +"author text, "            +"price real, "            +"pages integer, "            +"name text)"            ;    private static final String CREATE_CATEGORY = "create table Category ("            +"id integer primary key autoincrement, "            +"category_name text, "            +"category_code integer)"            ;    private Context mContext;    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {        super(context, name, factory, version);        mContext = context;    }    @Override    public void onCreate(SQLiteDatabase db) {        db.execSQL(CREATE_BOOK);        db.execSQL(CREATE_CATEGORY);        Toast.makeText(mContext, "数据库创建成功!", Toast.LENGTH_SHORT).show();    }    @Override    public void onUpgrade(SQLiteDatabase db, int i, int i1) {        db.execSQL("drop table if exists Book");        db.execSQL("drop table if exists Category");        onCreate(db);    }}

MainActivity.java文件

public class MainActivity extends AppCompatActivity {    private MyDatabaseHelper dbHelper;    private Button btnCreate;    private Button btnAdd;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        btnCreate = (Button) findViewById(R.id.btn_create);        btnAdd = (Button) findViewById(R.id.btn_add);        dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);        btnCreate.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                dbHelper.getWritableDatabase();            }        });        btnAdd.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                SQLiteDatabase db = dbHelper.getWritableDatabase();                ContentValues values = new ContentValues();                values.put("name","第一行代码");                values.put("author","guolin");                values.put("pages",500);                values.put("price",58.0);                db.insert("Book",null,values);                values.clear();                values.put("name","爱上android");                values.put("author","ylinlin");                values.put("pages",300);                values.put("price",48.0);                db.insert("Book",null,values);            }        });    }}
image.png

添加数据的另外一种写法例子,直接使用SQL的语句,如下图所示:


image.png image.png
4.4 更新数据
//        select * from Book;        btnUpdate.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                SQLiteDatabase db = dbHelper.getWritableDatabase();                ContentValues values = new ContentValues();                values.put("price",35.0);                db.update("Book",values,"name = ?",new String[]{"第一行代码"});            }        });

修改数据的另外一种写法例子,直接使用SQL的语句,如下图所示:


image.png
4.5 删除数据
        btnDelete.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                SQLiteDatabase db = dbHelper.getWritableDatabase();                db.delete("Book","pages > ?",new String[]{"400"});            }        });
image.png
4.6 查询数据
btnQuery.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                SQLiteDatabase db = dbHelper.getWritableDatabase();                Cursor cursor = db.query("Book", null, null, null, null, null, null);                if (cursor.moveToFirst()){                    do{                        String name = cursor.getString(cursor.getColumnIndex("name"));                        String author = cursor.getString(cursor.getColumnIndex("author"));                        int pages = cursor.getInt(cursor.getColumnIndex("pages"));                        double price = cursor.getDouble(cursor.getColumnIndex("price"));                        Log.d(TAG,"name=========>"+name);                        Log.d(TAG,"author=========>"+author);                        Log.d(TAG,"pages=========>"+pages);                        Log.d(TAG,"price=========>"+price);                    }while (cursor.moveToNext());                }                cursor.close();            }        });

查询数据的另外一种写法例子,直接使用SQL的语句,如下图所示:


image.png
image.png
image.png
image.png
image.png
4.7 使用SQL操作数据库

对应SQL语句很熟悉的开发人员,上面的增删改查操作可以用SQL语句来代替,具体用法参考于:
http://www.w3school.com.cn/sql/sql_select.asp

4.8 对事务的认识和基本使用
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png

5、使用LitePal操作数据库

5.1 LitePal简介

LitePal是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式,并且将我们平常开发最常用的一些数据库功能进行了封装,不用编写一行SQL语句就可以完成各种建表和增删改查的操作。

5.2 配置LitePal
image.png image.png
image.png image.png
5.3 创建和升级数据库

Book.java 实体类

public class Book extends DataSupport {    private int id;    private String author;    private double price;    private int pages;    private String name;    private String press;    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getAuthor() {        return author;    }    public void setAuthor(String author) {        this.author = author;    }    public double getPrice() {        return price;    }    public void setPrice(double price) {        this.price = price;    }    public int getPages() {        return pages;    }    public void setPages(int pages) {        this.pages = pages;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getPress() {        return press;    }    public void setPress(String press) {        this.press = press;    }}
        btnCreate.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                LitePal.getDatabase();            }        });
image.png

升级数据库

public class Category extends DataSupport{    private int id;    private String categoryName;    private int categoryCode;    public void setId(int id) {        this.id = id;    }    public void setCategoryName(String categoryName) {        this.categoryName = categoryName;    }    public void setCategoryCode(int categoryCode) {        this.categoryCode = categoryCode;    }}
<?xml version="1.0" encoding="utf-8"?>                                
image.png
5.4 使用LitePal添加数据
        btnAdd.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Book book = new Book();                book.setName("The Da Vinci Code");                book.setAuthor("Dan Brown");                book.setPages(454);                book.setPrice(16.96);                book.setPress("Unknow");                book.save();            }        });
5.5 使用LitePal更新数据
        btnUpdate.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Book book = new Book();                book.setPrice(14.95);                book.setPress("Anchor");                book.updateAll("name = ? and author = ?", "The Da Vinci Code", "Dan Brown");            }        });
5.6 使用LitePal删除数据
        btnDel.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                DataSupport.deleteAll(Book.class,"price < ?","15");            }        });
5.7 使用LitePal查询数据
        btnQuery.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                List books = DataSupport.findAll(Book.class);            }        });

更多关于Litepal的使用方法,请查看github地址:
https://github.com/LitePalFramework/LitePal

更多相关文章

  1. Android.jar文件分析
  2. android listview 详解 开发指南
  3. Android系统进程Zygote启动过程的源代码分析(1)
  4. Android(安卓)studio使用NDK编译和调试(生成so文件)
  5. Android中数据共享机制的实现——ContentProvider的应用从入门到
  6. Android(安卓)混淆打包
  7. Service与Android系统设计(2)
  8. Android(安卓)TabLayout 实现底部导航栏和顶部导航栏
  9. ORB_SLAM2在android studio上用cmake编译

随机推荐

  1. android webview字体大小的控制
  2. 四十七、实现调用Android手机的拍照功能
  3. 获取android手机基本信息
  4. 介绍一个Android开源项目:GifView——Andr
  5. Android(安卓)APK安装过程及原理详解
  6. Android客户端连接tomcat时出错:connect f
  7. android颜色设使用方法
  8. 简单的中间文字两边按钮
  9. Android中的MessageQueue,Handler,Looper
  10. Android学习笔记之mainfest文件中android