Android基础教程——从入门到精通(上)_生皮鞣制工艺流程

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
img

正文

使用androidstudio提供的模拟器,或者自行下载第三方安卓模拟器(雷电模拟器)

二、简单控件

1. 文本显示

设置文本内容有两种方式:

  • 在 XML 文件中通过属性 android:text 设置文本
  • 在 Java 代码中调用文本视图对象的 setText 方法设置文本

引用字符串资源:

  • 在XML文件中引用(@string/xxx)
  • 在Java代码中引用(R.string.xxx)

其余设置文本字体大小,颜色等都是可以通过关键词+代码提示很容易就能知道怎么写,这里就不赘述。

2. 按钮

Button继承于TextView,因此它们拥有的属性都是共通的。

除此之外,Button最重要的是点击事件。

  • 点击监听器:通过setOnClickListener方法设置。按钮被按住少于500毫秒时,会触发点击事件。
  • 长按监听器:通过setOnLongClickListener方法设置。按钮被按住超过500毫秒时,会触发长按事件。

3. 常用布局

(1)线性布局LinearLayout

特点:要不水平排列,要不竖直排列,通过orintation进行设置(horiztal为水平,vertical为竖直)

权重属性:通过layout_weight来设置,在线性布局的直接下级进行设置,表示该下级布局占据的宽高比例。

  • layout_width填0dp时,layout_weight表示水平方向的宽度比例。
  • layout_height填0dp时,layout_weight表示垂直方向的高度比例。
(3)相对布局RelativeLayout

相对布局中的视图位置由两个因素所影响:

  • 与该视图平级的其他视图
  • 上级视图(也就是它归属的RelativeLayout)

相对位置的一些取值:

image-20230107003412969

(3)网格布局GridLayout

顾名思义该布局适用于表格类型的布局。

4. 图像显示

图片一般放在res/drawable目录下,设置图像显示一般有两种方法:

  • 在XML文件中,通过属性android:src设置图片资源,属性值格式形如 @drawable/不含扩展名的图片名称。
  • 在Java代码中,调用setImageResource方法设置图片资源,方法参数格式形如 R.drawable.不含扩展名的图片名称。
(1)图像的缩放问题:

ImageView本身默认图片居中显示,若要改变图片的显示方式,可通过scaleType属性设定,该属性的取值说明如下:

image-20230107004159846

(2)图像按钮ImageButton:

ImageButton是显示图片的图像按钮,但它继承自ImageView,而非继承Button。

ImageButton和Button之间的区别有:

  • Button既可显示文本也可显示图片,ImageButton只能显示图片不能显示文本。
  • ImageButton上的图像可按比例缩放,而Button通过背景设置的图像会拉伸变形。
  • Button只能靠背景显示一张图片,而ImageButton可分别在前景和背景显示图片,从而实现两张图片叠加的效果。

三、Activity

Activity是安卓开发四大组件之一,非常重要。

1. Activity的启动和结束

Activity的启动这里指的是跳转,从一个页面跳转到一个新的页面,就相当于启动了一个新的页面。

示例:

bt.setOnClickListener(new View.OnClickListener(){

@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, MainActivity2.class);
startActivity(intent);
}
});

结束Activity:调用 finish()

2. Activity的生命周期

onCreate:此时将页面布局加载到内存中,初始化页面。

onStart:将页面展示在屏幕。

onResume:此时页面能够和用户进行交互。

onPause:页面进入暂停状态,无法和用户进行交互。

onStop:页面不在屏幕显示。

onDestory:回收Activity占用的资源,彻底销毁该Activity。

onRestart:onStop状态可以转为onRestart状态。

onNewIntent:重用已存在的活动实例。如果一个Activity已经启动了,并且存在与当前栈,而当前栈的启动模式为SingleTask,SingleInstance,SingleTop(此时在任务栈顶端),那么再次启动该Activity的话,并不会重新进行onCreate,而是会执行onNewIntent方法。

image-20230108223537951

3. Activity的启动模式

Android允许在创建Activity时设置启动模式,通过启动模式控制Activity的出入栈行为。

(1)静态设置

设置方式:打开AndroidManifest.xml文件,给activity添加属性android:launchMode。如以下表示该activity使用standard标准模式,默认也是标准模式。

launchMode的取值有:

image-20230109161545002

image-20230109161736927

image-20230109161750015

image-20230109161845199

image-20230109161930718

(2)动态设置

通过 Intent 动态设置 Activity启动模式:

intent.setFlags();

4. Activity之间传递信息

Intent能够让Android各组件之间进行沟通。

Intent可以完成3部分工作:

  • 表明本次通信从哪里来,往哪里走,要怎么走。
  • 发送方可以携带消息给接收方,接收方可以从收到的Intent解析数据。
  • 发送方如果想要知道接收方的处理结果,接收方也可以通过Intent返回结果。

Intent的一些组成元素:

image-20230109163305363

(1)显式Intent和隐式Intent
1. 显式Intent

创建方式:

  • 在Intent的构造函数中指定:

Intent intent = new Intent(this, NextActivity.class);

  • 调用setClass指定:

Intent intent = new Intent();
intent.setClass(this, NextActivity.class);

  • 调用setComponent指定:

Intent intent = new Intent();
ComponentName component = new ComponentName(this, NextActivity.class);
intent.setComponent(component);

2. 隐式Intent:

没有明确指定所要跳转的页面,而是通过一些动作字符串来让系统自动匹配。

通常是App不想向外暴露Activity的名称,只给出一些定义好的字符串。这些字符串可以自己定义,也有系统定义的。

常见的系统动作如下:

image-20230109224228884

下面以调用系统拨号页面举例:

String phone = “12345”;
Intent intent = new Intent();
//这里表示设置意图动作为准备拨号
intent.setAction(Intent.ACTION_DIAL);
intent.setData(Uri.parse(“tel:” + phone));
startActivity(intent);

如果想要跳转到自己定义的activity:

步骤一:在AndroidManifest.xml找到该activity,添加action和category标签,同时设置exported为true,表示允许被其他activity调用。

image-20230109230403488

步骤二:调用过程和上面一样:

Intent intent = new Intent();
intent.setAction(“android.intent.action.activity2”);
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);

(2)向下一个Activity发送消息:

Intent重载了很多putExtra方法用于传递各种类型的信息,包括整数类型,字符串等。但是显然通过调用putExtra方法会很不好管理,因为数据都是零碎传递。所以Android引入了Bundle,其内部是一个Map,使用起来也和Map一样。

image-20230109165406124

示例:

Intent intent = new Intent(this, NextActivity.class);
//通过bundle包装数据
Bundle bundle = new Bundle();
bundle.putString(“stringKey”, “stringValue”);
intent.putExtras(bundle);
startActivity(intent);

然后下一个Activity就可以通过intent获取到所想要的数据了:

Bundle bundle = getIntent().getExtras();
String stringValue = bundle.getString(“stringKey”);

(3)向上一个Activity返回消息:

上一个页面跳转到下一个页面,同时携带数据:

private ActivityResultLauncher register;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);

findViewById(R.id.bt).setOnClickListener(this);

//回调函数,返回到这个页面时所执行的程序
register = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback() {
//回调函数
@Override
public void onActivityResult(ActivityResult result) {
if (result != null) {
Intent intent = result.getData();
if (intent != null && result.getResultCode() == Activity.RESULT_OK) {
//获取到返回的数据
Bundle bundle = intent.getExtras();
//…
}
}
}
});
}

@Override
public void onClick(View v) {
Intent intent = new Intent(this, MainActivity3.class);
//跳转下一页面
register.launch(intent);

}

下一个页面接受到数据,处理之后返回结果给上一个页面:

Bundle bundle = getIntent().getExtras();
//…页面进行处理
//返回数据给上一个页面
Bundle bundle = new Bundle();
bundle.putString(“stringKey”, “stringValue”);
intent.putExtras(bundle);
setResult(Activity.RESULT_OK, intent);
finish();

5. Activity获取一些附加信息

(1)获取资源信息:

//获取strings.xml中的字符串资源
String text = getString(R.string.text);
//获取color.xml中的颜色资源
int black = getColor(R.color.black);

(2)获取元数据信息:

try {
//获取包管理器
PackageManager pm = getPackageManager();
//获取当前的Activity信息
ActivityInfo activityInfo = pm.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
Bundle bundle = activityInfo.metaData;
String text2 = bundle.getString(“text2”);

} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}

四、数据存储

1. 共享参数SharedPreferences

(1)使用:

sharedPreferences是安卓的一个轻量级存储工具,采用的方式是key-value,以xml文件形式存在,文件路径为/data/data/应用包名/shared_prefs/文件名.xml。

适合场景:

  1. 简单且孤立的数据
  2. 文本数据,二进制数据则不合适
  3. 需要持久化的数据,也就是重启APP后数据仍然存在且有效。

实际开发中,sharedPreferences经常用来存储的数据有:APP的个性化配置信息,用户使用APP的行为信息等。

sharedPreferences对数据的存储和读取类似Map,提供put和set方法。

获取数据可以通过SharedPreferences对象获取:

//第一个参数表示文件名,第二个参数表示私有模式
SharedPreferences shared = getSharedPreferences(“fileName”, MODE_PRIVATE);
String name = shared.getString(“name”);

而存储数据则还需要借助Editor类:

SharedPreferences.Editor editor = shared.edit();
editor.putString(“name”, “oymn”);
editor.putInt(“age”, 20);
editor.commit();

(2)应用实例:记住密码功能
  1. 声明一个共享参数对象,并在onCreate中调用getSharedPreferences方法获取共享参数的实例。
  2. 登录成功时,如果用户勾选了“记住密码”,就使用共享参数保存手机号码与密码。

所以在登录页面的onCreat方法中添加获取共享参数的代码:

// 从share_login.xml获取共享参数对象
mShared = getSharedPreferences(“share_login”, MODE_PRIVATE);
// 获取共享参数保存的手机号码
String phone = mShared.getString(“phone”, “”);
// 获取共享参数保存的密码
String password = mShared.getString(“password”, “”);
et_phone.setText(phone); // 往手机号码编辑框填写上次保存的手机号
et_password.setText(password); // 往密码编辑框填写上次保存的密码

接着在登录成功方法中添加保存功能:

// 如果勾选了“记住密码”,就把手机号码和密码都保存到共享参数中
if (isRemember) {
SharedPreferences.Editor editor = mShared.edit(); // 获得编辑器的对象
editor.putString(“phone”, et_phone.getText().toString()); // 添加名叫phone的手机号码
editor.putString(“password”, et_password.getText().toString()); // 添加名叫password的密码
editor.commit(); // 提交编辑器中的修改
}

2. 数据库SQLite

SQLite是安卓的一种小巧的嵌入式数据库,基本使用和思路和Mysql无异。

(1)SQLiteDatabase

java代码层面借助SQLiteDatabase来对SQLite进行操作。

//创建数据库text.db
SQLiteDatabase db = openOrCreateDatabase(getFileDir() + “/test.db”, Context.MODE_PRIVATE, null);

image-20230112213636282

(2)SQLiteOpenHelper

由于SQLiteDatabase存在局限性,一不小心就会重复打开数据库,处理数据库的升级也不方便;因此Android提供了数据库帮助器SQLiteOpenHelper,帮助开发者合理使用SQLite。

SQLiteOpenHelper的具体使用步骤如下:

  • 步骤一,新建一个继承自SQLiteOpenHelper的数据库操作类,按提示重写onCreate和onUpgrade两个方法。其中,onCreate方法只在第一次打开数据库时执行,在此可以创建表结构;而onUpgrade方法在数据库版本升高时执行,在此可以根据新旧版本号变更表结构。
  • 步骤二,为保证数据库安全使用,需要封装几个必要方法,包括获取单例对象、打开数据库连接、关闭数据库连接,说明如下:
  • 获取单例对象:确保在App运行过程中数据库只会打开一次,避免重复打开引起错误。
  • 打开数据库连接:SQLite有锁机制,即读锁和写锁的处理;故而数据库连接也分两种,读连接可调用getReadableDatabase方法获得,写连接可调用getWritableDatabase获得。
  • 关闭数据库连接:数据库操作完毕,调用数据库实例的close方法关闭连接。
  • 步骤三, 提供对表记录增加、删除、修改、查询的操作方法。能被SQLite直接使用的数据结构是ContentValues类,它类似于映射Map,也提供了put和get方法存取键值对。
  • 区别之处在于:ContentValues的键只能是字符串,不能是其他类型。ContentValues主要用于增加记录和更新记录,对应数据库的insert和update方法。
  • 记录的查询操作用到了游标类Cursor,调用query和rawQuery方法返回的都是Cursor对象,若要获取全部的查询结果,则需根据游标的指示一条一条遍历结果集合。Cursor的常用方法可分为3类,说明如下:

image-20230112214321301

(3)代码举例:

public class UserDBHelper extends SQLiteOpenHelper {

private static final String DB_NAME = “user.db”; //数据库名称
private static final int DB_VERSION = 1; //数据库的版本号
private static UserDBHelper helper = null; //单例
private SQLiteDatabase sdb = null; //数据库实例
public static final String TABLE_NAME = “user_info”; //表名

public UserDBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}

public UserDBHelper(Context context, int version) {
super(context, DB_NAME, null, version);
}

//通过单例模式获取 UserDBHelper 的唯一实例
public static synchronized UserDBHelper getInstance(Context context, int version) {
if (version > 0 && helper == null) {
helper = new UserDBHelper(context, version);
} else if (helper == null) {
helper = new UserDBHelper(context);
}

return helper;
}

//打开读连接
public SQLiteDatabase openReadLink() {
if (sdb == null || !sdb.isOpen()) {
sdb = helper.getReadableDatabase();
}

return sdb;
}

//打开写连接
public SQLiteDatabase openWriteLink() {
if (sdb == null || !sdb.isOpen()) {
sdb = helper.getWritableDatabase();
}

return sdb;
}

//关闭数据库连接
public void closeLink() {
if (sdb != null && sdb.isOpen()) {
sdb.close();
sdb = null;
}
}

//创建数据库,执行建表语句
@Override
public void onCreate(SQLiteDatabase db) {
//先删除已存在表
String drop_sql = "drop table if exists " + TABLE_NAME + “;”;
db.execSQL(drop_sql);

//创建表
String create_sql = “CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (”

  • “_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,”
  • “name VARCHAR NOT NULL,” + “age INTEGER NOT NULL,”
  • “height INTEGER NOT NULL,” + “weight FLOAT NOT NULL,”
  • “married INTEGER NOT NULL,” + “update_time VARCHAR NOT NULL”
    //演示数据库升级时要先把下面这行注释
  • “,phone VARCHAR” + “,password VARCHAR”
  • “);”;

db.execSQL(create_sql);
}

//修改表结构
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion > 1) {
//Android的ALTER命令不支持一次添加多列,只能分多次添加
String alter_sql = “ALTER TABLE " + TABLE_NAME + " ADD COLUMN phone VARCHAR;”;
db.execSQL(alter_sql);

alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + “password VARCHAR;”;
db.execSQL(alter_sql); // 执行完整的SQL语
}
}

//根据指定条件删除记录
public int delete(String condition) {
return sdb.delete(TABLE_NAME, condition, null);
}

//删除全部记录
public int deleteAll() {
return sdb.delete(TABLE_NAME, “1=1”, null);
}

//根据条件查询记录
public List query(String condition) {
String sql = String.format(“select rowid,_id,name,age,height,weight,married,update_time,” +
“phone,password from %s where %s;”, TABLE_NAME, condition);
//执行查询语句,该语句返回结果集的游标
Cursor cursor = sdb.rawQuery(sql, null);

ArrayList userInfos = new ArrayList<>();

//循环取出游标指向的结果集
while (cursor.moveToNext()) {
UserInfo userInfo = new UserInfo();
userInfo.name = cursor.getString(2);
userInfo.age = cursor.getInt(3);
userInfos.add(userInfo);
}

cursor.close();
return userInfos;
}

//往表里添加一条记录
public long insert(UserInfo userinfo) {
ArrayList userInfos = new ArrayList<>();
userInfos.add(userinfo);
return insert(userInfos);
}

//往表里添加多条记录
public long insert(List userInfos) {

long result = -1;

for (UserInfo userInfo : userInfos) {
//如果名字相同,则更新记录
if (userInfo.name != null && userInfo.name.length() > 0) {
String condition = String.format(“name = ‘%s’”, userInfo.name);
List dbUserInfoList = query(condition);
if (dbUserInfoList != null && dbUserInfoList.size() > 0) {
update(userInfo, condition);
//返回其id
result = dbUserInfoList.get(0).id;
continue;
}
}
//其余情况则说明记录不重复,添加新纪录
ContentValues cv = new ContentValues();
cv.put(“name”, userInfo.name);
cv.put(“age”, userInfo.age);
result = sdb.insert(TABLE_NAME, “”, cv);
if(result == -1){
return result;
}
}

return result;
}

//根据指定条件更新表记录
public int update(UserInfo userInfo, String condition) {

ContentValues cv = new ContentValues();
cv.put(“name”, userInfo.name);
cv.put(“age”, userInfo.age);

return sdb.update(TABLE_NAME, cv, condition, null);
}

}

(4)优化记住密码:

上面通过SharedPreferences存储密码的方式还是存在一定的局限性,该方式只能记住一个用户的登录信息,当下一个用户登录后,上一个用户的信息将会被覆盖。正确的记住密码功能应该是输入手机号自动补充密码,因此,可以考虑使用数据库来进行存储。

主要的改造如下:

  1. 声明一个数据库的helper对象,在Activity的OnResume方法中获取数据库连接,在OnPause方法中关闭数据库连接。

private UserDBHelper helper;

@Override
protected void onResume() {
super.onResume();
//获取数据库帮助器实例 (此处是单例,所以不怕重复获取)
helper = UserDBHelper.getInstance(this, 1);
//恢复页面时则获取连接
helper.openWriteLink();
}

@Override
protected void onPause() {
super.onPause();
//暂停页面时就断开连接
helper.closeLink();
}

  1. 登录成功后,如果用户勾选了记住密码功能,则保存到数据库。也就是在loginSuccess方法中添加如下:

if (isRemember) {
UserInfo info = new UserInfo(); // 创建一个用户信息对象
info.phone = et_phone.getText().toString();
info.password = et_password.getText().toString();
info.update_time = DateUtil.getNowDateTime(“yyyy-MM-dd HH:mm:ss”);
mHelper.insert(info); // 往用户数据库添加登录成功的用户信息
}

  1. 用户进行登录时,根据输入手机号自动查找密码:

// 根据手机号码查询指定记录
public UserInfo queryByPhone(String phone) {
UserInfo info = null;
List infoList = query(String.format(“phone=‘%s’”, phone));
if (infoList.size() > 0) { // 存在该号码的登录信息
info = infoList.get(0);
}
return info;
}

3. 存储卡

(1)私有空间和公有空间

为了更规范地管理手机存储空间,Android从7.0开始将存储卡划分为私有存储和公共存储两大部分,也就是分区存储方式,系统给每个App都分配了默认的私有存储空间。App在私有空间上读写文件无须任何授权,但是若想在公共空间读写文件,则要在AndroidManifest.xml里面添加下述的权限配置。

但是即使App声明了完整的存储卡操作权限,系统仍然默认禁止该App访问公共空间。打开手机的系统设置界面,进入到具体应用的管理页面,会发现该应用的存储访问权限被禁止了。

既然存储卡分为公共空间和私有空间两部分,它们的空间路径获取也就有所不同。若想获取公共空间的存储路径,调用的是Environment.getExternalStoragePublicDirectory方法;若想获取应用私有空间的存储路径,调用的是getExternalFilesDir方法。

//获取系统的公共存储路径
String publicPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString();

//获取系统的私有存储路径
String privatePath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();

boolean isLegacy = true;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
//Android10的存储空间默认采用分区方式,这里是判断是使用传统方式还是分区方式
isLegacy = Environment.isExternalStorageLegacy();
}

(2)在存储卡上读写文件

文本文件的读写借助IO流 FileOutputStream(写文件)和 FileInputStream(读文件)

// 把字符串保存到指定路径的文本文件
public static void saveText(String path, String txt) {
// 根据指定的文件路径构建文件输出流对象
try (FileOutputStream fos = new FileOutputStream(path)) {
fos.write(txt.getBytes()); // 把字符串写入文件输出流
} catch (Exception e) {
e.printStackTrace();
}
}
// 从指定路径的文本文件中读取内容字符串
public static String openText(String path) {
String readStr = “”;
// 根据指定的文件路径构建文件输入流对象
try (FileInputStream fis = new FileInputStream(path)) {
byte[] b = new byte[fis.available()];
fis.read(b); // 从文件输入流读取字节数组
readStr = new String(b); // 把字节数组转换为字符串
} catch (Exception e) {
e.printStackTrace();
}
return readStr; // 返回文本文件中的文本字符串
}

(3)在存储卡上读写 图片文件

文本文件可以转化为对字符串的读写,而图像的读写就需要借助专门的位图工具Bitmap处理。不同图像来源获取Bitmap的方式不同,有三种:

  1. 从指定资源文件中获取:decodeResource,例如从资源文件img.png获取位图对象:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img);

  1. 从指定路径下获取:decodeFile,但是要注意从Android10开始,该方法只能获取私有空间下的图片,公共空间下获取不了。

Bitmap bitmap = BitmapFactory.decodeFile(“C:\Users\OYMN\Pictures\onepunch.jpg”);

  1. 从指定的输入流中获取,比如使用IO流打开图片文件,然后作为参数传入decodeStream:

public static Bitmap openImage(String path) {
Bitmap bitmap = null; // 声明一个位图对象
// 根据指定的文件路径构建文件输入流对象
try (FileInputStream fis = new FileInputStream(path)) {
bitmap = BitmapFactory.decodeStream(fis); // 从文件输入流中解码位图数据
} catch (Exception e) {
e.printStackTrace();
}
return bitmap; // 返回图片文件中的位图数据
}

获取到图片之后就可以通过ImageView的setImageBitmap进行设置了。

有多种读取图片的方式,但是写图片只有一种方式。通过Bitmap的compress方法将位图数据压缩到文件输出流:

public static void saveImage(String path, Bitmap bitmap){
//根据文件路径构建文件输出流
try(FileOutputStream fos = new FileOutputStream()){
//将位图数据压缩到文件输出流
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
}catch(Exception e){
e.printStackTrace();
}
}

以下演示一下完整的文件读写操作:

// 获取当前App的私有下载目录
String path = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() +
“/”;
// 从指定的资源文件中获取位图对象
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.huawei);
String file_path = path + DateUtil.getNowDateTime(“”) + “.jpeg”;
FileUtil.saveImage(file_path, bitmap); // 把位图对象保存为图片文件
tv_path.setText(“图片文件的保存路径为:\n” + file_path);

// 获取当前App的私有下载目录
mPath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + “/”;
// 获得指定目录下面的所有图片文件
mFilelist = FileUtil.getFileList(mPath, new String[]{“.jpeg”});
if (mFilelist.size() > 0) {
// 打开并显示选中的图片文件内容
String file_path = mFilelist.get(0).getAbsolutePath();
tv_content.setText(“找到最新的图片文件,路径为”+file_path);
// 显示存储卡图片文件的第一种方式:直接调用setImageURI方法
//iv_content.setImageURI(Uri.parse(file_path)); // 设置图像视图的路径对象
// 第二种方式:先调用BitmapFactory.decodeFile获得位图,再调用setImageBitmap方法
//Bitmap bitmap = BitmapFactory.decodeFile(file_path);
//iv_content.setImageBitmap(bitmap); // 设置图像视图的位图对象
// 第三种方式:先调用FileUtil.openImage获得位图,再调用setImageBitmap方法
Bitmap bitmap = FileUtil.openImage(file_path);
iv_content.setImageBitmap(bitmap); // 设置图像视图的位图对象

4. 应用组件Application

Application是Android的一大组件,在App运行期间只有一个Application对象贯穿整个应用的生命周期。因此,Application适合保存全局变量,主要是以下三类数据:

  • 会频繁读取的信息:如用户名,手机号码等
  • 不方便通过intent传递的数据,如位图对象,非字符串的集合对象等。
  • 容易因频繁分配内存而导致内存泄漏的对象,如Handler处理器实例等。

image-20230115170457698

通过Application实现对全局内存的读写:

  1. 先继承Application,并获取唯一实例:

public class MyApplication extends Application {

private static MyApplication myApplication; //Application唯一实例

public Map<String, String> map = new HashMap<>(); //当作全局变量,用来存储数据

public static MyApplication getInstance(){
return myApplication;
}

@Override
public void onCreate() {
super.onCreate();

// 在打开应用时对静态的应用实例赋值
myApplication = this;
}
}

  1. 在AndroidManifest.xml 通过name属性添加该Application

image-20230117121213401

  1. 接下来就可以通过该Application在整个App中存取数据了:

如在MainActivity6存储数据:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main6);

//存储数据
MyApplication myApplication = MyApplication.getInstance();
myApplication.map.put(“myKey”, “myValue”);

//跳转到MainActivity5
View bt5 = findViewById(R.id.bt5);
bt5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity6.this, MainActivity5.class);
startActivity(intent);
}
});

}

在MainActivity5中获取数据:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main5);

TextView tv = findViewById(R.id.tv);
tv.setText(MyApplication.getInstance().map.get(“myKey”)); //成功获取到数据
}

5. 实战:购物车

五、内容共享

1. 在应用之间共享数据

接下来将介绍Android的四大组件之一ContentProvider,通过ContentProvider封装内部数据的外部访问接口,实现不同应用能够互相传输数据。

和ContentProvider搭配使用的还有:ContentResolver(内容解析器),ContentObserver(内容观察器)。

上面提到的SQLite可以操作自身的数据库,而ContentProvider则是作为中间接口,通过SQLiteOpenHelper和SQLiteDatabase间接操控数据库,实现为其他应用提供数据的功能。

image-20230119194835570

使用举例如下:

  1. 创建一个UserInfoProvider,用来提供用户信息给外界应用

在弹出的右键菜单中依次选择New→Other→Content Provider

此时会自动修改两处地方:

(1)一是在AndroidManifest.xml中添加该Provider的配置信息:

image-20230118224755856

(2)二是创建的这个Provider会继承ContentProvider,并重写了一些方法。

Server端代码:

public class UserInfoProvider extends ContentProvider {

//这里是上面实现的dbHelper,用来操作本地数据库
private UserDBHelper userDBHelper;

//初始化
@Override
public boolean onCreate() {
//初始化 dbHelper
userDBHelper = UserDBHelper.getInstance(getContext());

return true;
}

//插入
//uri格式:content://com.example.secondandroidapp.UserInfoProvider/user
@Override
public Uri insert(Uri uri, ContentValues values) {
//使用sqlite插入数据
SQLiteDatabase db = userDBHelper.getWritableDatabase();
db.insert(UserDBHelper.TABLE_NAME, null, values);

return uri;
}

//查询
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {

SQLiteDatabase db = userDBHelper.getReadableDatabase();
return db.query(UserDBHelper.TABLE_NAME, projection, selection, selectionArgs, null, null, null);
}

//删除
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)) {
//这种是uri不带参数:“content://com.example.secondandroidapp.UserInfoProvider/user”
case USER:

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

//初始化
@Override
public boolean onCreate() {
//初始化 dbHelper
userDBHelper = UserDBHelper.getInstance(getContext());

return true;
}

//插入
//uri格式:content://com.example.secondandroidapp.UserInfoProvider/user
@Override
public Uri insert(Uri uri, ContentValues values) {
//使用sqlite插入数据
SQLiteDatabase db = userDBHelper.getWritableDatabase();
db.insert(UserDBHelper.TABLE_NAME, null, values);

return uri;
}

//查询
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {

SQLiteDatabase db = userDBHelper.getReadableDatabase();
return db.query(UserDBHelper.TABLE_NAME, projection, selection, selectionArgs, null, null, null);
}

//删除
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
switch (uriMatcher.match(uri)) {
//这种是uri不带参数:“content://com.example.secondandroidapp.UserInfoProvider/user”
case USER:

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-xLlerrpi-1713552895590)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值