HarmonyOS用Matrix实现各种图片ScaleType缩放

开发 前端 OpenHarmony
本文将从零开始实现一个图片组件,并展示如何使用 Matrix 实现图片的各种 ScaleType 缩放效果,鸿蒙 Image 组件没有对外公开 setMatrix 接口,可参考本文,自己实现自绘式 Image 组件。

[[418482]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

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三种形式。原因是矩阵乘法不满足乘法交换律,因此左乘还是右乘最终的效果都不一样。我们可以把Matrix变换想象成一个队列,队列里面包含了若干个变换操作,队列中每个操作按照先后顺序操作变换目标完成变换,pre相当于向队首增加一个操作,post相当于向队尾增加一个操作,set相当于清空当前队列重新设置。

鸿蒙 Image 组件没有对外公开 setMatrix 接口,如果项目中需要通过 setMatrix 来控制图片显示,可参考本文,自己实现自绘式 Image 组件。

创建图片组件

先创建一个组件类 MyImageComponent,因为是从零开始,所以我们从 Component 继承,包含以下三个属性:

  1. public class MyImageComponent extends Component implements Component.DrawTask {    
  2.     // 图片资源,从 src 属性读取 
  3.     private Element element; 
  4.     // 读取 scaleType 属性 
  5.     private ScaleType scaleType = ScaleType.fitCenter; 
  6.     // 用于实现 scaleType 的 Matrix  
  7.     private final Matrix matrix = new Matrix(); 
  8.      
  9.     // ...其他构造函数略 
  10.     public MyImageComponent(Context context, AttrSet attrSet, int resId) { 
  11.         super(context, attrSet, resId); 
  12.         init(attrSet); 
  13.     } 

然后执行初始化流程:

  1. // 初始化,包括读取属性,根据 scaleType 设置 matrix,添加绘制方法      
  2.    private void init(AttrSet attrSet) { 
  3.        readAttrSet(attrSet);       
  4.        dealScaleType(); 
  5.        addDrawTask(this); 
  6.    } 
  7.  
  8.    // 读取 xml 属性,包括 src 和 scaleType 
  9.    private void readAttrSet(AttrSet attrSet) { 
  10.        element = attrSet.getAttr("src").get().getElement(); 
  11.         
  12.        if (attrSet.getAttr("scaleType").isPresent()) { 
  13.            String scaleTypeString = attrSet.getAttr("scaleType").get().getStringValue(); 
  14.            scaleType = Utils.getEnumFromString(ScaleType.class, scaleTypeString, ScaleType.center); 
  15.        } 
  16.    } 
  17.  
  18.    // 根据 scaleType 属性实现对应的缩放效果 
  19.    private void dealScaleType() { 
  20.        switch (scaleType) { 
  21.            default
  22.            case fitCenter: 
  23.                fitCenter(); 
  24.                break; 
  25.            case center: 
  26.                center(); 
  27.                break; 
  28.            case fitXY: 
  29.                fitXY(); 
  30.                break; 
  31.            case fitStart: 
  32.                fitStart(); 
  33.                break; 
  34.            case fitEnd: 
  35.                fitEnd(); 
  36.                break; 
  37.            case centerCrop: 
  38.                centerCrop(); 
  39.                break; 
  40.            case centerInside: 
  41.                centerInside(); 
  42.                break; 
  43.        } 
  44.    } 
  45.  
  46.    private int getDisplayWidth() { 
  47.        return getWidth() - getPaddingLeft() - getPaddingRight(); 
  48.    } 
  49.   
  50.    private int getDisplayHeight() { 
  51.        return getHeight() - getPaddingLeft() - getPaddingRight(); 
  52.    } 
  53.  
  54. private int getElementWidth() { 
  55.        return element.getWidth(); 
  56.    } 
  57.  
  58.    private int getElementHeight() { 
  59.        return element.getHeight(); 
  60.    } 

ScaleType 效果展示和对应源码

以下逐个展示各种 ScaleType 效果及其实现代码,为方便对比不同大小的图片的 ScaleType 差异,准备了一大一小两张图片。

【中软国际】HarmonyOS 用 Matrix 实现各种图片 ScaleType 缩放-鸿蒙HarmonyOS技术社区
【中软国际】HarmonyOS 用 Matrix 实现各种图片 ScaleType 缩放-鸿蒙HarmonyOS技术社区

用于预览的 xml 布局代码如下:

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <DirectionalLayout 
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  4.     xmlns:app="http://schemas.ohos.com/apk/res-auto" 
  5.     ohos:height="match_parent" 
  6.     ohos:width="match_parent" 
  7.     ohos:alignment="center" 
  8.     ohos:orientation="vertical"
  9.  
  10.     <com.bm.mycomponent.MyImageComponent 
  11.         ohos:width="200vp" 
  12.         ohos:height="200vp" 
  13.         ohos:background_element="#4682B4" 
  14.         ohos:margin="6vp" 
  15.         app:src="$media:big" 
  16.         app:scaleType="center" 
  17.         /> 
  18.     <com.bm.mycomponent.MyImageComponent 
  19.         ohos:width="200vp" 
  20.         ohos:height="200vp" 
  21.         ohos:margin="6vp" 
  22.         ohos:background_element="#4682B4" 
  23.         app:src="$media:small" 
  24.         app:scaleType="center" 
  25.         /> 
  26.  
  27. </DirectionalLayout> 

以下是各种 scaleType 的效果和源码:

center

【中软国际】HarmonyOS 用 Matrix 实现各种图片 ScaleType 缩放-鸿蒙HarmonyOS技术社区
  1. /** 
  2.     * 图片居中显示在组件中,不对图片进行缩放 
  3.     */ 
  4.    private void center() { 
  5.        float wTranslate = (getDisplayWidth() - getElementWidth()) * 0.5f; 
  6.        float hTranslate = (getDisplayHeight() - getElementHeight()) * 0.5f; 
  7.  
  8.        matrix.postTranslate(wTranslate, hTranslate); 
  9.    } 

fitCenter

保持图片的宽高比,对图片进行X和Y方向缩放,直到一个方向铺满组件,缩放后的图片居中显示在组件中。

【中软国际】HarmonyOS 用 Matrix 实现各种图片 ScaleType 缩放-鸿蒙HarmonyOS技术社区
  1. private void fitCenter() { 
  2.         float wPercent = (float)getDisplayWidth() / (float)getElementWidth(); 
  3.         float hPercent = (float)getDisplayHeight() / (float)getElementHeight(); 
  4.         float minPercent = Math.min(wPercent, hPercent); 
  5.  
  6.         float targetWidth = minPercent * getElementWidth(); 
  7.         float targetHeight = minPercent * getElementHeight(); 
  8.  
  9.         float wTranslate = (getDisplayWidth() - targetWidth) * 0.5f; 
  10.         float hTranslate = (getDisplayHeight() - targetHeight) * 0.5f; 
  11.  
  12.         matrix.setScale(minPercent, minPercent); 
  13.         matrix.postTranslate(wTranslate, hTranslate); 
  14.     } 

fitXY

对X和Y方向独立缩放,直到图片铺满组件。这种方式可能会改变图片原本的宽高比,导致图片拉伸变形。

【中软国际】HarmonyOS 用 Matrix 实现各种图片 ScaleType 缩放-鸿蒙HarmonyOS技术社区
  1. private void fitXY(){ 
  2.        float wPercent = (float)getDisplayWidth() / (float)getElementWidth(); 
  3.        float hPercent = (float)getDisplayHeight() / (float)getElementHeight(); 
  4.        matrix.setScale(wPercent, hPercent); 
  5.    } 

fitStart

保持图片的宽高比,对图片进行X和Y方向缩放,直到一个方向铺满组件,缩放后的图片与组件左上角对齐进行显示。

【中软国际】HarmonyOS 用 Matrix 实现各种图片 ScaleType 缩放-鸿蒙HarmonyOS技术社区
  1. private void fitStart() { 
  2.       float wPercent = (float)getDisplayWidth() / (float)getElementWidth(); 
  3.       float hPercent = (float)getDisplayHeight() / (float)getElementHeight(); 
  4.       float minPercent = Math.min(wPercent, hPercent); 
  5.        
  6.       matrix.setScale(minPercent, minPercent); 
  7.   } 

fitEnd

保持图片的宽高比,对图片进行X和Y方向缩放,直到一个方向铺满组件,缩放后的图片与组件右下角对齐进行显示。

【中软国际】HarmonyOS 用 Matrix 实现各种图片 ScaleType 缩放-鸿蒙HarmonyOS技术社区
  1. private void fitEnd() { 
  2.      float wPercent = (float)getDisplayWidth() / (float)getElementWidth(); 
  3.      float hPercent = (float)getDisplayHeight() / (float)getElementHeight(); 
  4.      float minPercent = Math.min(wPercent, hPercent); 
  5.  
  6.      float targetWidth = minPercent * getElementWidth(); 
  7.      float targetHeight = minPercent * getElementHeight(); 
  8.      float wTranslate = getDisplayWidth() - targetWidth; 
  9.      float hTranslate = getDisplayHeight() - targetHeight; 
  10.  
  11.      matrix.setScale(minPercent, minPercent); 
  12.      matrix.postTranslate(wTranslate, hTranslate); 
  13.  } 

centerCrop

保持图片的宽高比,等比例对图片进行X和Y方向缩放,直到每个方向都大于等于组件对应的尺寸,缩放后的图片居中显示在组件中,超出部分做裁剪处理。

【中软国际】HarmonyOS 用 Matrix 实现各种图片 ScaleType 缩放-鸿蒙HarmonyOS技术社区
  1. private void centerCrop() { 
  2.       float scale; 
  3.       float dx; 
  4.       float dy; 
  5.  
  6.       if (getElementWidth() * getDisplayHeight() > getDisplayWidth() * getElementHeight()) { 
  7.           scale = (float)getDisplayHeight() / (float)getElementHeight(); 
  8.           dx = (getDisplayWidth() - getElementWidth() * scale) * 0.5f; 
  9.           dy = 0f; 
  10.       } else { 
  11.           scale = (float)getDisplayWidth() / (float)getElementWidth(); 
  12.           dx = 0f; 
  13.           dy = (getDisplayHeight() - getElementHeight() * scale) * 0.5f; 
  14.       } 
  15.  
  16.       matrix.setScale(scale, scale); 
  17.       matrix.postTranslate(dx + 0.5f, dy + 0.5f); 
  18.   } 

centerInside

如果图片宽度<= 组件宽度&&图片高度<= 组件高度,不执行缩放,居中显示在组件中。其余情况按 fitCenter 处理。

【中软国际】HarmonyOS 用 Matrix 实现各种图片 ScaleType 缩放-鸿蒙HarmonyOS技术社区
  1. private void centerInside() { 
  2.        if (getElementWidth() <= getDisplayWidth() && getElementHeight() <= getElementHeight()){ 
  3.            float wTranslate = (getDisplayWidth() - getElementWidth()) * 0.5f; 
  4.            float hTranslate = (getDisplayHeight() - getElementHeight()) * 0.5f;             
  5.            matrix.setTranslate(wTranslate, hTranslate); 
  6.        } else { 
  7.            fitCenter(); 
  8.        } 
  9.    } 

绘制图片组件

关键是这句 canvas.concat(matrix)

  1. @Override 
  2.     public void onDraw(Component component, Canvas canvas) { 
  3.         // 以下是关键代码,将 matrix 应用到 canvas 
  4.         canvas.concat(matrix);         
  5.         // 将图片绘制到 canvas 
  6.         element.drawToCanvas(canvas); 
  7.     } 

项目地址:my-image-component

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-08-05 19:27:22

通用API鸿蒙

2021-08-25 09:38:16

鸿蒙HarmonyOS应用

2012-05-23 15:45:13

JavaScript

2023-05-11 07:38:51

2011-05-12 11:28:20

按比例缩放

2011-09-01 10:20:18

VMworld虚拟化惠普

2013-05-27 10:01:06

Android开发Android应用缩放图片

2013-05-27 09:52:35

Android开发移动开发移动应用

2022-07-28 14:31:04

canvas鸿蒙

2023-09-08 09:12:57

内存缓存图像

2021-08-26 15:28:05

鸿蒙HarmonyOS应用

2022-06-09 14:08:34

多设备协同鸿蒙

2022-07-12 17:33:00

消息定时提醒鸿蒙

2023-10-19 10:12:34

图形编辑器开发缩放图形

2021-11-23 10:00:55

鸿蒙HarmonyOS应用

2022-07-28 14:12:31

动画API鸿蒙

2022-06-29 14:06:54

canvas鸿蒙

2022-07-01 17:14:03

消息通知鸿蒙

2009-05-08 09:17:48

动态数据库图片

2023-10-27 08:53:13

Python验证码图片识别
点赞
收藏

51CTO技术栈公众号