Loading images into ImageView is one of the most common tasks when developing Android applications. Without the right approach, pictures may slow down the interface, take up extra memory, or not be displayed at all. This article will help you understand how to correctly load images from different sources: from standard resources drawable to dynamic loading from the Internet.
We will consider not only basic methods like setImageResource(), but also advanced techniques: working with libraries Glide and Picasso, processing large files, optimization for RecyclerView and even uploading pictures directly from the deviceโs camera. We will pay special attention to typical errors due to which images are not displayed or distorted.
The article will be useful both for beginners who are just learning to work with Android Studioand for experienced developers looking for optimal solutions for complex scenarios. All code examples have been tested on the latest versions Android 14 i Android Studio Giraffe, but are also suitable for earlier releases.
1. Loading images from application resources (drawable/mipmap)
The easiest way to add an image to ImageView is to use the built-in project resources. This method is ideal for static images that do not change during application operation (logos, icons, background textures).
All graphic files should be stored in folders res/drawable (for vector and raster images) or res/mipmap (for adaptive icons). Android Studio automatically optimizes them for different screen densities (mdpi, hdpi, xhdpi etc.) if you place the files in the appropriate subfolders.
Add an image to the folder res/drawable|Make sure the file name is in lowercase with no spaces|Follow size guidelines (e.g. 48x48dp for icons)|Check format support (PNG, WebP, SVG for vector)
-->
To load an image from resources programmatically, use the method setImageResource():
ImageView imageView = findViewById(R.id.my_image_view);
imageView.setImageResource(R.drawable.my_image);
For vector images (Vector Drawable) the code remains the same, but make sure that in build.gradle support is enabled:
android {defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
โ ๏ธ Attention: If the image is not displayed, check:
- ๐น Is the resource name specified correctly (case matters!)
- ๐น Are there any errors in
XML-markup (for example, typos inapp:srcCompat)- ๐น Does your ImageView vector images support (for API < 21 you need a support library)
To load via XML-markup use the attribute android:src or app:srcCompat (for vector images):
<ImageViewandroid:id="@+id/my_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/my_image" />
2. Dynamic loading from the Internet (using Glide and Picasso)
When images are stored on the server, they need to be loaded directly while the application is running. For this, specialized libraries are used. since a "naked" URL right while the application is running. For this purpose, specialized libraries are used, since "naked" HTTPrequest followed by setImageBitmap() leads to blocking of the main thread and a crash ANR (Application Not Responding).
Two most popular libraries for asynchronous loading:
- ๐ผ๏ธ Glide โrecommended Google, supports caching, animations and working with GIF
- ๐จ Picasso โeasier to set up, but inferior Glide in performance for large images
To connect Glide, add a dependency to build.gradle:
implementation 'com.github.bumptech.glide:glide:4.16.0'
Example of loading an image with placeholder and error handling:
Glide.with(context).load("https://example.com/image.jpg")
.placeholder(R.drawable.placeholder) // Background image
.error(R.drawable.error_image) // Image on error
.override(300, 300) // Forced size
.into(imageView);
For Picasso the dependency and code will be slightly different:
implementation 'com.squareup.picasso:picasso:2.8'
Picasso.get().load("https://example.com/image.jpg")
.placeholder(R.drawable.placeholder)
.error(R.drawable.error_image)
.resize(300, 300)
.centerCrop()
.into(imageView);
โ ๏ธ Attention: Always check your Internet access permissionsAndroidManifest.xml:<uses-permission android:name="android.permission.INTERNET" />Without this permission, downloading by URL will not work!
3. Loading images from the file system (internal/external memory)
Sometimes images need to be loaded from the device's local storage - for example, when the user selects a photo from the gallery or an application saves pictures for offline access. In Android there are two types of memory:
- ๐ Internal memory โavailable only to your application, does not require permission
- ๐ External memory (SD card)โrequires permission
READ_EXTERNAL_STORAGE(for API < 33)
To work with files, use the class File and methods BitmapFactory. Example of loading from internal memory:
File imageFile = new File(context.getFilesDir(), "my_image.jpg");if (imageFile.exists()) {
Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath());
imageView.setImageBitmap(bitmap);
}
For external memory, do not forget to request permission (for Android 13+ use READ_MEDIA_IMAGES):
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
To get the path to an image from the gallery, use Intent:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType("image/*");
startActivityForResult(intent, PICK_IMAGE_REQUEST);
In the method onActivityResult process the result:
@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK) {
Uri imageUri = data.getData();
imageView.setImageURI(imageUri);
}
}
For Android 11+ use MediaStore instead of direct paths to files - this guarantees compatibility with new storage access rules.
4. Loading optimization for RecyclerView and lists
When displaying images in lists (for example, in RecyclerView), it is important to avoid flickering and lags when scrolling. To do this:
- ๐ Use caching ( Glide it is enabled by default)
- ๐ Specify a fixed size
.override(width, height) - ๐ซ Avoid loading high resolution for thumbnails
- ๐ Cancel loading when reusing views (
Glide.with().clear(view))
Example of adapter for RecyclerView s Glide:
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder> {@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Glide.with(holder.itemView.getContext())
.load(imageUrls.get(position))
.placeholder(R.drawable.placeholder)
.centerCrop()
.into(holder.imageView);
}
@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
Glide.with(holder.itemView.getContext()).clear(holder.imageView);
}
}
For additional optimization, use BitmapPool v Glide or adjust the size cache in AndroidManifest.xml:
<applicationandroid:largeHeap="true" // Only if you really need it!
... >
โ ๏ธ Attention: Installing android:largeHeap="true" does not solve problems with memory leaks! This is a temporary solution that can lead to OutOfMemoryError on weak devices.
What is a BitmapPool in Glide?
BitmapPool is a mechanism for reusing objects Bitmap to reduce the load on the garbage collector, instead of creating a new bitmap on each load, Glide takes. free Bitmap from the pool or creates a new one if the pool is empty. This reduces the number of memory allocation operations and improves performance when scrolling lists.
5. Loading images from the device camera
To upload photos directly from the camera you need:
- ๐ท Request resolution
CAMERA(andWRITE_EXTERNAL_STORAGEfor saving on older versions of Android) - ๐ Create
Intentto call the camera - ๐ผ๏ธ Process the result and display the snapshot
Example code for calling cameras:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
Processing the result (the thumbnail is returned to Intent):
@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
imageView.setImageBitmap(imageBitmap);
}
}
To save the full-size image, use FileProvider:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
File photoFile = createImageFile(); // Creates a file in the cache
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.android.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
โ ๏ธ Attention: Starting from Android 10, direct access to files viafile://URI is blocked. Always useFileProviderto transfer file paths between applications.
6. Error handling and common problems.
Even with the correct code, images may not load. Here are the most common reasons and solutions:
| Problem | Possible reason | Solution |
|---|---|---|
| Image is not displayed | Invalid path to resource or URL | Check the case in the file name, error log in Logcat |
| The application crashes when loading | Not enough memory for a large image | Use .override() or BitmapFactory.Options s inSampleSize |
| Image is distorted | Proportions do not match ImageView and picture | Set android:scaleType="centerCrop" or "fitCenter" |
| Long download from the Internet | No caching or slow connection | Set up disk cache in Glide/Picasso, add a loading indicator |
| Resolution not requested for camera/storage | For Android 6.0+ you need to request permission in runtime | Use ActivityCompat.requestPermissions() |
To debug image loading, use Android Profiler in Android Studio:
- ๐ Open
View โ Tool Windows โ Profiler - ๐ Go to the tab Memory
- ๐ฑ๏ธ Launch the application and reproduce the problem
- ๐ Look for leaks
Bitmapor spikes in memory usage
Critical error: If you see "SkImageDecoder::Factory returned null" in the logs, it means Android was unable to decode the image. Most often the problem is a damaged file or an unsupported format (for example, some versions of WebP are not supported on older devices).
7. Advanced techniques: transformations, filters and animations
Modern libraries allow you not only to load images, but also to apply various effects to them directly during display. For example, in Glide you can:
- ๐ญ Apply transformations (
.circleCrop(),.blur()) - ๐๏ธ Add animations when loading (
.transition()) - ๐ Change the color palette (
.colorFilter()) - ๐ผ๏ธ Create thumbnails (
.thumbnail())
Example of using circular cropping and blur:
Glide.with(context).load("https://example.com/image.jpg")
.transform(new CircleCrop(), new BlurTransformation(25))
.into(imageView);
To create custom transformations, inherit from BitmapTransformation:
public class RoundedCornersTransformation extends BitmapTransformation {@Override
protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
// Your transformation code
}
// ... the rest code
}
For animations, use .transition(DrawableTransitionOptions.withCrossFade()) โthis will add a smooth appearance of the image.
Always test transformations on devices with different performance - complex effects can be slow on budget smartphones.
FAQ: Frequently asked questions about loading images into ImageView
How to reduce the size of an image before loading into ImageView?
Use BitmapFactory.Options with the parameter inSampleSize:
BitmapFactory.Options options = new BitmapFactory.Options();options.inSampleSize = 4 // Reduces the size by 4 times
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image, options);
Or in Glide:
.override(300, 300) // Forced size
Why is the image from the Internet not cached?
Check your cache settings in Glide:
.diskCacheStrategy(DiskCacheStrategy.ALL) // Caches both the original and transformed image
For Picasso caching is enabled by default, but can be disabled:
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
How to load an image into an ImageView from Base64?
Decode the string and convert to Bitmap:
byte[] decodedString = Base64.decode(base64String, Base64.DEFAULT);Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
imageView.setImageBitmap(decodedByte);
Or via Glide:
Glide.with(context).load(Base64.decode(base64String, Base64.DEFAULT))
.into(imageView);
How to make an ImageView with rounded corners without additional libraries?
Create custom Drawable s ShapeAppearanceModel (for Material Components):
GradientDrawable drawable = new GradientDrawable();drawable.setShape(GradientDrawable.RECTANGLE);
drawable.setCornerRadius(30f); // Corner radius
imageView.setBackground(drawable);
imageView.setClipToOutline(true);
Or use card_view:cardCornerRadius in XML, wrapping ImageView in CardView.
What to do if the image from the gallery is displayed sideways?
The problem is in the tag Exif, which stores the orientation of the photo. Correct this:
ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.postRotate(90);
break;
// Handle other cases
}
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);