关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 安卓开发 »Android Resources 资源文件的使用详解

    Android Resources 资源文件的使用详解

    2015-03-13 00:00:00 出处:投稿
    分享

    该文文章是对Android中原始资源文件的使用进行了详细的分析介绍,需要的朋友参考下。

    背景知识介绍

    与其他平台的应用程序一样,Android中的应用程序也会使用各种资源,比如图片,字串等,会把它们放入源码的相应文件夹下面,如/res/drawable, /res/xml, /res/values/, /res/raw, /res/layout和/assets。Android也支持并鼓励开发者把UI相关的布局和元素,用XML资源来实现。总结起来,Android中支持的资源有:

    颜色值 /res/values 以resources为Root的XML文件,定义形式为<color name>value</color> 字串 /res/values 以resources为Root的XML文件<string name>value</string> 图片 /res/drawable 直接放入,支持9 Patch可自由拉伸 图片的颜色 /res/values 以resources为Root的XML文件,定义形式为<drawable name>value</drawable> 单位资源 /res/values 以resources为Root的XML文件<dimen name>value</dimen> 菜单 /res/menu 以menuo为root的XML文件 布局 /res/layout 这个就是GUI的布局和元素 风格和主题 /res/values 以resources为Root的XML文件<style name>value</style> 动画 /res/anim 有二种:一个是帧动画(frame animation),也就是连续变换图片以animation-list为Root的XML文件;另外一种就是补间动画(tweened animation),它对应于API中的Animation和AnimationSet,有translate、scale、rotate、alpha四种,以set为root来定义,这个set就相当于AnimationSet

    再说下目录:

    /res/anim 用于存放动画 /res/drawable 存放图片,或等同于图片的资源如shape,或selector /res/menu 存放Menu /res/values 存放修饰性资源:字串,颜色,单位,风格和主题 /res/layout 存放UI布局和元素 /res/raw 存放运行时想使用的原始文件 /assets 存放运行时想使用的原始文件

    除了原始文件目录/res/raw和/assets以外,其他的资源在编译的时候都会被第三方软件aapt进行处理,一个是把图片和XML文件进行处理,例如把XML编译成为二进制形式;另外处理的目的就是生成R.java文件,这个文件是访问资源时必须要用到的。

    /res目录下面的所有文件都会映射到R.java文件中,以整数Id的形式被标识,相同类型的资源被一个内部类来封装,一个R.java的文件类似于这样:

    /* AUTO-GENERATED FILE.  DO NOT MODIFY.
     *
     * This class was automatically generated by the
     * aapt tool from the resource data it found.  It
     * should not be modified by hand.
     */
    package com.android.explorer;
    public final class R {
        public static final class attr {
        }
        public static final class drawable {
            public static final int icon=0x7f020000;
        }
        public static final class id {
            public static final int action=0x7f060004;
            public static final int description_panel=0x7f060001;
            public static final int fileinfo=0x7f060003;
            public static final int filename=0x7f060002;
            public static final int linearlayout_test_1=0x7f060005;
            public static final int linearlayout_test_2=0x7f060006;
            public static final int linearlayout_test_3=0x7f060007;
            public static final int thumbnail=0x7f060000;
        }
        public static final class layout {
            public static final int fileant_list_item=0x7f030000;
            public static final int linearlayout_test=0x7f030001;
        }
        public static final class raw {
            public static final int androidmanifest=0x7f040000;
        }
        public static final class string {
            public static final int app_name=0x7f050001;
            public static final int hello=0x7f050000;
        }
    }

    从这个R.java就可看出在/res中定义或提供资源时的注意事项:

    1. 同一个类型,或同一文件夹下面的资源不可以使用相同的文件名,也就是说不能用文件扩展名来区别不同的文件,因为R.java中只保留资源的文件名而不管扩展名,所以假如有二个图片一个是icon.png另一个是icon.jpg,那么在R.java中只会有一个R.drawable.icon。另外一个则会无法访问到。

    2. 资源文件的名字必须符合Java变量的命名规则,且不能有大写,只能是’[a-z][0-9]._’,否则会有编译错误,因为R.java中的变量Id要与资源中的文件一一对应,也就是说用资源文件名来作为Id的变量名,所以一定要符合Java变量的命名规则,另外它还不能有大写。

    3. 除了SDK支持的folder外,不能再有子Folder,虽不会有编译错误,但是子Folder会被完全忽略,如在/res/layout下在建一个子Folder activity(/res/layout/acitivity/, 那么你在生成的R.java中是看不到activity和其内的内容的。

    4. 对于资源文件的大小有限制,最好不要让单个文件大于1M,这是SDK文档说明的限制,但具体的我没有进行试验(据说2.2版本以后的可支持到10M,不知道是真的还是假的)

    5. 所有/res下面的资源都能通过Resources()并提供Id来访问。

    使用原始资源

    对于大多数资源在编译时会对文件内容进行特殊处理,以方便Apk在运行时访问。 假如想要运行时使用未经处理的原始资源,可以把资源文件放在/res/raw和/assets目录下面,这二个目录的主要区别在于:

    1. /res/raw中的文件会被映射到R.java中

    虽然/res/raw中的文件不会被aapt处理成为二进制,但是它的文件还是被映射到R.java中,以方便以资源Id形式来访问

    2. 子目录结构

    如上面所述,/res/raw中虽可以有子目录,但是在程序运行时是无法访问到的,因为/res下面的所有非法子目录在R.java中都是看不到的。而且这些子目录和文件都不会被编译进入Apk中,解压Apk文件后也看不到/res/raw下面去找了。
    而/assets是允许有子目录的,并且完全可以访问到,并且会被打包进Apk,解压Apk后,这些文件仍然存在并且与源码包中的一样。

    3. 访问方式

    /res/raw下面的文件(子文件夹是访问不到的了)的访问方式是通过Resources,并且必须提供资源的Id

    InputStream in = Context.getResources().openRawResource(R.id.filename);

    所以为什么子文件夹无法访问,因为没有Id啊。

    而/assets则要通过AssetManager来访问。下面着重讲解如何访问/assets下面的资源文件。

    通过AssetManager来访问/assets下面的原始资源文件

    1. 文件的读取方式

    用AssetManager.open(String filename)来打开一个文件,这是一组重载方法,还有其他参数可以设置打开模式等,可以参考文档
    这里的filename是相对于/assets的路径,比如:

    InputStream in = mAssetManager.open("hello.txt"); // '/assets/hello.txt'
    InputStream in2 = mAssetManager.open("config/ui.txt"); // '/assets/config/ui.txt'

    2. 文件夹处理 — 如何遍历/assets

    可以看到假如想要访问/assets下面的文件,必须要知道文件名和相对于/assets的路径。所以,假如你不预先知道其下面有什么的时候又该如何处理呢?那就需要列出它下面所有的文件,然后再选取我们需要的,所以新的问题来了,如何列出/assets下面所有的文件呢?
    AssetManager提供了一个列出/assets下某个路径下面的方法:

    public finalString[]list(String path)
    Since: API Level 1
    Return a String array of all the assets at the given path.
    Parameters
    path  A relative path within the assets, i.e., "docs/home.html". 
    Returns
     String[] Array of strings, one for each asset. These file names are relative to 'path'. You can open the file by concatenating 'path' and a name in the returned string (via File) and passing that to open().

    其实这个文档写的有问题,list()是列出一个文件夹下面的文件,所以应该传入一个文件夹路径而非文档中的”docs/home.html”。

    还有一个最大的问题就是如何列出根目录/assets下面的内容,因为只有知道了根目录下面的东西,才能去相对的子目录去找东西,所以这是个必须最先解决的问题。

    其实文档没有说的太明白这个方法到底如何使用,也就是说这个String参数到底如何传。猜想着根目录为/assets,所以尝试了以下:

    mAssetManager.list(".");  // returns array size is 0
    mAssetManager.list("/");  // returns [AndroidManifest.xml, META-INF, assets, classes.dex, res, resources.arsc] // don't worry, u can see these files though, no way to access them
    mAssetManager.list("/assets");  // returns array size is 0
    //Google了一下,找到了正解:
    mAssetManager.list("");  // returns stuff in /assets

    然后再根据所列出的子项去递归遍历子文件,直到找到所有的文件为止。

    常见的问题

    1. 资源文件只能以InputStream方式来获取

    假如想操作文件怎么办,假如想要用文件Uri怎么办。光靠API当然不行,它只给你InputStream,也就是说它是只读的。可行的办法就是读取文件然后写入一个临时文件中,再对临时文件进行想要的文件操作。可以在内部存储或外部存储上面用Context提供的接口来创建文件。

    File File.createTempFile(String prefix, String suffix);  
    File File.createTempFile(String prefix, String suffix, File path);

    这也是可以的,但要考虑Android系统的特性,也就是说所写的路径是否有权限。比如对于第一个方法,用的是”java.io.tmpdir”这个在Android当中就是”/sdcard”,所以当没有SD卡时这个方法必抛异常。

    2. 所有资源文件都是只读的,运行时无法更改

    因为,程序运行时是把Apk动态解析加载到内存中,也就是说,Apk是不会有变化的,它是无法被改变的。

    3. 所有的资源文件夹/res和/assets也都是只读的,不可写入

    如上面所说,Apk是在编译后是无法再改变的了。

    实例

    下面是一个实例,可以递归式的遍历/assets下面所有的文件夹和文件

    package com.android.explorer;
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import android.app.ListActivity;
    import android.content.Context;
    import android.content.Intent;
    import android.content.res.AssetManager;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.text.TextUtils;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.webkit.MimeTypeMap;
    import android.widget.BaseAdapter;
    import android.widget.ImageButton;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    /*
     * Explore all stuff in /assets and perform actions specified by users.
     */
    public class FileAntActivity extends ListActivity {
        private static final String TAG = "FileAntActivity";
        private AssetManager mAssetManager;
        private static final String EXTRA_CURRENT_DIRECTORY = "current_directory";
        private static final String EXTRA_PARENT = "parent_directory";
        public static final String FILEANT_VIEW = "com.android.fileant.VIEW";
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Intent intent = getIntent();
            String current = null;
            String parent = null;
            if (intent != null && intent.hasExtra(EXTRA_CURRENT_DIRECTORY)) {
                current = intent.getStringExtra(EXTRA_CURRENT_DIRECTORY);
            }
            if (current == null) {
                current = "";
            }
            if (intent != null && intent.hasExtra(EXTRA_PARENT)) {
                parent = intent.getStringExtra(EXTRA_PARENT);
            }
            if (parent == null) {
                parent = "";
            }
            mAssetManager = getAssets();
            if (TextUtils.isEmpty(parent)) {
                setTitle("/assets");
            } else {
                setTitle(parent);
            }
            try {
                // List all the stuff in /assets
                if (!TextUtils.isEmpty(parent)) {
                    current = parent + File.separator + current;
                }
                Log.e(TAG, "current: '" + current + "'");
                String[] stuff = mAssetManager.list(current);
                setListAdapter(new FileAntAdapter(this, stuff, current));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private class FileAntAdapter extends BaseAdapter {
            private Context mContext;
            private String[] mEntries;
            private String mParentDirectory;
    
            public FileAntAdapter(Context context, String[] data, String parent) {
                mContext = context;
                this.mEntries = data;
                mParentDirectory = parent;
            }
    
            public int getCount() {
                return mEntries.length;
            }
            public Object getItem(int position) {
                return mEntries[position];
            }
            public long getItemId(int position) {
                return (long) position;
            }
            public View getView(final int position, View item, ViewGroup parent) {
                LayoutInflater factory = LayoutInflater.from(mContext);
                if (item == null) {
                    item = factory.inflate(R.layout.fileant_list_item, null);
                    TextView filename = (TextView) item.findViewById(R.id.filename);
                    TextView fileinfo = (TextView) item.findViewById(R.id.fileinfo);
                    ImageButton action = (ImageButton) item.findViewById(R.id.action);
                    final String entry = mEntries[position];
                    filename.setText(entry);
                    boolean isDir = isDirectory(entry);
                    if (isDir) {
                        fileinfo.setText("Click to view folder");
                        action.setVisibility(View.GONE);
                        item.setClickable(true);
                        item.setOnClickListener(new View.OnClickListener() {
                            public void onClick(View view) {
                                Intent intent = new Intent(FILEANT_VIEW);
                                intent.putExtra(EXTRA_CURRENT_DIRECTORY, entry);
                                intent.putExtra(EXTRA_PARENT, mParentDirectory);
                                startActivity(intent);
                            }
                        });
                    } else {
                        final String type = 
                            MimeTypeMap.getSingleton().getMimeTypeFromExtension(getExtension(entry));
                        fileinfo.setText(type);
                        item.setClickable(false);
                        action.setOnClickListener(new View.OnClickListener() {
                            public void onClick(View view) {
                                 String filepath = entry;
                                 if (!TextUtils.isEmpty(mParentDirectory)) {
                                     filepath = mParentDirectory + File.separator + filepath;
                                 }
                                 BufferedInputStream in = new BufferedInputStream(mManager.open(filepath));
                                 // Do whatever you like with this input stream
                            }
                        });
                    }
                }
                return item;
            }
         }
    
        /**
         * Test Whether an entry is a file or directory based on the rule:
         * File: has extension *.*, or starts with ".", which is a hidden files in Unix/Linux,
         * otherwise, it is a directory
         * @param filename
         * @return
         */
        private boolean isDirectory(String filename) {
            return !(filename.startsWith(".") || (filename.lastIndexOf(".") != -1));
        }
    
        private String getExtension(String filename) {
            int index = filename.lastIndexOf(".");
            if (index == -1) {
                return "";
            }
            return filename.substring(index + 1, filename.length()).toLowerCase();
        }
    }
    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    商业智能成CIO优先关注点 技术落地方显成效(1)

    用linux安装MySQL时产生问题破解

    JAVA中关于Map的九大问题

    windows 7旗舰版无法使用远程登录如何开启telnet服务

    Android View 事件分发机制详解

    MySQL用户变量的用法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。