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的文件类似于这样:

  1. /*AUTO-GENERATEDFILE.DONOTMODIFY.
  2. *
  3. *Thisclasswasautomaticallygeneratedbythe
  4. *aapttoolfromtheresourcedataitfound.It
  5. *shouldnotbemodifiedbyhand.
  6. */
  7. packagecom.android.explorer;
  8. publicfinalclassR{
  9. publicstaticfinalclassattr{
  10. }
  11. publicstaticfinalclassdrawable{
  12. publicstaticfinalinticon=0x7f020000;
  13. }
  14. publicstaticfinalclassid{
  15. publicstaticfinalintaction=0x7f060004;
  16. publicstaticfinalintdescription_panel=0x7f060001;
  17. publicstaticfinalintfileinfo=0x7f060003;
  18. publicstaticfinalintfilename=0x7f060002;
  19. publicstaticfinalintlinearlayout_test_1=0x7f060005;
  20. publicstaticfinalintlinearlayout_test_2=0x7f060006;
  21. publicstaticfinalintlinearlayout_test_3=0x7f060007;
  22. publicstaticfinalintthumbnail=0x7f060000;
  23. }
  24. publicstaticfinalclasslayout{
  25. publicstaticfinalintfileant_list_item=0x7f030000;
  26. publicstaticfinalintlinearlayout_test=0x7f030001;
  27. }
  28. publicstaticfinalclassraw{
  29. publicstaticfinalintandroidmanifest=0x7f040000;
  30. }
  31. publicstaticfinalclassstring{
  32. publicstaticfinalintapp_name=0x7f050001;
  33. publicstaticfinalinthello=0x7f050000;
  34. }
  35. }
从这个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
  1. InputStreamin=Context.getResources().openRawResource(R.id.filename);
所以为什么子文件夹无法访问,因为没有Id啊。
而/assets则要通过AssetManager来访问。下面着重讲解如何访问/assets下面的资源文件。

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

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

  1. InputStreamin=mAssetManager.open("hello.txt");//'/assets/hello.txt'
  2. InputStreamin2=mAssetManager.open("config/ui.txt");//'/assets/config/ui.txt'
2. 文件夹处理 --- 如何遍历/assets
可以看到如果想要访问/assets下面的文件,必须要知道文件名和相对于/assets的路径。所以,如果你不预先知道其下面有什么的时候又该如何处理呢?那就需要列出它下面所有的文件,然后再选取我们需要的,所以新的问题来了,如何列出/assets下面所有的文件呢?
AssetManager提供了一个列出/assets下某个路径下面的方法:

public finalString[]list(Stringpath)
Since:API Level 1

Return a String array of all the assets at the given path.

Parameters
pathA 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,所以尝试了以下:

  1. mAssetManager.list(".");//returnsarraysizeis0
  2. mAssetManager.list("/");//returns[AndroidManifest.xml,META-INF,assets,classes.dex,res,resources.arsc]//don'tworry,ucanseethesefilesthough,nowaytoaccessthem
  3. mAssetManager.list("/assets");//returnsarraysizeis0
  4. //Google了一下,找到了正解:
  5. mAssetManager.list("");//returnsstuffin/assets
然后再根据所列出的子项去递归遍历子文件,直到找到所有的文件为止。

常见的问题

1. 资源文件只能以InputStream方式来获取
如果想操作文件怎么办,如果想要用文件Uri怎么办。光靠API当然不行,它只给你InputStream,也就是说它是只读的。可行的办法就是读取文件然后写入一个临时文件中,再对临时文件进行想要的文件操作。可以在内部存储或外部存储上面用Context提供的接口来创建文件,详细的请参考这里。Java牛人可能想要用Java本身的能力:

  1. FileFile.createTempFile(Stringprefix,Stringsuffix);
  2. FileFile.createTempFile(Stringprefix,Stringsuffix,Filepath);
这也是可以的,但要考虑Android系统的特性,也就是说所写的路径是否有权限。比如对于第一个方法,用的是"java.io.tmpdir"这个在Android当中就是"/sdcard",所以当没有SD卡时这个方法必抛异常。
2. 所有资源文件都是只读的,运行时无法更改
因为,程序运行时是把Apk动态解析加载到内存中,也就是说,Apk是不会有变化的,它是无法被改变的(至于逆向工程来修改那是另外一回事,请参考这里)。
3. 所有的资源文件夹/res和/assets也都是只读的,不可写入

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

实例

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

  1. packagecom.android.explorer;
  2. importjava.io.BufferedInputStream;
  3. importjava.io.BufferedOutputStream;
  4. importjava.io.File;
  5. importjava.io.FileNotFoundException;
  6. importjava.io.FileOutputStream;
  7. importjava.io.IOException;
  8. importjava.io.InputStream;
  9. importandroid.app.ListActivity;
  10. importandroid.content.Context;
  11. importandroid.content.Intent;
  12. importandroid.content.res.AssetManager;
  13. importandroid.net.Uri;
  14. importandroid.os.Bundle;
  15. importandroid.os.Environment;
  16. importandroid.text.TextUtils;
  17. importandroid.util.Log;
  18. importandroid.view.LayoutInflater;
  19. importandroid.view.View;
  20. importandroid.view.ViewGroup;
  21. importandroid.webkit.MimeTypeMap;
  22. importandroid.widget.BaseAdapter;
  23. importandroid.widget.ImageButton;
  24. importandroid.widget.LinearLayout;
  25. importandroid.widget.TextView;
  26. /*
  27. *Exploreallstuffin/assetsandperformactionsspecifiedbyusers.
  28. */
  29. publicclassFileAntActivityextendsListActivity{
  30. privatestaticfinalStringTAG="FileAntActivity";
  31. privateAssetManagermAssetManager;
  32. privatestaticfinalStringEXTRA_CURRENT_DIRECTORY="current_directory";
  33. privatestaticfinalStringEXTRA_PARENT="parent_directory";
  34. publicstaticfinalStringFILEANT_VIEW="com.android.fileant.VIEW";
  35. @Override
  36. publicvoidonCreate(BundlesavedInstanceState){
  37. super.onCreate(savedInstanceState);
  38. Intentintent=getIntent();
  39. Stringcurrent=null;
  40. Stringparent=null;
  41. if(intent!=null&&intent.hasExtra(EXTRA_CURRENT_DIRECTORY)){
  42. current=intent.getStringExtra(EXTRA_CURRENT_DIRECTORY);
  43. }
  44. if(current==null){
  45. current="";
  46. }
  47. if(intent!=null&&intent.hasExtra(EXTRA_PARENT)){
  48. parent=intent.getStringExtra(EXTRA_PARENT);
  49. }
  50. if(parent==null){
  51. parent="";
  52. }
  53. mAssetManager=getAssets();
  54. if(TextUtils.isEmpty(parent)){
  55. setTitle("/assets");
  56. }else{
  57. setTitle(parent);
  58. }
  59. try{
  60. //Listallthestuffin/assets
  61. if(!TextUtils.isEmpty(parent)){
  62. current=parent+File.separator+current;
  63. }
  64. Log.e(TAG,"current:'"+current+"'");
  65. String[]stuff=mAssetManager.list(current);
  66. setListAdapter(newFileAntAdapter(this,stuff,current));
  67. }catch(IOExceptione){
  68. e.printStackTrace();
  69. }
  70. }
  71. privateclassFileAntAdapterextendsBaseAdapter{
  72. privateContextmContext;
  73. privateString[]mEntries;
  74. privateStringmParentDirectory;
  75. publicFileAntAdapter(Contextcontext,String[]data,Stringparent){
  76. mContext=context;
  77. this.mEntries=data;
  78. mParentDirectory=parent;
  79. }
  80. publicintgetCount(){
  81. returnmEntries.length;
  82. }
  83. publicObjectgetItem(intposition){
  84. returnmEntries[position];
  85. }
  86. publiclonggetItemId(intposition){
  87. return(long)position;
  88. }
  89. publicViewgetView(finalintposition,Viewitem,ViewGroupparent){
  90. LayoutInflaterfactory=LayoutInflater.from(mContext);
  91. if(item==null){
  92. item=factory.inflate(R.layout.fileant_list_item,null);
  93. TextViewfilename=(TextView)item.findViewById(R.id.filename);
  94. TextViewfileinfo=(TextView)item.findViewById(R.id.fileinfo);
  95. ImageButtonaction=(ImageButton)item.findViewById(R.id.action);
  96. finalStringentry=mEntries[position];
  97. filename.setText(entry);
  98. booleanisDir=isDirectory(entry);
  99. if(isDir){
  100. fileinfo.setText("Clicktoviewfolder");
  101. action.setVisibility(View.GONE);
  102. item.setClickable(true);
  103. item.setOnClickListener(newView.OnClickListener(){
  104. publicvoidonClick(Viewview){
  105. Intentintent=newIntent(FILEANT_VIEW);
  106. intent.putExtra(EXTRA_CURRENT_DIRECTORY,entry);
  107. intent.putExtra(EXTRA_PARENT,mParentDirectory);
  108. startActivity(intent);
  109. }
  110. });
  111. }else{
  112. finalStringtype=
  113. MimeTypeMap.getSingleton().getMimeTypeFromExtension(getExtension(entry));
  114. fileinfo.setText(type);
  115. item.setClickable(false);
  116. action.setOnClickListener(newView.OnClickListener(){
  117. publicvoidonClick(Viewview){
  118. Stringfilepath=entry;
  119. if(!TextUtils.isEmpty(mParentDirectory)){
  120. filepath=mParentDirectory+File.separator+filepath;
  121. }
  122. BufferedInputStreamin=newBufferedInputStream(mManager.open(filepath));
  123. //Dowhateveryoulikewiththisinputstream
  124. }
  125. });
  126. }
  127. }
  128. returnitem;
  129. }
  130. }
  131. /**
  132. *TestWhetheranentryisafileordirectorybasedontherule:
  133. *File:hasextension*.*,orstartswith".",whichisahiddenfilesinUnix/Linux,
  134. *otherwise,itisadirectory
  135. *@paramfilename
  136. *@return
  137. */
  138. privatebooleanisDirectory(Stringfilename){
  139. return!(filename.startsWith(".")||(filename.lastIndexOf(".")!=-1));
  140. }
  141. privateStringgetExtension(Stringfilename){
  142. intindex=filename.lastIndexOf(".");
  143. if(index==-1){
  144. return"";
  145. }
  146. returnfilename.substring(index+1,filename.length()).toLowerCase();
  147. }
  148. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值