/** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.litho.widget; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.widget.ImageView.ScaleType; import com.facebook.litho.ComponentContext; import com.facebook.litho.ComponentLayout; import com.facebook.litho.Diff; import com.facebook.litho.DrawableMatrix; import com.facebook.litho.MatrixDrawable; import com.facebook.litho.Output; import com.facebook.litho.R; import com.facebook.litho.Size; import com.facebook.litho.SizeSpec; import com.facebook.litho.annotations.FromBoundsDefined; import com.facebook.litho.annotations.MountSpec; import com.facebook.litho.annotations.OnBind; import com.facebook.litho.annotations.OnBoundsDefined; import com.facebook.litho.annotations.OnCreateMountContent; import com.facebook.litho.annotations.OnLoadStyle; import com.facebook.litho.annotations.OnMeasure; import com.facebook.litho.annotations.OnMount; import com.facebook.litho.annotations.OnUnmount; import com.facebook.litho.annotations.Prop; import com.facebook.litho.annotations.ResType; import com.facebook.litho.annotations.ShouldUpdate; import com.facebook.litho.utils.MeasureUtils; import static com.facebook.litho.SizeSpec.UNSPECIFIED; /** * A component that is able to display drawable resources. It takes a drawable * resource ID as prop. * * @prop drawable Drawable to display. * @prop scaleType Scale type for the drawable within the container. */ @MountSpec(isPureRender = true, poolSize = 30) class ImageSpec { private static final ScaleType[] SCALE_TYPE = ScaleType.values(); @OnLoadStyle static void onLoadStyle( ComponentContext c, Output<Drawable> drawable, Output<ScaleType> scaleType) { final TypedArray a = c.obtainStyledAttributes(R.styleable.Image, 0); for (int i = 0, size = a.getIndexCount(); i < size; i++) { final int attr = a.getIndex(i); if (attr == R.styleable.Image_android_src) { drawable.set(c.getResources().getDrawable(a.getResourceId(attr, 0))); } else if (attr == R.styleable.Image_android_scaleType) { scaleType.set(SCALE_TYPE[a.getInteger(attr, -1)]); } } a.recycle(); } @OnMeasure static void onMeasure( ComponentContext c, ComponentLayout layout, int widthSpec, int heightSpec, Size size, @Prop(resType = ResType.DRAWABLE) Drawable drawable) { if (drawable == null || drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { size.width = 0; size.height = 0; return; } final int intrinsicHeight = drawable.getIntrinsicHeight(); final int intrinsicWidth = drawable.getIntrinsicWidth(); if (SizeSpec.getMode(widthSpec) == UNSPECIFIED && SizeSpec.getMode(heightSpec) == UNSPECIFIED) { size.width = intrinsicWidth; size.height = intrinsicHeight; return; } final float aspectRatio = intrinsicWidth / (float) intrinsicHeight; MeasureUtils.measureWithAspectRatio( widthSpec, heightSpec, intrinsicWidth, intrinsicHeight, aspectRatio, size); } @OnBoundsDefined static void onBoundsDefined( ComponentContext c, ComponentLayout layout, @Prop(resType = ResType.DRAWABLE) Drawable drawable, @Prop(optional = true) ScaleType scaleType, Output<DrawableMatrix> drawableMatrix, Output<Integer> drawableWidth, Output<Integer> drawableHeight) { final int horizontalPadding = layout.getPaddingLeft() + layout.getPaddingRight(); final int verticalPadding = layout.getPaddingTop() + layout.getPaddingBottom(); if (ScaleType.FIT_XY == scaleType || drawable == null || drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { drawableMatrix.set(null); drawableWidth.set(layout.getWidth() - horizontalPadding); drawableHeight.set(layout.getHeight() - verticalPadding); } else { final DrawableMatrix matrix = DrawableMatrix.create( drawable, scaleType, layout.getWidth() - horizontalPadding, layout.getHeight() - verticalPadding); drawableMatrix.set(matrix); drawableWidth.set(drawable.getIntrinsicWidth()); drawableHeight.set(drawable.getIntrinsicHeight()); } } @OnCreateMountContent static MatrixDrawable onCreateMountContent(ComponentContext c) { return new MatrixDrawable(); } @OnMount static void onMount( ComponentContext c, MatrixDrawable matrixDrawable, @Prop(resType = ResType.DRAWABLE) Drawable drawable, @FromBoundsDefined DrawableMatrix drawableMatrix) { matrixDrawable.mount(drawable, drawableMatrix); } @OnBind static void onBind( ComponentContext c, MatrixDrawable mountedDrawable, @FromBoundsDefined Integer drawableWidth, @FromBoundsDefined Integer drawableHeight) { mountedDrawable.bind(drawableWidth, drawableHeight); } @OnUnmount static void onUnmount( ComponentContext c, MatrixDrawable convertDrawable, @Prop(resType = ResType.DRAWABLE) Drawable drawable) { convertDrawable.unmount(); } @ShouldUpdate(onMount = true) static boolean shouldUpdate( Diff<ScaleType> scaleType, Diff<Drawable> drawable) { return (scaleType.getNext() != scaleType.getPrevious()) || drawable.getNext() != drawable.getPrevious(); } }