转-Android sdk自带“9妹”(9patch 工具)和padding

http://blog.csdn.net/xiaominghimi/article/details/6107837

http://su1216.iteye.com/blog/1536253

 

 李华明Himi 原创,转载务必在明显处注明:
转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/android-game/321.html

 

很多童鞋说我的代码运行后,点击home或者back后会程序异常,如果你也这样遇到过,那么你肯定没有仔细读完Himi的博文,第十九篇Himi专门写了关于这些错误的原因和解决方法,这里我在博客都补充说明下,省的童鞋们总疑惑这一块;请点击下面联系进入阅读:

【Android游戏开发十九】(必看篇)SurfaceView运行机制详解—剖析Back与Home按键及切入后台等异常处理!

前几天群成员讨论过关于9patch的工具【我比较喜欢喊它9妹子,西西(*^_^*)】、然后研究了一下,比较简单但是很实用的一个Android sdk 自带工具、这里给大家做一个分享下经验!

 

1.什么是“9妹”(9patch)?

它是一个对png图片做处理的一个工具,能够为我们生成一个”*.9.png”的图片;

 2.何为”*.9.png”?

所谓”*.9.png”这是Android os里所支持的一种特殊的图片格式,用它可以实现部分拉伸;这种图片是经过”9妹“进行特殊处理过的,如果不处理的话,直接用PNG图就会有失真,拉伸不正常的现象出现。

 3.它的用途是?

       说到用途,这种特殊格式的png图,我也看了网上的相关文章但都是用一个能自适应的button举例子!(如下图)清一色抄袭.. – -、

        Draw 9-Patch(3gqa.com)  (此实例咱们直接无视掉,在后面我会给大家灌输游戏中实例)

 这个例子是指当button上的字体大小改变,那么文字底下的png图也会自动适应文字。

这似乎表明做Android 软件应用 使用一些组件的的时候会时常用到;

       4.那么实际在游戏中到底如何使用呢?什么情况下去使用呢?

….当然啦,身为做游戏我一定要”9妹“利用在咱们游戏中才行,不然岂不是白研究了、经过思考突然想到了一些情况,并且发现“9妹”确实在游戏开发中占有一定的分量!下面我们来先熟习“9妹”工具,然后再跟大家举例,贴图来说明其用途、毕竟有图有真相 呵呵~

 

启动9妹:

在你Android SDK 路径下 X:/android sdk/tools ,你会找到一个 【draw9patch.bat】,没错这就是9妹啦、官方名 NinePatch ;

 

提示导入一张png图片,然后真正进入”9妹”的操作界面(如下图):     (图1)

 

 

 

序列 ① :在拉伸区域周围用红色边框显示可能会对拉伸后的图片产生变形的区域,如果完全消除该内容则图片拉伸后是没有变形的,也就是说,不管如何缩放图片显示都是良 好的。 (实际试 发现NinePatch编辑器是根据图片的颜色值来区分是否为bad patch的,一边来说只要色差不是太大不用考虑这个设置。)

序列 ② :区域是导入的图片,以及可操作区域。

序列 ③ :这里 zoom:的长条bar 是对导入的图放大缩小操作,这里的放大缩小只是为了让使用者更方便操作,毕竟是对像素点操作比较费眼,下面的 patch scale 是序列 ④区域中的三种形态的拉伸后的一个预览操作,可以看到操作后的图片拉伸后的效果。

序列 ④: 区域这里从上到下,依次为:纵向拉伸的效果预览、横向拉伸的效果预览,以及整体拉伸的效果预览

序列 ⑤: 这里如果你勾选上,那么当你鼠标放在 ② 区域内的时候并且当前位置为不可操作区域就会出现lock的一张图,就是显示不可编辑区域 ;

序列 ⑥: 这里勾选上,那么在④ 区域中你就会看到当前操作的像素点在拉伸预览图中的相对位置和效果。

序列 ⑦: 在编辑区域显示图片拉伸的区域;

 

如何操作:

                   鼠标左键选取需要拉伸的像素点;  shift+鼠标左键取消当前像素点。

 

 

操作区域:

                                    

大家看到导入的png图片默认周围多了一像素点,也就是这一圈一像素点就是咱们的可操作区域。因为方和右方可操作区域是指定内容的显示区域,属于可选区域,可不予理会;但是要注意内容区域的标记不能有间断,也就是说标记要连续且仅有一处,否则.9.png图片在放入项目下会报错。

主要大家注意Left 和 top 操作区域;

 

Top操作区域的一排像素点,表示横向拉伸的像素点;

Left操作区的一排像素点,表示纵向拉伸的像素点;

 

 

下图是我对图片的操作:

(图2)

      

 

 

大家看到上方和左边的黑色像素了么?对,这些是我手动操作的地方,我这里是想让此png图像拉伸操作的时候,只是中间区域被拉伸。选择上方中间区域是为了横向拉伸的时候选取的拉伸像素点,左边则是纵向拉伸的;

  

那么大家现在回头看一眼(图1)然后对比(图2),看到区别了吧!很明显,(图1)我们没有任何操作,默认整体拉伸,那么拉伸的效果很明显的失真了…而(图2)我们指定了拉伸的像素点所以只是中间的被拉伸,图片的花边我们保留不拉伸这样看起来就好太多啦 娃哈哈、

然后通过“9妹”就可以保存出来一张“*.9.png”图片,我们放在android 项目的res 下的 drawable 下就可以拉!

 

现在我就可以跟大家讲下使用“*.9.png”的好处:

 

在我们手机游戏开发的过程中,我们最关系的是生成的安装文件、比如j2me 的jar 包,塞班的sis、sisx 以及咱们andrid中的apk都希望打包后的包越小越好、虽然现在的手机趋向于智能了,但是毕竟手机的容量和内存还是有限、身为移动设备开发者的我们对此都很看重,那么通过”9妹”处理后的图片我们就可以省去不少的内存和容量。

 1. 省精力和时间!

如果我们有一张50*50的类似上面那种带花边的png图片,那么我们在android或者大分辨率的机器上使用的画,肯定需要对其处理,那么要不就是让美工的mm们给咱们重新做一张,那么通过”9妹”处理得到的“*.9.png”就会省去美工的负担了。

2.省内存!

如果不想用代码来对其小图进行缩放来再次使用(因为考虑会失真),那么可能会多加了图片,这样一来游戏包的大小就会增加了,几K—几十K不等,而利用”9妹”处理的就省去了这些麻烦。

3.减少代码量! 

  有些童鞋该说啦,我用代码一样能实现(图2)的效果不失真,OK,我也知道。当初我在J2ME平台做RPG的时候也是利用设置可视区域等代码来实现的,但是如果你用“.9.png”的方式就更简单!!!

 

不多吹 “9妹”的好处,下面我们来看看代码如何实现此格式的方式和效果吧!

 

  

  1. package com.himi;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.graphics.Canvas;
  6. import android.graphics.Color;
  7. import android.graphics.NinePatch;
  8. import android.graphics.Paint;
  9. import android.graphics.RectF;
  10. import android.util.Log;
  11. import android.view.SurfaceHolder;
  12. import android.view.SurfaceView;
  13. import android.view.SurfaceHolder.Callback;
  14. public class MySurfaceView extends SurfaceView implements Callback, Runnable {
  15.     private Thread th = new Thread(this);
  16.     private SurfaceHolder sfh;
  17.     private Canvas canvas;
  18.     private Paint paint;
  19.     private Bitmap bmp_old;
  20.     private Bitmap bmp_9path;
  21.     private NinePatch np;
  22.     public MySurfaceView(Context context) {
  23.         super(context);
  24.         this.setKeepScreenOn(true);
  25.         bmp_old = BitmapFactory.decodeResource(getResources(), R.drawable.himi_old);
  26.         bmp_9path = BitmapFactory.decodeResource(getResources(), R.drawable.himi_9path);
  27.         np = new NinePatch(bmp_9path, bmp_9path.getNinePatchChunk(), null);
  28.         //创建一个ninePatch的对象实例,第一个参数是bitmap、第二个参数是byte[],这里其实要求我们传入
  29.         //如何处理拉伸方式,当然我们不需要自己传入,因为“.9.png”图片自身有这些信息数据,
  30.         //也就是我们用“9妹”工具操作的信息! 我们直接用“.9.png”图片自身的数据调用getNinePatchChunk()即可
  31.         //第三个参数是图片源的名称,这个参数为可选参数,直接null~就OK~
  32.         sfh = this.getHolder();
  33.         sfh.addCallback(this);
  34.         paint = new Paint();
  35.         paint.setAntiAlias(true);
  36.         setFocusable(true);
  37.     }
  38.     public void surfaceCreated(SurfaceHolder holder) {
  39.         Log.v(“Himi”“surfaceCreated”);
  40.         th.start();
  41.     }
  42.     /**
  43.      * @author Himi
  44.      */
  45.     public void draw() {
  46.         canvas = sfh.lockCanvas();
  47.         canvas.drawColor(Color.BLACK);
  48.         RectF rectf_old_two = new RectF(050, bmp_old.getWidth() * 2120 + bmp_old.getHeight() * 2);//备注1
  49.         RectF rectf_old_third = new RectF(0120 + bmp_old.getHeight() * 2, bmp_old.getWidth() * 3,
  50.                 140 + bmp_old.getHeight() * 2 + bmp_old.getHeight() * 3);
  51.         // ——–下面是对正常png绘画方法———–
  52.         canvas.drawBitmap(bmp_old, 00, paint);
  53.         canvas.drawBitmap(bmp_old, null, rectf_old_two, paint);
  54.         canvas.drawBitmap(bmp_old, null, rectf_old_third, paint);
  55.         RectF rectf_9path_two = new RectF(25050250 + bmp_9path.getWidth() * 290 + bmp_9path.getHeight() * 2);
  56.         RectF rectf_9path_third = new RectF(250120 + bmp_9path.getHeight() * 2250 + bmp_9path.getWidth() * 3,
  57.                     140 + bmp_9path.getHeight() * 2
  58.                 + bmp_9path.getHeight() * 3);
  59.         canvas.drawBitmap(bmp_9path, 2500, paint);
  60.         // ——–下面是”.9.png”图像的绘画方法———–
  61.         np.draw(canvas, rectf_9path_two);
  62.         np.draw(canvas, rectf_9path_third);
  63.         sfh.unlockCanvasAndPost(canvas);
  64.     }
  65.     public void run() {
  66.         // TODO Auto-generated method stub
  67.         while (true) {
  68.             draw();
  69.             try {
  70.                 Thread.sleep(100);
  71.             } catch (Exception ex) {
  72.             }
  73.         }
  74.     }
  75.     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  76.         Log.v(“Himi”“surfaceChanged”);
  77.     }
  78.     public void surfaceDestroyed(SurfaceHolder holder) {
  79.         Log.v(“Himi”“surfaceDestroyed”);
  80.     }
  81. }

 

下图是模拟器中的效果图、

 

 

                       

 

左边是正常png的缩放不同大小的情况,右边是咱们的9妹处理过的“*.9.png”、娃哈哈,怎么样  效果明显不一样吧!

 

 

好啦,到这里就结篇吧,挺累的 写了三个多小时了……. 希望大家以后多多的利用 “9妹”哦、

 

(推荐大家订阅本博客,因为咱的更新速度可是很快的~娃哈哈)

 

 

 

前几天遇到这样一个问题

我们做的应用客户看了说UI有问题,本来是一个正方形的区域,呈现的结果却是长方形,有些偏差

之后我就开始修改,觉得UI问题肯定是哪里没有设计好,95%是因为xml里面配置错了,这是我第一直觉.

找了一上午,发现xml里面没有问题,定义的确实是正方形区域,也没有任何padding设置.于是下午就在代码中找,看看是不是代码中设置padding

我觉得一般人不会在代码中干这种事情吧,果然,没有找到.汗了!

 

于是开始debug,代码中也没有什么特别的设置,很崩溃.后来发现setBackgroundResource这个api有些诡异

其中调用了void android.view.View.setBackgroundDrawable(Drawable d),setBackgroundDrawable方法中有这么一句

 

Java代码  收藏代码
  1. setPaddingFromDrawable(d, padding);

这是啥……怎么看上去这么诡异,setPaddingFromDrawable有

 

Java代码  收藏代码
  1. draw.getPadding(padding)

debug发现,走过这里,padding就被改变了.然后没有继续深入调查,猜测和android:background的设置有关,去掉这个设置,padding不见了

后来美工证实了我的猜想,padding是写在了9patch图片里面.

 

所以当你在xml和代码中找不到padding的设置,而结果却有padding效果时,那基本应该就是9-patch干的好事了

 

下面就简单学习下9-patch

拿原生Email为例吧,随便找了个.9图片:attachment_bg_holo.9.png(packages/apps/Email/res/drawable-hdpi)

sdk为我们提供了查看9-patch图片的工具,在tools文件夹下

android-sdk/tools$ ./draw9patch

打开上述图片:

 

下面几个滑动条看上去很别扭是因为我把工具页面缩小然后他们被挤到一起了

当你滑动patch scale时,右边的预览也会随着改变大小

下面先说说padding在哪

中间的图片四周带有黑线(点)的就是我们打开的.9图片

黑色部分又分为两部分,其中下和右两条黑线(点)控制padding

勾选show lock后会看到图片区域(就是现在我的截图去掉四周一圈)

那么上下左右padding分别是多少呢?

很简单,一个小格子是一个像素

上下左右padding分别是2,3,2,2(要去掉最外圈的格子,因为他们不是图片的一部分)

那么我说的对不对,如何检验,sdk提供了另一个工具在tools文件夹下hierarchyviewer

android-sdk/tools$ ./draw9patch

看到右边的数值了吧.

下面是官方的说明

 

guide/topics/graphics/2d-graphics.html#nine-patch
You can also define an optional drawable section of the image (effectively, the padding lines) by drawing a line on the right and bottom lines. If a View object sets the NinePatch as its background and then specifies the View’s text, it will stretch itself so that all the text fits inside only the area designated by the right and bottom lines (if included). If the padding lines are not included, Android uses the left and top lines to define this drawable area.

 

 

下面看看9-patch上边和左边的黑线是做什么的

下面是http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch的图片

我想大家也看明白了,另外的两条线是控制拉伸区域的,可以是一条也可以是多条

下面是官方说明

 

guide/topics/graphics/2d-graphics.html#nine-patch 写道
The border is used to define the stretchable and static areas of the image. You indicate a stretchable section by drawing one (or more) 1-pixel-wide black line(s) in the left and top part of the border (the other border pixels should be fully transparent or white). You can have as many stretchable sections as you want: their relative size stays the same, so the largest sections always remain the largest

至此,关于9-patch的讲解结束,如有更多需求和疑问请移步下列地址

http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch

http://developer.android.com/reference/android/graphics/NinePatch.html

http://developer.android.com/guide/developing/tools/draw9patch.html

 

 

转贴请保留以下链接

本人blog地址

http://su1216.iteye.com/

http://blog.csdn.net/su1216/