Hello各位小伙伴,第二期来了
这一期记录了Android设备和工程下的资源目录相关知识
资源
设备存储系统
Android采用VFS虚拟文件系统
/data/data是内部存储
/storage/emulated/0是内置SD卡,但是实际上这个链接通过软链接+挂载,有了好多个"快捷方式",你在很多地方都可以查看到
访问不同目录的方法
Environment.isExternalStorageRemovable()//判断是内置SD卡还是外置SD卡
Context类中有一些在内部存储的沙盒目录中操作的方法
//获取/data/data/<包名>/files
getFilesDir();
//读取files文件夹下的某文件
openFileInput("files文件夹下的某文件");
//写入files文件夹下的某文件,不存在则新建,(private是若存在则覆盖,append是若存在则追加)
openFileOutput("想放在files文件夹下的文件",MODE_PRIVATE);
//获取/data/data/<包名>/子目录
getDir("子目录",MODE_PRIVATE);
//获取文件列表
fileList();
//获取/data/data/<包名>/cache
getCacheDir();
还有一些访问SD卡中本APP沙盒目录的方法
//获取/Android/data/<包名>/cache
getExternalCacheDir();
//获取/Android/data/<包名>/files/alarms
getExternalFilesDir(Environment.DIRECTORY_ALARMS);
Android 10 应用无法访问SD卡的非沙盒目录部分,需要在Manifest的application中添加"使用已过时的外部存储"
android:requestLegacyExternalStorage="true"
Android 11 已经完全无法使用上述方法,应用只能读写内存和SD卡中的沙盒目录
像素
1080*2208分辨率代表手机屏幕横向1080个像素块,纵向2208个像素块
我的手机拍照成片像素会被算法压缩到1200W(3000*4000)
所以此时手机屏幕显示图片就出现像素块不够用的情况
有两种可能的方式:
方式 | 效果 |
---|---|
1:1显示 | 1个照片像素块对应1个屏幕像素块,那么屏幕上只能显示照片的不到1/4 |
智能压缩 | 照片的多个像素块取均值合成为1个像素块,显示在屏幕上(抗锯齿) |
所以Bitmap要进行手动压缩,不手动压缩的话,照片像素块数很可能远多于ImageView区域像素块数,导致Bitmap体积过大;
手动压缩可以使每个像素块物尽其用
数字单位
单位 | |
---|---|
px | 像素 |
in | 英寸 |
pt | 磅;1pt=1/72英寸 |
dp | 抽象单位;在分辨率160的设备上,1dp=1px |
分辨率 | 以1英寸为对角线长度的方块里面,能容纳多少个像素块 |
Assets
-
无法自动响应配置限定的资源,在打包后不会以二进制的形式存在
-
只能通过AssetManager获取
-
在R.java里没有对应ID
//context对象的getAssets()方法可以获取AssetManager
AssetManager mAssets = getAssets();
//获得所有在assets/my_folder文件夹下的文件列表
String[] names = mAssets.list("my_folder");
//打开asset文件
InputStream in = mAssets.open("my_folder/filename");
//取得AssetsFileDescriptor
AssetsFileDescriptor afd = mAssets.openFD("my_folder/filename");
//取得FileDescriptor
FileDescriptor fd = afd.getFileDescriptor();
配置限定符
不同限定文件夹如果冲突,按照优先级表
屏幕分辨率
ldpi:提供给低分辨率设备的资源(120dpi以下)
mdpi:提供给中等分辨率设备的资源(120dpi~160dpi)
hdpi:提供给高分辨率设备的资源(160dpi~240dpi)
xhdpi:提供给超高分辨率设备的资源(240dpi~320dpi)
xxhdpi:提供给超超高分辨率设备的资源(320dpi~480dpi)
xxxhdpi:提供给超超超高分辨率设备的资源(480dpi~)
屏幕大小
small
normal
large
xlarge
layout-w600dp:提供给当前宽度超过600dp设备的资源
layout-sw600dp:提供给最小宽度超过600dp设备的资源
屏幕方向
layout-land:横屏
layout-port:默认竖屏
语言
zh:中文
en:英文
例子:程序会根据当前设备的屏幕大小,自动选择采用单页还是双页布局
新建layout-large文件夹,放入双页布局的xml文件
在原layout文件夹,放入单页布局的xml文件
如果不使用默认的限定符,也可以自定义最小宽度限定符
把layout-large文件夹重命名为layout-sw600dp文件夹,就意味着屏幕宽度超过600dp时,自动采用双页布局
更多查阅应用资源概览 | Android 开发者 | Android Developers
引用res资源的语法
在代码中通过R.string.string_name
可以获得该字符串的引用
在XML中通过@string/string_name
可以获得该字符串的引用
string可以替换为drawable,mipmap,layout...
在XML中通过@+id/id_name
可以定义id
res中是没有id的,所以在其他XML中定义
raw
-
该文件夹没有目录结构,不可创建子文件夹
-
原封不动的存储到设备上,不会转换为二进制
strings.xml
%1$s是占位符
<string name="format">这是第%1$s个字符串</string>
使用时
getString(R.string.format,"1")
refs.xml
给单双页布局指定同一个别名,使用时用别名,系统会自动选择合适的
新建res/values/refs.xml
<!--从此之后activity_list是activity_xxx的别名-->
<item name="activity_list" type="layout">@layout/activity_xxx</item>
themes.xml
外部通过?attr/属性名
获得<item name="属性名">xxx</item>指向的值
Menu
在res
中新建menu
文件夹
在
menu
文件夹中新建Menu resource file
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<group android:checkableBehavior="single"><!--单选选项组-->
<item
android:id="@+id/add_item"
android:title="添加" <!--长按图标会显示-->
android:icon="xxx"
app:showAsAction="always" /><!--永远把这个选项以ImageButton的形式放在栏里,尽量使用ifRoom-->
<item
android:id="@+id/remove_item"
android:title="删除"
android:icon="xxx"
app:showAsAction="ifRoom" /><!--如果有空间,就把这个选项以ImageButton的形式放在栏里-->
</group>
</menu>
回到
Activity
中重写onCreateOptionsMenu()
方法,快捷键ctrl+o
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.getMenuInflater().inflate(R.menu.main, menu);//inflate膨胀
return true;
}
现在有了菜单,需要给菜单里的每一个选项定义响应事件
继续重写
onOptionsItemSelected()
方法
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.add_item:
Toast.makeText(this,"你点击了添加按钮~", Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(this,"你点击了删除按钮~",Toast.LENGTH_SHORT).show();
invalidateOptionsMenu();//需要再次执行onCreateOptionsMenu时使用
break;
}
return true;
}
注意:如果在Fragment中加载菜单,重写的方法是
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.xxx, menu);
}
并且需要在onCreate内加
setHasOptionsMenu(true);