声明:本文为RIAMeeting原创文章,谢绝转载,但您可以链接到本文,请尊重作者的劳动成果,谢谢!

在我们为Flex组件定制外观的时候,经常会使用各种外部嵌入的资源,比如一张位图,一个SWF,或者一个SVG,对于嵌入的方式(Embed元数据标签),如果您已经具备了一定的Flex开发经验,那么您对它应该比较熟悉了。那么,您是否在组件外观实施中考虑到了当组件的尺寸发生改变,组件外观如何去适应新的尺寸呢?这就是通常我们所说的组件的缩放问题,一个好的组件外观实施,应该可以适应各种不同的场景(比如对于按钮,文本标签的长度是不确定的,所以按钮的背景应该可以适应各种情况,如果您很不幸只考虑到了短文本的情况,当文本长度增加,并没有相应的调整背景的尺寸或者只是粗糙的直接将背景的宽度与文本标签宽度绑定,那么您将不得不面对发火的老板和抱怨的客户,因为这个外观在某些场景下表现的实在太糟糕了-_-!见图1)。

图1 糟糕的按钮外观实施

解决这个问题的办法是使用Scale9技术,也就是通常我们所说的九宫格,使用这种机制,我们可以灵巧的制定图片的缩放策略,让图片在缩放的时候可以依据一定的规则(比如保持圆角的尺寸不变,只缩放中间部分),从而达到比较完美的效果。下面来让我们探讨一下Scale9的技术原理和使用技巧。

要求

为了充分利用本文,您需要以下软件和文件:

Flash Builder 4

  • 试用
  • 购买

范例文件:

  • scale9_demo.zip | 0.98 MB

必备知识

为了了解本文所叙述的内容,您需要具备一定的Flex开发经验,如果您是一位Flex外观的实施者,那么本文对您来说将更容易理解。

前言

您首先需要了解Scale9的定义,它是使用网格将图像划分为9个区域,如图2所示,我用数字1-9标出了对应的区域,其中标红色的区域是不需要缩放的,而标蓝色的区域则是需要缩放的区域。

图2 对图片使用Scale9技术进行缩放

当这张图片被放大,依据Scale9的定义,区域1,3,7,9需要保持原有的尺寸(通过对比左侧和右侧的图像可以发现这一点),而其它区域则需要依据一定的规则改变自己的尺寸:

  • 区域2,当图片放大,本区域宽度增加,高度保持不变
  • 区域4,当图片放大,本区域高度增加,宽度保持不变
  • 区域5,当图片放大,本区域宽度和高度都增加
  • 区域6,当图片放大,本区域高度增加,宽度保持不变(规则与区域4一致)
  • 区域8,当图片放大,本区域宽度增加,高度保持不变(规则与区域2一致)

对于区域5,我们在做外观设计的时候要重点注意,这个区域不要放置复杂图形,对于单色或简单渐变色而言,一定程度的放大是可以接受的,但如果是复杂图形,那么放大所产生的失真是非常明显的(当然如果是矢量图形,就不会有这个问题,但矢量图形对于CPU有较大的计算消耗,通常在性能敏感型的Flex应用中我们尽量使用位图皮肤)。

Flash技术对于Scale9的支持

Flash对于Scale9的缩放方式有比较好的支持,在IDE的层面,使用Adobe的专业动画创作工具Flash CS5可以通过可视化的操作方式为一个电影剪辑(MovieClip)制定Scale9所需的网格位置,如图3所示:

图3 使用Flash CS5为电影剪辑设定Scale9

您或许很好奇,在IDE中的这个设置,对应到代码层面,发生了什么事情?下面让我们探究一下背后的技术原理。我们都知道,在ActionScript 3中,所有显示对象的基类都是DisplayObject,那么您是否注意到了,DisplayObject有一个属性是scale9Grid,这个属性就是用来设置显示对象如果需要基于Scale9来缩放,所需的各个区域的参数设置。它的默认值为Null,也就是说,默认情况下显示对象是关闭掉Scale9缩放方式的,当我们为scale9Grid赋值(必须是合法的值),也就为显示对象启用了Scale9。这个属性的类型是矩形Rectangle,实际上它定义的是我们前述9个区域中的中间区域(也就是区域5)的位置(x,y)和尺寸(width,height),因为显示对象的尺寸是确定的,根据中间区域的位置和尺寸,Flash Player就可以确定所有9个区域的位置和尺寸,以便在运行时处理缩放的细节。这里有一个约束,就是不能对启用Scale9的显示对象进行旋转,否则Scale9的定义会被忽略。详细描述请参阅ActionScript 3 API中对于Scale9的描述。

那么应该如何确定scale9Grid所需的各个参数呢(即4个值:矩形的x,y坐标和width,height)?通过一些辅助工具,我们可以很容易做到这一点。比如使用Adobe Photoshop,我们可以借助标尺和参考线规划图片Scale9的各个区域,如图4所示:

图4 使用Adobe Photoshop绘制参考线

然后通过Adobe Photoshop的Info面板,我们可以衡量中间区域的位置和尺寸,如图5所示:

图5 Adobe Photoshop的Info面板

获得这些参数后,我们就可以应用到显示对象的scale9Grid,为显示对象启用9宫格缩放。下面我们来看几段具体的代码:

为矢量显示对象启用scale9Grid

如果显示对象包含的是矢量图形,那么我们就可以直接使用scale9Grid。比如我们通过如下代码绘制一个矩形的图形,并启用Scale9:

  1. /**
  2. * 内部类,用于绘制一个圆角矩形
  3. * @author Neo Guo
  4. *
  5. */
  6. class RectDemo extends Sprite {
  7. public function RectDemo() {
  8. var g:Graphics = this.graphics;
  9. g.lineStyle(1,0x000000);
  10. g.beginFill(0xFF0000,1);
  11. g.drawRoundRect(0,0,50,50,20,20);
  12. g.endFill();
  13. //注意这个设置,基于Scale9设置中间区域的矩形
  14. scale9Grid = new Rectangle(20,20,10,10);
  15. }
  16. }

复制代码

然后创建两个RectDemo显示对象,分别赋予不同的尺寸:

  1. //添加两个矢量图形到stage,注意对比缩放效果
  2. rect1 = new RectDemo();
  3. ...
  4. rect2 = new RectDemo();
  5. rect2.width = rect2.height = 200;
  6. ...

复制代码

然后运行代码,我们会看到图形被正确缩放,如图6所示:

图6 矢量图形缩放

为位图对象使用scale9缩放方式

如果是位图对象,那很不幸,如果您尝试直接设置scale9Grid,虽然程序没有抛出异常,但位图并没有按照您预期的方式进行缩放(API中的说明:如果显示对象包含位图,则对位图数据按照默认方式缩放),也就是说,对于位图,我们是不能使用scale9Grid这个属性的。那么对于位图,我们就不能使用9宫格了吗?当然不是,虽然不能使用scale9Grid,但我们可以结合位图数据绘制和Matrix来实现这个效果。

原理分析:首先我们要获取源位图对象的像素数据(BitmapData),然后创建一个目标位图数据对象,使用BitmapData.draw()这个方法,将源数据拷贝到目标位图数据对象中。但是因为目标位图对象的尺寸与源位图并不相同,所以我们要结合裁剪区域(对应Scale9中的9个区域)和Matrix矩阵来对像素进行必须的位移或缩放。Matrix类表示一个转换矩阵,当我们在位图绘制时,定义好裁剪区域之后,由于源位图数据可能并不处于裁剪区域之内,或者尺寸不匹配,我们需要结合Matrix的a,d,tx,ty对像素进行处理。本文不详细介绍Matrix的使用方式,具体说明请参阅API。而对于裁剪区域(BitmapData.draw()的第5个参数),注意这个区域对应Scale9的9个区域,但位置和尺寸应当以目标位图(也就是缩放后的位图)为准,而不是源位图,如果您对Flash中的遮罩(Mask)有所了解的话,这个区域的定义实际上非常类似于遮罩,如果像素数据处于这个区域内,则被保留,如果不处于这个区域内,将被裁切(即忽略)。

来看一下具体的代码实现,在这里我们实现了一个方法getScaledBitmapData,对传入的源位图数据进行处理(结合Scale9的参数设置),将缩放后的位图数据返回,目标位图获得处理后的位图数据,即可呈现出正确的效果。基于篇幅考虑,贴出的代码中省略了若干细节,请参考下载文件的源码,或在线查看Demo的源码部分。

  1. /**
  2. * 这个方法是实现位图Scale9缩放的关键,使用BitmapData.draw和图像矩阵Matrix来实现功能
  3. * @param source 源位图数据
  4. * @param w 调整后所需的宽度
  5. * @param h 调整后所需的高度
  6. * @param scaleInfo scale9定义矩形
  7. * @return 处理后的位图数据
  8. */
  9. private function getScaledBitmapData(source:BitmapData,w:Number,h:Number,scaleInfo:Rectangle):BitmapData {
  10. var bmpData:BitmapData = new BitmapData(w,h,true,0x000000);
  11. var matrix:Matrix = new Matrix();
  12. //绘制后的图像数据的剪裁区域,它用来定义绘制后的位图数据哪些部分是需要保留的,类似于遮罩,不处于这个区域内部的像素将被忽略
  13. //对于Scale9来说,我们需要定义9个这样的裁剪区域,分别对应文章示意图中的9个区域
  14. var clipRect:Rectangle;
  15. ...
  16. //注意下面的循环,分别对应Scale9的9个区域,每个区域需要设置相应的裁剪区域,并使用Matrix来让图像缩放或移动位置
  17. for(var i:uint=1;i<10;i++) {
  18. switch(i) {
  19. case 1:
  20. clipRect = new Rectangle(0,0,left,top);
  21. break;
  22. case 2:
  23. clipRect = new Rectangle(left,0,w-offsetRight-left,top);
  24. matrix.a = clipRect.width/scaleInfo.width;
  25. matrix.tx = clipRect.x - clipRect.x * matrix.a;
  26. break;
  27. ...
  28. }
  29. bmpData.draw(source,matrix,null,null,clipRect,true);
  30. matrix.identity();
  31. }
  32. return bmpData;
  33. }
普通浏览复制代码保存代码打印代码
  1. /**
  2. * 这个方法是实现位图Scale9缩放的关键,使用BitmapData.draw和图像矩阵Matrix来实现功能
  3. * @param source 源位图数据
  4. * @param w 调整后所需的宽度
  5. * @param h 调整后所需的高度
  6. * @param scaleInfo scale9定义矩形
  7. * @return 处理后的位图数据
  8. */       
  9. private function getScaledBitmapData(source:BitmapData,w:Number,h:Number,scaleInfo:Rectangle):BitmapData {
  10.     var bmpData:BitmapData = new BitmapData(w,h,true,0x000000);
  11.     var matrix:Matrix = new Matrix();
  12.     //绘制后的图像数据的剪裁区域,它用来定义绘制后的位图数据哪些部分是需要保留的,类似于遮罩,不处于这个区域内部的像素将被忽略
  13.     //对于Scale9来说,我们需要定义9个这样的裁剪区域,分别对应文章示意图中的9个区域
  14.     var clipRect:Rectangle;
  15.     ...
  16.     //注意下面的循环,分别对应Scale9的9个区域,每个区域需要设置相应的裁剪区域,并使用Matrix来让图像缩放或移动位置
  17.     for(var i:uint=1;i<10;i++) {
  18.         switch(i) {
  19.             case 1:
  20.                 clipRect = new Rectangle(0,0,left,top);
  21.                 break;
  22.             case 2:
  23.                 clipRect = new Rectangle(left,0,w-offsetRight-left,top);
  24.                 matrix.a = clipRect.width/scaleInfo.width;
  25.                 matrix.tx = clipRect.x - clipRect.x * matrix.a;
  26.                 break;
  27.             ...
  28.         }
  29.         bmpData.draw(source,matrix,null,null,clipRect,true);
  30.         matrix.identity();
  31.     }
  32.     return bmpData;
  33. }

然后我们创建两个位图对象,调用上面的方法获取位图数据,然后呈现到屏幕:

  1. //添加两个位图到stage,注意对比缩放效果
  2. var scaleInfo:Rectangle = new Rectangle(12,14,113,74);
  3. var bmd:BitmapData = (imgLoader.content as Bitmap).bitmapData;
  4. img1 = new Bitmap(getScaledBitmapData(bmd.clone(),50,50,scaleInfo),"auto",true);
  5. img2 = new Bitmap(getScaledBitmapData(bmd.clone(),200,200,scaleInfo),"auto",true);

复制代码

运行程序,可以看到预期的效果,如图7所示:

图7 位图缩放

点击这里运行本节Demo | 查看源码

另外,有开发者已经基于Scale9将Bitmap类重新进行了封装,参见:ScaleBitmap

Flex中对于Scale9的支持

Flex作为一个成熟的框架,对于Scale9也做了一些增强。首先我们要了解一个很常用的元数据标签:Embed。我们会用它嵌入各种资源,最常见的方式就是嵌入位图。来看一个简单的嵌入声明:

  1. [Embed(source="assets/btn.png")]
  2. private var imgClass:Class;
普通浏览复制代码保存代码打印代码
  1. [Embed(source="assets/btn.png")]
  2. private var imgClass:Class;

注意嵌入后的位图将作为一个类定义存在。然后我们就可以在代码中创建它的一个实例:

  1. var img1:DisplayObject = new imgClass();

复制代码

那么这个类定义到底是什么呢?这个取决于Flex编译器的巧妙处理。编译后的SWF中,这个位图资源作为一个独立的类定义存在,它的类名是当前所在类的名称和引用这个自定义类的变量名称的结合,比如在这个例子中,它的类名是EmbedDemo_imgClass,而它的父类,则是BitmapAsset。

我们可以用一行代码来验证这一点:

  1. trace(img1 is BitmapAsset);//true

复制代码

如果您希望详细了解编译器的处理结果,可以在项目的编译参数中添加"-keep-generated-actionscript=true",这样会在您的目标输出目录中增加一个名为generated的目录,里面是编译过程的中间代码(如果您了解Flex的编译器,就会知道,Flex编译器去编译源码的时候,有两个步骤,首先将MXML和ActionScript代码全部编译为ActionScript代码,然后再编译为最终的SWF文件)。通过查看中间代码,我们找到了EmbedDemo_imgClass这个类定义,如下:

  1. package {
  2. import mx.core.BitmapAsset;
  3. [ExcludeClass]
  4. [Embed(source="assets/btn.png",exportSymbol="EmbedDemo_imgClass")]
  5. public class EmbedDemo_imgClass extends mx.core.BitmapAsset {...}

复制代码

通过这样的方式,将位图数据嵌入到EmbedDemo_imgClass类定义中,然后被编译到SWF。

BitmapAsset是Bitmap类的子类,表示您在 Flex 应用程序中嵌入的位图图像。而嵌入后的位图数据,则存放于BitmapAsset.bitmapData属性中(这个属性从Bitmap继承而来),这样我们就可以获取位图数据(如果需要的话),当然大部分情况下我们只是需要将位图显示出来即可。

但是请注意,如果直接这样嵌入,对位图的缩放会走形(因为没有使用Scale9的方式),幸运的是,对于Embed标签,我们可以指定9宫格的各个参数,分别是:scaleGridTop, scaleGridBottom, scaleGridLeft和scaleGridRight,需要注意的是它们的参考坐标点是图像的左上角(0,0),所以scaleGridRight是相对于左侧的边距,而不是右侧,示例代码:

  1. [Embed(source="assets/btn.png",scaleGridTop="14",scaleGridBottom="88",scaleGridLeft="12",scaleGridRight="125")]
  2. private var imgClass:Class;
普通浏览复制代码保存代码打印代码
  1. [Embed(source="assets/btn.png",scaleGridTop="14",scaleGridBottom="88",scaleGridLeft="12",scaleGridRight="125")]
  2. private var imgClass:Class;

注意如果启用了Scale9,那么编译后的位图类将继承SpriteAsset,而不是BitmapAsset,下面是编译的中间代码:

  1. package {
  2. import mx.core.SpriteAsset;
  3. [ExcludeClass]
  4. [Embed(scaleGridTop="14",scaleGridLeft="12",source="assets/btn.png",exportSymbol="EmbedDemo_imgClass",scaleGridBottom="88",scaleGridRight="125")]
  5. public class EmbedDemo_imgClass extends mx.core.SpriteAsset {...}

复制代码

您可能会好奇,因为按照API的说明,SpriteAsset是用来放置矢量图形的,为什么会被用来做位图嵌入呢?注意,这又是一个Flex编译器做了巧妙处理的地方,因为在前面的部分我们也说到,对于位图对象启用scaleGrid是无效的,那这里是怎么做的呢?Flex编译器将嵌入的位图,转换为了矢量图形,虽然听起来难以置信,但却是事实。尝试运行下面的代码:

  1. trace(img1.getChildAt(0) is Shape);//true

复制代码

现在,可以解释发生了什么事情:在编译的时候,Flex编译器发现被嵌入的位图需要启用Scale9,于是放弃了从BitmapAsset继承(也就不再视为位图处理),转而继承了SpriteAsset,并将位图数据转换为矢量图形,添加到显示列表(这也是上面那行代码输出true的原因),既然已经是矢量图形,那么要启用Scale9就很容易了,于是我们在Embed标签中设置的参数,会被设置给SpriteAsset的scale9Grid属性。之后的事情就很明了了,不再阐述。

位图转换为矢量图,这个过程听起来很神奇,但考虑到Adobe在图像处理领域的多年积累,实现这个功能并不困难。在Flash CS5中,我们就可以通过"Trace Bitmap"功能,将位图转换为实例图形,如果您的参数设置合理,那么转换后的矢量图与原位图效果将非常接近。如图8所示:

图8 在Flash中转换位图

需要注意的是,矢量图形对于CPU的计算消耗较大,所以我们需要在代码中做一些优化,SpriteAsset默认情况下,cacheAsBitmap的值为false,那么我们在代码实施的时候,如果发现显示对象并不会频繁改变自己的尺寸,就可以设置cacheAsBitmap为true,这样通过位图缓存的方式,虽然增加了一定的内存消耗,但相对于CPU的负载这个代价一般是划算的。

关于Embed资源的各种方式,请参阅ADC中关于Embed嵌入资源的方式说明

在Flex组件外观实施中使用Scale9

在Flex外观的实施过程中,我们也经常会为组件的皮肤嵌入资源,最常见的案例就是嵌入位图。基于外观和组件逻辑分离原则,我们需要尽量将组件外观相关的定义转移到CSS样式表中,而不是定义在MXML代码内。CSS是Web领域非常盛行的外观定义方式,在Flex中,虽然最终对于CSS的执行方式与传统HTML并不相同,但从代码架构的角度考虑,原则则是相通的。Flex中对于CSS有比较好的支持,特别是到了Flex4之后,对于CSS做了多方面的增强,包括命名空间,选择器等等,在使用规则上,与我们在HTML中编写CSS的规则基本一致,如果您之前是一位WEB前端工程师,那么Flex中的CSS编写对您将非常容易。

而且在CSS中,我们也可以使用Embed标签,来嵌入一个资源,对于位图或矢量图形,我们就可以启用Scale9。下面是一段对Spark中的Button组件定义的CSS样式细则,注意我们为Button的皮肤指定了一张位图,并设置了Scale9的详细参数:

  1. s|Button {
  2. bgImg: Embed(source="../image/btn.png",scaleGridTop="12",scaleGridBottom="46",scaleGridLeft="13",scaleGridRight="238");
  3. skinClass: ClassReference("view.skin.ButtonSkin");
  4. }

复制代码

注意在Spark的外观实施中,更倚重于Skin的实现,在Skin中我们有更大的灵活性(在MX组件中,我们虽然通过CSS可以设置很多样式属性,但一旦我们需要的功能超出了MX组件的设定范围,那么自定义外观将十分困难,可能会涉及到扩展组件,覆写组件方法,需要您对组件的逻辑构造有非常清晰的了解),对于Spark组件,我们可以在自定义的Skin中实现所需的各种外观元素,当然也可以结合CSS的自定义属性,将一些可灵活配置的部分定义在CSS中,比如上面那段CSS定义中的bgImg,就是一个自定义的CSS属性,我们为这个属性指定了一个嵌入的资源,然后通过skinClass引用我们创建的一个自定义皮肤类。

在自定义Skin中,我们可以通过getStyle()方法,获取我们在CSS中定义的样式属性:

  1. ...
  2. override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void {
  3. bgSkin = getStyle("bgImg");
  4. }
  5. ...
  6. <s:BitmapImage source="{bgSkin}"
  7. left="0" right="0" top="0" bottom="0"
  8. alpha.over="0.5" alpha.down="0.8" alpha.disabled="0.1"
  9. />

复制代码

当程序运行,我们就会看到Button应用了我们定义的样式,而且是基于Scale9缩放的,如图9所示:

图9 Button外观

那么我们在CSS中的定义,最终是如何被组件应用呢?通过查看编译中间代码,我们可以发现,Flex编译器会检索CSS中的定义,如果有嵌入资源,则为嵌入资源生成一个新的类(与我们在MXML或AS3类中使用Embed的处理方式一致),比如上面的按钮背景位图,类代码是:

  1. package {
  2. import mx.core.SpriteAsset;
  3. [ExcludeClass]
  4. [Embed(scaleGridTop="12",scaleGridLeft="13",scaleGridBottom="46",original="../image/btn.png",scaleGridRight="238")]
  5. public class Flex4ComponentDemo__embed_css____image_btn_png_758788510 extends mx.core.SpriteAsset {...}

复制代码

那么这个类是在什么地方被应用到Button的呢?在编译的中间阶段,Flex编译器会为当前项目中每一个主应用(即容器是Application)生成一个ActionScript类,当然它的父类就是Application,如下面的代码所示:

  1. public class Flex4ComponentDemo
  2. extends spark.components.Application {...}

复制代码

这个类中包含了一个_Flex4ComponentDemo_StylesInit()方法,这个方法的作用是初始化当前Application和组件所需的样式属性,具体代码执行可以划分为两个部分:

  1. 首先,对于Flex框架而言,有一套预置的样式(位于framework.swc中,你可以解压这个SWC文件,会发现里面有一个预置的样式表:default.css),这样来保证我们没有编写自定义样式表的情况下,Application和组件可以按照默认的外观进行显示,这些预置的样式属性,以及相关的素材嵌入,会被Application的StyleManager进行初始化和管理。在Application的启动阶段,需要先从StyleManager中获取到Application或组件的默认样式和皮肤定义,比如:

    1. style = styleManager.getStyleDeclaration("spark.components.Button");

    复制代码

  2. 如果我们有自定义的样式规则,则会与默认样式进行合并(如果冲突,自定义规则会覆盖默认规则),比如在前面所述的CSS定义中,我们为Button组件制定了自定义皮肤,那么这些更改会在此步骤中完成:
    1. if (style.factory == null){
    2. style.factory = function():void{
    3. this.skinClass = view.skin.ButtonSkin;
    4. this.bgImg = _embed_css____image_btn_png_758788510;
    5. }
    6. }

    复制代码

当然,Flex中的StyleManager远比我们这里看到的要复杂,在这里我们就不再做详细介绍,您可以参阅API获得更多的信息。

laizi来自:http://www.riadev.com/flex-thread-784-1-1.html

在Flex组件外观实施中使用Scale9相关推荐

  1. ActionScript3.0自定义Flex组件问题 重写组件的使用

    最近在做Flex的一些学习,需要对Flex组件重写,当然可以两种选择MXML和ActionScript3.0重写,当然MXML的可视化的操作为重写提供了方便,但是要是更改组件的默认属性和添加一些框架属 ...

  2. 别指望在NativeWindow里添加Flex组件

    别指望在NativeWindow里添加Flex组件 -------------------------------------------------------------------------- ...

  3. Flex组件的项目渲染器(ItemRenderer)使用总结

    系列文章导航 Flex,Fms3相关文章索引 Air版免费视频成人聊天室,免费网络远程视频会议系统((Flex,Fms3联合打造)) Flex中提供了大量用于数据呈现的组件,这其中,以List系列组件 ...

  4. 走近Flex组件系列(三):按扭组件(Button,CheckBox,LinkBar,LinkButton,PopUpButton,RadioButton,ToggleButtonBar)...

    本文主要介绍Flex的Button.ButtonBar.CheckBox.LinkBar.LinkButton.PopUpButton.RadioButton.RadioButtonGroup和Tog ...

  5. 计算机系统集成难点,企业MES实施中存在的难点及建议

    MES是企业生产管理服务的核心信息化系统.实施MES是为了将现代企业生产管理思想.理念引入企业生产管理,对企业生产管理流程进行重组和优化,促进企业生产管理水平的提高.可是作用如此大的MES系统在实施过 ...

  6. 深入浅出Flex组件生命周期Part4 ─ 引擎LayoutManager【转载】

    一. 小结 我们已经知道,Spark组件实际上由两个UIComponent构成,一个是所谓的Skin类,一个是所谓的Component类.Component类负责管理数据和逻辑,Skin类负责管理可视 ...

  7. App自动化测试实施中的技术挑战

    一次编写多处运行的动态化容器技术给研发效率带来了极大的提升,但对于依旧需要多端验证的测试流程来说,在效率层面却面临着极大的挑战.本文围绕动态化容器中的动态布局技术,阐述了如何通过可测性改造来帮助达成提 ...

  8. 19、angular1之pass-word组件、input-select组件 、only-select组件(两种)、on-off组件、layui中的datetime示例、京东购物车、两种作用域绑定、

    19.angular1之pass-word组件.input-select组件 .only-select组件(两种).on-off组件.layui中的datetime示例.京东购物车.两种作用域绑定.两 ...

  9. 详细解析:uni-app|vue组件实现茶杯中茶水浮动loading纯CSS动效

    前言 前面我已经跟大家分享了[科技感Loading动效]和[水珠晃动Loading动效]两篇文章了,如果大家没有读过,建议大家花一点点时间去看一下,有了前两篇文章的基础后,这次,小凡给大家再分享一个茶 ...

最新文章

  1. 计算机网络中网络拓扑,计算机网络基础知识:不同的网络拓扑结构对网络的影响...
  2. 量子计算机对人类长寿,科学家称“极端长寿”在未来几十年可能会达到新的里程碑...
  3. 使用Java注解不正确的方法
  4. ASP.NET - 一般处理程序获取session值
  5. 微星msi B450M+i5-8500+1060成功黑苹果
  6. 2020年30米二级分类北京市土地利用数据
  7. Java编程题:年会抽奖(错排问题)
  8. js实现canvas在线画板
  9. matlab设计译码器,基于Matlab编译码器的仿真与设计
  10. java安装报错循环冗余检查_数据错误 循环冗余检查怎么解决
  11. Android组件化开发实践和案例分享
  12. 机顶盒开发助手Tvbox
  13. scrapy 抓取豆瓣Top250书籍信息
  14. 用python画哆啦a梦的头_用 Python 画个哆啦A梦
  15. CodingTrip - 携程编程大赛 (预赛第二场)
  16. 输入法的半角和全角切换
  17. echarts.js刻度标签实现富文本实现中文+图片的效果解决方法
  18. java中文域名转码
  19. android模拟器限制网速,安卓模拟器设置网速和延迟
  20. 微软面试题4-6 (偏向经典面试题)

热门文章

  1. sql 执行计划 嵌套循环_性能调优–嵌套和合并SQL循环与执行计划
  2. python批量提取哔哩哔哩bilibili视频
  3. Bootstrap框架(二)
  4. property内存管理策略
  5. Android 博客园客户端 (四) 基本功能完成(博客列表和内容、新闻列表和内容、推荐博主)...
  6. F#基础教程 ref类型
  7. C#多线程操作----处理并发的问题,Lock关键字
  8. intelj idea安装和配置
  9. beta阶段——项目复审
  10. 由System.getProperty(user.dir)引发的联想