当前位置: 首页 > 科技观察

HarmonyOS使用Matrix实现各种图像ScaleType缩放

时间:2023-03-15 16:02:10 科技观察

更多信息请访问:https://harmonyos.51cto.com,与华为官方共同打造的鸿蒙技术社区。本文将从零开始实现一个图片组件。并展示了如何使用Matrix实现图片的各种ScaleType缩放效果。背景知识:Matrix内部维护了一个float[9]的数组,组成一个3x3的矩阵。从底层原理来看,所有的变换方法都是改变数组中一个或几个位置的值;Matrix提供了Translate(平移)、Scale(缩放)、Rotate(旋转)、Skew(扭曲)四种变换操作,这四种操作本质上是调用setValues()方法设置矩阵数组来实现变换效果。除了Translate(平移),Scale(缩放)、Rotate(旋转)和Skew(扭曲)都可以围绕一个中心点进行。如果不指定,默认是围绕(0,0)进行相应的变换。Matrix提供的四种操作中的每一种都有三种形式:pre、set和post。原因是矩阵乘法不满足乘法交换律,所以左乘和右乘最后的效果是不一样的。我们可以把矩阵变换看成一个队列,里面包含了若干个变换操作。队列中的每个操作都操作转换目标以完成转换。pre相当于在队列的头部添加一个操作,post相当于在队列的尾部添加一个操作。一个操作,set相当于清空当前队列,重新设置。鸿蒙Image组件没有对外暴露setMatrix接口。如果你的项目中需要使用setMatrix来控制图片显示,可以参考这篇文章实现自己的自绘Image组件。创建一个图片组件,首先创建一个组件类MyImageComponent,因为是从零开始,所以我们继承自Component,包括以下三个属性:publicclassMyImageComponenttextendsComponentimplementsComponent.DrawTask{//图片资源,从src属性读取privateElementelement;//读取scaleType属性privateScaleTypescaleType=ScaleType.fitCenter;//用于实现scaleType的矩阵privatefinalMatrixmatrix=newMatrix();//...其他构造函数略publicMyImageComponent(Contextcontext,AttrSetattrSet,intresId){super(context,attrSet,resId);init(attrSet);}}然后执行初始化过程://初始化,包括读取属性,根据scaleType设置矩阵,添加绘图方法privatevoidinit(AttrSetattrSet){readAttrSet(attrSet);dealScaleType();addDrawTask(this);}//读取xml属性,包括src和scaleTypeprivatevoidreadAttrSet(AttrSetattrSet){element=attrSet.getAttr("src").get().getElement();if(attrSet.getAttr("scaleType").isPresent()){StringscaleTypeString=attrSet.getAttr("scaleType").get().getStringValue();scaleType=Utils.getEnumFromString(ScaleType.class,scaleTypeString,ScaleType.center);}}//根据scaleType属性实现对应的缩放效果privatevoiddealScaleType(){switch(scaleType){default:casefitCenter:fitCenter();break;casecenter:center();break;casefitXY:fitXY();break;casefitStart:fitStart();break;casefitEnd:fitEnd();break;casecenterCrop:centerCrop();break;casecenterInside:centerInside();break;}}privateintgetDisplayWidth(){returngetWidth()-getPaddingLeft()-getPaddingRight();}privateintgetDisplayHeight(){returngetHeight()-getPaddingLeft()-getPaddingRight();}privateintgetElementWidth(){returnelement.getWidth();}privateintgetElementHeight(){returnelement.getHeight();}ScaleType效果展示及对应源码下面一一展示各种ScaleType效果及其实现代码。为了比较不同尺寸图片的ScaleType差异,准备一大一小两张图片预览的xml布局代码如下:下面是各种scaleType的效果和源码=(getDisplayWidth()-getElementWidth())*0.5f;floathTranslate=(getDisplayHeight()-getElementHeight())*0.5f;matrix.postTranslate(wTranslate,hTranslate);}fitCenter保持图片的纵横比,X和Scale在Y方向,直到一个方向覆盖组件,缩放后的图像显示在组件的中心。float)getElementHeight();floatminPercent=Math.min(wPercent,hPercent);floattargetWidth=minPercent*getElementWidth();floattargetHeight=minPercent*getElementHeight();floatwTranslate=(getDisplayWidth()-targetWidth)*0.5f;floathTranslate=(getDisplayHeight()-targetHeight)*0.5f;matrix.setScale(minPercent,minPercent);matrix.postTranslate(wTranslate,hTranslate);}fitXY独立缩放X和Y方向,直到图像被组件覆盖。该方法可能会改变图片原有的纵横比,导致图片被拉伸变形。privatevoidfitXY(){floatwPercent=(float)getDisplayWidth()/(float)getElementWidth();floathPercent=(float)getDisplayHeight()/(float)getElementHeight();matrix.setScale(wPercent,hPercent);}fitStart保持图像Aspectratio,在X和Y方向对图片进行缩放,直到一个方向填满组件,缩放后的图片与组件的左上角对齐显示。privatevoidfitStart(){floatwPercent=(float)getDisplayWidth()/(float)getElementWidth();floatPercent=(float)getDisplayHeight()/(float)getElementHeight();floatminPercent=Math.min(wPercent,hPercent);矩阵.setScale(minPercent,minPercent);}fitEnd保持图片的纵横比,在X和Y方向对图片进行缩放,直到一个方向填满组件,缩放后的图片与组件的右下角对齐为展示。privatevoidfitEnd(){floatwPercent=(float)getDisplayWidth()/(float)getElementWidth();floatPercent=(float)getDisplayHeight()/(float)getElementHeight();floatminPercent=Math.min(wPercent,hPercent);floattargetWidth=minPercent*getElementWidth();floattargetHeight=minPercent*getElementHeight();floatwTranslate=getDisplayWidth()-targetWidth;floathTranslate=getDisplayHeight()-targetHeight;matrix.setScale(minPercent,minPercent);matrix.postTranslate(wTranslate,hTranslate);}centerCrop保持图片的纵横比,在X和Y方向等比例缩放图片,直到每个方向都大于等于组件对应的尺寸,缩放后的图片显示在组件的中心,并裁掉多余的部分。privatevoidcenterCrop(){floatscale;floatdx;floatdy;if(getElementWidth()*getDisplayHeight()>getDisplayWidth()*getElementHeight()){scale=(float)getDisplayHeight()/(float)getElementHeight();dx=(getDisplayWidth()-getElementWidth()*scale)*0.5f;dy=0f;}else{scale=(float)getDisplayWidth()/(float)getElementWidth();dx=0f;dy=(getDisplayHeight()-getElementHeight()*scale)*0.5f;}matrix.setScale(scale,scale);matrix.postTranslate(dx+0.5f,dy+0.5f);}centerInside如果imagewidth<=componentwidth&&imageheight<=componentheight,donot在组件中执行缩放居中显示。其余的由fitCenter处理。privatevoidcenterInside(){if(getElementWidth()<=getDisplayWidth()&&getElementHeight()<=getElementHeight()){floatwTranslate=(getDisplayWidth()-getElementWidth())*0.5f;floathTranslate=(getDisplayHeight()-getElementHeight())*0.5f;matrix.setTranslate(wTranslate,hTranslate);}else{fitCenter();}}绘制图片组件的关键就是这句话canvas.concat(matrix)@OverridepublicvoidonDraw(Componentcomponent,Canvascanvas){//下面是关键代码,Applymatrixtocanvascanvas.concat(matrix);//绘制图片到canvaselement.drawToCanvas(canvas);}项目地址:my-image-component更多信息请访问:鸿蒙与华为共建官方技术社区https://harmonyos.51cto.com