Skip to content

fix: Android onLoad event when view width and height are zero #953

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions ReactNativeFastImageExample/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

def REACT_NATIVE_VERSION = new File(['node', '--print',"JSON.parse(require('fs').readFileSync(require.resolve('react-native/package.json'), 'utf-8')).version"].execute(null, rootDir).text.trim())

buildscript {
ext {
buildToolsVersion = "30.0.2"
Expand Down Expand Up @@ -34,5 +36,13 @@ allprojects {

google()
maven { url 'https://www.jitpack.io' }

}

configurations.all {
resolutionStrategy {
// Remove this override in 0.65+, as a proper fix is included in react-native itself.
force "com.facebook.react:react-native:" + REACT_NATIVE_VERSION
}
}
}
50 changes: 41 additions & 9 deletions ReactNativeFastImageExample/metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,45 @@
* @format
*/



const path = require('path')
const escape = require('escape-string-regexp')
const exclusionList = require('metro-config/src/defaults/exclusionList')
const pak = require('../package.json')

const root = path.resolve(__dirname, '..')

const modules = Object.keys({
...pak.peerDependencies,
})

module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
}
projectRoot: __dirname,
watchFolders: [root],

// We need to make sure that only one version is loaded for peerDependencies
// So we block them at the root, and alias them to the versions in example's node_modules
resolver: {
blacklistRE: exclusionList(
modules.map(
(m) =>
new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)
)
),

extraNodeModules: modules.reduce((acc, name) => {
acc[name] = path.join(__dirname, 'node_modules', name)
return acc
}, {}),
},

transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
}
1 change: 1 addition & 0 deletions ReactNativeFastImageExample/src/AutoSizeExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const AutoSizingImage = (props: AutoSizingImageProps) => {
<FastImage
{...props}
onLoad={onLoad}
resizeMode={FastImage.resizeMode.contain}
style={[{ width: props.width, height }, props.style]}
/>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.dylanvann.fastimage;

import android.graphics.BitmapFactory;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.resource.SimpleResource;

import java.io.IOException;
import java.io.InputStream;

public class BitmapSizeDecoder implements ResourceDecoder<InputStream, BitmapFactory.Options> {

@Override
public boolean handles(@NonNull InputStream source, @NonNull Options options) throws IOException {
return true;
}

@Nullable
@Override
public Resource<BitmapFactory.Options> decode(@NonNull InputStream source, int width, int height, @NonNull Options options) throws IOException {
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(source, null, bitmapOptions);
return new SimpleResource(bitmapOptions);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.dylanvann.fastimage;

import android.graphics.BitmapFactory;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.resource.SimpleResource;
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;

public class BitmapSizeTranscoder implements ResourceTranscoder<BitmapFactory.Options, Size> {
@Nullable
@Override
public Resource<Size> transcode(@NonNull Resource<BitmapFactory.Options> toTranscode, @NonNull Options options) {
BitmapFactory.Options bitmap = toTranscode.get();
Size size = new Size();
size.width = bitmap.outWidth;
size.height = bitmap.outHeight;
return new SimpleResource(size);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.dylanvann.fastimage;

import android.content.Context;
import android.graphics.BitmapFactory;

import androidx.annotation.NonNull;

import com.bumptech.glide.Glide;
Expand Down Expand Up @@ -47,6 +49,10 @@ public void registerComponents(
.build();
OkHttpUrlLoader.Factory factory = new OkHttpUrlLoader.Factory(client);
registry.replace(GlideUrl.class, InputStream.class, factory);

// Decoder + Transcoder pair for InputStream -> Size
registry.prepend(InputStream.class, BitmapFactory.Options.class, new BitmapSizeDecoder());
registry.register(BitmapFactory.Options.class, Size.class, new BitmapSizeTranscoder());
}

private static Interceptor createInterceptor(final ResponseProgressListener listener) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ public class FastImageRequestListener implements RequestListener<Drawable> {
this.key = key;
}

private static WritableMap mapFromResource(Drawable resource) {
WritableMap resourceData = new WritableNativeMap();
resourceData.putInt("width", resource.getIntrinsicWidth());
resourceData.putInt("height", resource.getIntrinsicHeight());
return resourceData;
}

@Override
public boolean onLoadFailed(@androidx.annotation.Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
FastImageOkHttpProgressGlideModule.forget(key);
Expand All @@ -53,7 +46,6 @@ public boolean onResourceReady(Drawable resource, Object model, Target<Drawable>
ThemedReactContext context = (ThemedReactContext) view.getContext();
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
int viewId = view.getId();
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_EVENT, mapFromResource(resource));
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
import android.content.Context;
import android.graphics.drawable.Drawable;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;

import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
Expand Down Expand Up @@ -148,6 +151,25 @@ public void onAfterUpdate(
builder.listener(new FastImageRequestListener(key));

builder.into(this);

// Used specifically to handle the `onLoad` event for the image
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
int viewId = this.getId();
requestManager
.as(Size.class)
.load(imageSource == null ? null : imageSource.getSourceForLoad())
.into(new SimpleTarget<Size>() {
@Override
public void onResourceReady(@NonNull Size resource, @Nullable Transition<? super Size> transition) {
WritableMap resourceData = new WritableNativeMap();
resourceData.putInt("width", resource.width);
resourceData.putInt("height", resource.height);
eventEmitter.receiveEvent(viewId,
"onFastImageLoad",
resourceData
);
}
});
}
}

Expand Down
6 changes: 6 additions & 0 deletions android/src/main/java/com/dylanvann/fastimage/Size.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.dylanvann.fastimage;

public class Size {
int width;
int height;
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
"main": "dist/index.cjs.js",
"module": "dist/index.js",
"typings": "dist/index.d.ts",
"source": "src/index",
"react-native": "src/index",
"files": [
"android",
"!android/build",
Expand Down Expand Up @@ -79,4 +81,4 @@
"react": "^17 || ^18",
"react-native": ">=0.60.0"
}
}
}