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

Posted HarmonyOS技术社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HarmonyOS 用 Matrix 实现各种图片 ScaleType 缩放相关的知识,希望对你有一定的参考价值。

本文将从零开始实现一个图片组件,并展示如何使用 Matrix 实现图片的各种 ScaleType 缩放效果

创建图片组件

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

public class MyImageComponent extends Component implements Component.DrawTask {   
    // 图片资源,从 src 属性读取
    private Element element;
    // 读取 scaleType 属性
    private ScaleType scaleType = ScaleType.fitCenter;
    // 用于实现 scaleType 的 Matrix 
    private final Matrix matrix = new Matrix();

    // ...其他构造函数略
    public MyImageComponent(Context context, AttrSet attrSet, int resId) {
        super(context, attrSet, resId);
        init(attrSet);
    }
}

然后执行初始化流程

    // 初始化,包括读取属性,根据 scaleType 设置 matrix,添加绘制方法     
    private void init(AttrSet attrSet) {
        readAttrSet(attrSet);      
        dealScaleType();
        addDrawTask(this);
    }

    // 读取 xml 属性,包括 src 和 scaleType
    private void readAttrSet(AttrSet attrSet) {
        element = attrSet.getAttr("src").get().getElement();

        if (attrSet.getAttr("scaleType").isPresent()) {
            String scaleTypeString = attrSet.getAttr("scaleType").get().getStringValue();
            scaleType = Utils.getEnumFromString(ScaleType.class, scaleTypeString, ScaleType.center);
        }
    }

    // 根据 scaleType 属性实现对应的缩放效果
    private void dealScaleType() {
        switch (scaleType) {
            default:
            case fitCenter:
                fitCenter();
                break;
            case center:
                center();
                break;
            case fitXY:
                fitXY();
                break;
            case fitStart:
                fitStart();
                break;
            case fitEnd:
                fitEnd();
                break;
            case centerCrop:
                centerCrop();
                break;
            case centerInside:
                centerInside();
                break;
        }
    }

    private int getDisplayWidth() {
        return getWidth() - getPaddingLeft() - getPaddingRight();
    }

    private int getDisplayHeight() {
        return getHeight() - getPaddingLeft() - getPaddingRight();
    }

    private int getElementWidth() {
        return element.getWidth();
    }

    private int getElementHeight() {
        return element.getHeight();
    }

ScaleType 效果展示和对应源码

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


:::

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

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    xmlns:app="http://schemas.ohos.com/apk/res-auto"
    ohos:
    ohos:
    ohos:alignment="center"
    ohos:orientation="vertical">

    <com.bm.mycomponent.MyImageComponent
        ohos:
        ohos:
        ohos:background_element="#4682B4"
        ohos:margin="6vp"
        app:src="$media:big"
        app:scaleType="center"
        />
    <com.bm.mycomponent.MyImageComponent
        ohos:
        ohos:
        ohos:margin="6vp"
        ohos:background_element="#4682B4"
        app:src="$media:small"
        app:scaleType="center"
        />

</DirectionalLayout>

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

center

::: hljs-center

:::

      /**
       * 图片居中显示在组件中,不对图片进行缩放
       */
      private void center() {
          float wTranslate = (getDisplayWidth() - getElementWidth()) * 0.5f;
          float hTranslate = (getDisplayHeight() - getElementHeight()) * 0.5f;

          matrix.postTranslate(wTranslate, hTranslate);
      }

fitCenter

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

::: hljs-center

:::

      private void fitCenter() {
          float wPercent = (float)getDisplayWidth() / (float)getElementWidth();
          float hPercent = (float)getDisplayHeight() / (float)getElementHeight();
          float minPercent = Math.min(wPercent, hPercent);

          float targetWidth = minPercent * getElementWidth();
          float targetHeight = minPercent * getElementHeight();

          float wTranslate = (getDisplayWidth() - targetWidth) * 0.5f;
          float hTranslate = (getDisplayHeight() - targetHeight) * 0.5f;

          matrix.setScale(minPercent, minPercent);
          matrix.postTranslate(wTranslate, hTranslate);
      }

fitXY

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

::: hljs-center

:::

      private void fitXY(){
          float wPercent = (float)getDisplayWidth() / (float)getElementWidth();
          float hPercent = (float)getDisplayHeight() / (float)getElementHeight();
          matrix.setScale(wPercent, hPercent);
      }

fitStart

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

:::

      private void fitStart() {
          float wPercent = (float)getDisplayWidth() / (float)getElementWidth();
          float hPercent = (float)getDisplayHeight() / (float)getElementHeight();
          float minPercent = Math.min(wPercent, hPercent);

          matrix.setScale(minPercent, minPercent);
      }

fitEnd

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

::: hljs-center

:::

      private void fitEnd() {
          float wPercent = (float)getDisplayWidth() / (float)getElementWidth();
          float hPercent = (float)getDisplayHeight() / (float)getElementHeight();
          float minPercent = Math.min(wPercent, hPercent);

          float targetWidth = minPercent * getElementWidth();
          float targetHeight = minPercent * getElementHeight();
          float wTranslate = getDisplayWidth() - targetWidth;
          float hTranslate = getDisplayHeight() - targetHeight;

          matrix.setScale(minPercent, minPercent);
          matrix.postTranslate(wTranslate, hTranslate);
      }

centerCrop

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

:::

      private void centerCrop() {
          float scale;
          float dx;
          float dy;

          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

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

::: hljs-center

:::

      private void centerInside() {
          if (getElementWidth() <= getDisplayWidth() && getElementHeight() <= getElementHeight()){
              float wTranslate = (getDisplayWidth() - getElementWidth()) * 0.5f;
              float hTranslate = (getDisplayHeight() - getElementHeight()) * 0.5f;            
              matrix.setTranslate(wTranslate, hTranslate);
          } else {
              fitCenter();
          }
      }

绘制图片组件

关键是这句 canvas.concat(matrix)

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

更多原创内容请关注:中软国际 HarmonyOS 技术学院
入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,共建鸿蒙生态,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

想了解更多关于鸿蒙的内容,请访问:

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

https://harmonyos.51cto.com/#bkwz

以上是关于HarmonyOS 用 Matrix 实现各种图片 ScaleType 缩放的主要内容,如果未能解决你的问题,请参考以下文章

用HarmonyOS ArkUI调用三方库PhotoView实现图片的联播缩放

手把手教你用鸿蒙HarmonyOS实现微信聊天界面

HarmonyOS - 基于ArkUI (JS) 实现图片旋转验证

HarmonyOS之AI能力·图像超分辨率

HarmonyOS 基于JSAPI实现刮刮乐效果

HarmonyOS Image 图片的使用 随机选妹子