add_action('wp_footer', function () { echo ''; }, 99);
add_action('wp_footer', function () { echo ''; }, 99);
The post Android Navigation Drawer Tutorial (Modern Implementation Guide) appeared first on javatechig.com.
]]>In modern Android development, navigation drawers are implemented using DrawerLayout along with Material Design components, ensuring a consistent and user-friendly experience across devices.
This guide explains when to use a navigation drawer, design best practices, and how to implement it using updated Android APIs.
A navigation drawer is ideal when:
Always follow Material Design navigation guidelines when choosing a drawer.
A standard navigation drawer implementation includes:
Make sure you’re using AndroidX and Material Components:
implementation "androidx.drawerlayout:drawerlayout:1.2.0"
implementation "com.google.android.material:material:1.11.0"
public class MainActivity extends AppCompatActivity {
private DrawerLayout drawerLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
drawerLayout = findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this,
drawerLayout,
toolbar,
R.string.drawer_open,
R.string.drawer_close
);
drawerLayout.addDrawerListener(toggle);
toggle.syncState();
}
}
NavigationView navigationView = findViewById(R.id.navigation_view);
navigationView.setNavigationItemSelectedListener(item -> {
switch (item.getItemId()) {
case R.id.nav_home:
loadFragment(new HomeFragment());
break;
case R.id.nav_profile:
loadFragment(new ProfileFragment());
break;
}
drawerLayout.closeDrawers();
return true;
});
private void loadFragment(Fragment fragment) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
}
The navigation drawer remains a powerful navigation pattern when used correctly. By following Material Design principles and using modern Android APIs, you can build scalable, user-friendly navigation for complex applications.
This updated approach ensures your app remains maintainable, modern, and aligned with current Android development standards.
The post Android Navigation Drawer Tutorial (Modern Implementation Guide) appeared first on javatechig.com.
]]>The post Android Frame Animation Example – Drawable Animation Guide appeared first on javatechig.com.
]]>This updated guide on javatechig.com explains how to create frame animations using XML and Kotlin/Java, with best practices for performance and lifecycle-safe control.
Frame animation (also called drawable animation) displays a sequence of drawable images in rapid succession.
Key characteristics:
For more advanced motion effects, prefer property animations (ObjectAnimator, MotionLayout).
Android frame animation uses an AnimationDrawable object. You define an animation list in XML with your frames, durations, and sequence order. When played back, Android renders the frames one after another.
Create an XML file in res/drawable (e.g., frame_anim.xml):
Attribute explanation:
android:oneshot="false" — animation loops continuouslyandroid:duration — milliseconds per frameIn your layout XML:
This binds your animation list to an ImageView.
val imageView = findViewById(R.id.frameImageView)
val frameAnimation = imageView.drawable as AnimationDrawable
frameAnimation.start() // start animation
frameAnimation.stop() // stop animation
ImageView imageView = findViewById(R.id.frameImageView);
AnimationDrawable frameAnimation = (AnimationDrawable) imageView.getDrawable();
frameAnimation.start(); // start animation
frameAnimation.stop(); // stop animation
Too many frames increase memory consumption and slow devices.
Match frame sizes to display size to avoid scaling overhead.
Use BitmapFactory.Options when loading large frames.
Start and stop animations based on lifecycle to conserve resources:
override fun onStart() {
super.onStart()
frameAnimation.start()
}
override fun onStop() {
frameAnimation.stop()
super.onStop()
}
@Override
protected void onStart() {
super.onStart();
frameAnimation.start();
}
@Override
protected void onStop() {
frameAnimation.stop();
super.onStop();
}
| Feature | Frame Animation | Property Animation |
|---|---|---|
| Based on | Drawable frames | Object property changes |
| Use case | Sprite sequences | Smooth transitions |
| Flexibility | Limited | High |
| Performance | Lower for many frames | Optimized |
Use frame animation for simple sprite effects; prefer property animations for complex UI motion.
Cause: android:oneshot="true"
Fix: Set false for looping or re-start manually.
Cause: Many large frames
Fix: Reduce frame count, scale bitmaps
Cause: Lifecycle interruptions
Fix: Control start/stop in lifecycle methods
The post Android Frame Animation Example – Drawable Animation Guide appeared first on javatechig.com.
]]>The post Android TextToSpeech Example with Kotlin & Java appeared first on javatechig.com.
]]>This updated guide on javatechig.com covers TextToSpeech implementation using modern APIs, proper language handling, and lifecycle management for robust integration.
Android’s TextToSpeech API provides a system service that turns text into spoken words by using language engines available on the device.
Key benefits:
This API works with both Kotlin and Java projects.
TextToSpeech is part of the Android framework; no extra Gradle dependency is required.
Ensure minimum SDK is set appropriately:
minSdkVersion 21+
Higher SDK levels improve language support and engine stability.
lateinit var tts: TextToSpeech
tts = TextToSpeech(this) { status ->
if (status == TextToSpeech.SUCCESS) {
tts.language = Locale.US
}
}
TextToSpeech tts = new TextToSpeech(this, status -> {
if (status == TextToSpeech.SUCCESS) {
tts.setLanguage(Locale.US);
}
});
In both cases, onInit callback confirms when the engine is ready.
fun speak(text: String) {
tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, "tts1")
}
tts.speak("Hello from TTS", TextToSpeech.QUEUE_FLUSH, null, "tts1");
Use unique utterance IDs to track speech callbacks if needed.
Always verify that the selected language is supported:
val result = tts.setLanguage(Locale.UK)
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.e("TTS", "Language not supported")
}
Missing data can be resolved by installing voice packs via device settings.
TextToSpeech consumes system resources. Always release it when no longer needed.
override fun onDestroy() {
super.onDestroy()
tts.stop()
tts.shutdown()
}
@Override
protected void onDestroy() {
super.onDestroy();
tts.stop();
tts.shutdown();
}
Proper shutdown prevents memory leaks and runtime issues.
Track speech events:
tts.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
override fun onStart(utteranceId: String) {}
override fun onDone(utteranceId: String) {}
override fun onError(utteranceId: String) {}
})
Useful for sequencing spoken text or UI actions.
Cause: TTS engine not initialized
Fix: Confirm SUCCESS in init callback
Cause: Missing voice data
Fix: Prompt user to install language data
Cause: QUEUE_ADD misuse
Fix: Use QUEUE_FLUSH for immediate speech
onDestroy() to free resourcesUtteranceProgressListener for advanced flowsThe post Android TextToSpeech Example with Kotlin & Java appeared first on javatechig.com.
]]>The post Add Zoom Controls to Android MapView – Google Maps SDK Guide appeared first on javatechig.com.
]]>This updated guide on javatechig.com shows how to include zoom controls in your map screen using current best practices from Google and the Maps SDK for Android.
Zoom controls are UI elements consisting of “+” (zoom in) and “–” (zoom out) buttons that let users adjust the map’s zoom level with a tap. These built‑in controls are hidden by default and can be enabled programmatically.
In addition to zoom controls, the Maps SDK supports other UI controls such as:
When using a SupportMapFragment or MapView in your layout, you can enable zoom controls directly via XML attributes:
Add the namespace:
xmlns:map="http://schemas.android.com/apk/res-auto"
This automatically turns on zoom buttons without additional code.
If you prefer to control the map settings in code, you can enable zoom controls after the map initializes.
supportFragmentManager
.findFragmentById(R.id.map)!!
.getMapAsync { googleMap ->
val uiSettings = googleMap.uiSettings
uiSettings.isZoomControlsEnabled = true
}
SupportMapFragment mapFragment = (SupportMapFragment)
getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(GoogleMap googleMap) {
googleMap.getUiSettings().setZoomControlsEnabled(true);
}
});
This shows zoom controls anchored on the map’s UI by default.
The SDK places zoom controls in the bottom‑right corner by default, but you can offset UI elements using padding:
googleMap.setPadding(left, top, right, bottom)
This helps avoid overlapping with other UI elements (e.g., buttons or toolbars).
Zoom controls work alongside gesture zoom (pinch and double‑tap). If needed, you can enable or disable gestures:
googleMap.uiSettings.isZoomGesturesEnabled = true
This ensures users can use both button controls and touch gestures.
Avoid deprecated classes such as ZoomButtonsController (which was deprecated in API 26) for maps — prefer the Maps SDK’s UiSettings.
Set padding to avoid UI overlap with action bars or other views.
Ensure your map UI (buttons, compass, gestures) functions predictably on phones, tablets, and foldables.
Enabling zoom controls on Android MapView (with the Google Maps SDK) is simple and follows modern Android practices:
UiSettingsThis implementation leverages the Maps SDK’s native UI settings and keeps your map interactions consistent and intuitive for users.
The post Add Zoom Controls to Android MapView – Google Maps SDK Guide appeared first on javatechig.com.
]]>The post JSON Feed Reader in Android appeared first on javatechig.com.
]]>This updated guide on javatechig.com explains how to implement a JSON feed reader in Android using robust networking, parsing, and UI handling techniques with both Kotlin and Java.
A JSON feed reader in Android:
Modern Android applications should use lifecycle-aware networking and asynchronous tasks to ensure smooth performance and responsiveness.
Shared modern options:
This guide focuses on Retrofit with Gson because it balances simplicity and power.
In build.gradle:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
Also add:
implementation 'androidx.recyclerview:recyclerview:1.3.1'
Assuming the JSON feed:
[
{
"title": "First Article",
"description": "Description here",
"imageUrl": "https://example.com/image.jpg"
}
]
data class FeedItem(
val title: String,
val description: String,
val imageUrl: String
)
public class FeedItem {
private String title;
private String description;
private String imageUrl;
// getters and setters
}
interface ApiService {
@GET("feed.json")
suspend fun fetchFeed(): List
}
For Java with Callbacks:
public interface ApiService {
@GET("feed.json")
Call> fetchFeed();
}
val retrofit = Retrofit.Builder()
.baseUrl("https://your-api-domain.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val api = retrofit.create(ApiService::class.java)
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://your-api-domain.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiService api = retrofit.create(ApiService.class);
lifecycleScope.launch {
try {
val feed = api.fetchFeed()
adapter.submitList(feed)
} catch (e: Exception) {
Log.e("JSONFeed", "Error: ${e.localizedMessage}")
}
}
api.fetchFeed().enqueue(new Callback>() {
@Override
public void onResponse(Call> call, Response> response) {
adapter.setData(response.body());
}
@Override
public void onFailure(Call> call, Throwable t) {
Log.e("JSONFeed", "Error: " + t.getMessage());
}
});
Create an adapter that binds FeedItem objects to a RecyclerView.
class FeedAdapter : ListAdapter(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemFeedBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
Bind data in the view holder with image loaders like Glide or Coil for performance.
Show a retry UI and message:
Show a placeholder or “No items available”.
Ensure model matches API structure. Use @SerializedName where necessary.
The post JSON Feed Reader in Android appeared first on javatechig.com.
]]>The post Different Way to Handle Events in Android appeared first on javatechig.com.
]]>This updated guide explains the different ways to manage events efficiently in Android applications using both Kotlin and Java, aligned with modern Android app architecture.
Event handling refers to reacting to user interactions such as:
Android’s event system is built on a well-defined set of listener interfaces and callback methods that ensure responsive and intuitive user experiences.
The most common interaction is a button click.
button.setOnClickListener {
Toast.makeText(this, "Button clicked!", Toast.LENGTH_SHORT).show()
}
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "Button clicked!", Toast.LENGTH_SHORT).show();
}
});
This is the basic event listener used for most UI actions.
A long press triggers when the user presses and holds.
button.setOnLongClickListener {
Toast.makeText(this, "Long pressed!", Toast.LENGTH_SHORT).show()
true
}
button.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Toast.makeText(MainActivity.this, "Long pressed!", Toast.LENGTH_SHORT).show();
return true;
}
});
Returning true indicates that the event is consumed.
Touch events give more control over pointer actions.
view.setOnTouchListener { v, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> { /* Handle down */ }
MotionEvent.ACTION_UP -> { /* Handle up */ }
}
true
}
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
});
Touch listeners provide granular control for custom interactions.
To detect gestures efficiently, use GestureDetector.
val gestureDetector = GestureDetector(this, object: GestureDetector.SimpleOnGestureListener() {
override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
// handle fling
return true
}
})
view.setOnTouchListener { _, event ->
gestureDetector.onTouchEvent(event)
}
final GestureDetector gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return true;
}
});
view.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event));
Gesture detectors simplify complex gesture handling.
In RecyclerView:
holder.itemView.setOnClickListener {
listener.onItemClick(position)
}
Implement listener in the Activity/Fragment:
adapter.setOnItemClickListener { position ->
// Handle click
}
RecyclerView event delegation ensures clear separation of UI logic.
To write scalable and maintainable event code:
Avoid findViewById() and leverage ViewBinding for type safety.
Use shared handlers or delegate interactions through interfaces.
Separate event logic from UI binding for testability.
Make sure events do not leak memory after Activity/Fragment destruction.
Using reactive streams decouples UI and business logic:
buttonClicks
.onEach { /* handle */ }
.launchIn(lifecycleScope)
Compose handles events declaratively:
Button(onClick = { /* handle */ }) {
Text("Click Me")
}
Compose replaces traditional XML + listener patterns for modern apps.
This leads to untestable code. Prefer separation with architecture patterns.
Avoid setting listeners globally for unrelated UI.
Touch events require careful threshold management (swipe vs. click).
The post Different Way to Handle Events in Android appeared first on javatechig.com.
]]>The post How to Generate APK and Install on Android Device appeared first on javatechig.com.
]]>An APK (Android Package Kit) is the packaged artifact of your app that includes compiled code, resources, and metadata. Android devices use APKs to install and run applications.
This guide focuses on APK generation and installation workflows.
Debug APKs are signed automatically by a debug keystore and are suitable for development testing.
Build → Build Bundles / APKs → Build APKs
You’ll find the APK under:
/app/build/outputs/apk/debug/app-debug.apk
For publishing or distribution outside Play Store, your app must be signed with your release key.
In Android Studio:
Build → Generate Signed Bundle / APK
Choose APK and select or create a keystore:
Select:
Once finished, locate your release APK:
/app/build/outputs/apk/release/app-release.apk
If you prefer terminal workflows:
./gradlew assembleDebug
./gradlew assembleRelease
These tasks produce builds under:
app/build/outputs/apk/
This approach is essential for CI/CD pipelines.
To install APKs on a physical device, follow these steps.
Navigate to:
Settings → Developer options → USB debugging
Toggle on.
)This method simplifies iterative testing during development.
ADB (Android Debug Bridge) is ideal for scripted installs.
adb devices
Your device should appear in the list.
adb install path/to/app-debug.apk
To reinstall and overwrite:
adb install -r path/to/app-debug.apk
ADB installation is often used in CI/CD test scripts.
Faster transfers improve productivity.
Always sign release builds with your secure keystore.
Validate compatibility across Android 8, 9, 10, 11, 12, 13 and beyond.
Google Play now prefers AAB (Android App Bundle) but APKs are still valid for testing.
Cause: APK minSdkVersion > device SDK
Fix: Build with compatible SDK
Cause: Unsigned APK
Fix: Use signed build or debug APK
The post How to Generate APK and Install on Android Device appeared first on javatechig.com.
]]>The post Android Toast Example – Show Messages in Android Apps appeared first on javatechig.com.
]]>This updated guide on javatechig.com covers how to show Toast messages using modern Android APIs in both Kotlin and Java — including custom layouts, duration control, positioning, and best practices.
A Toast is a transient message that pops up on the screen for a short duration without blocking user interaction. It’s part of the Android UI framework and requires no extra permission.
Typical use cases:
Toasts are not suitable for critical alerts — use Snackbars or Dialogs for that.
Toast.makeText(
this,
"This is a simple Toast message",
Toast.LENGTH_SHORT
).show()
Toast.makeText(
MainActivity.this,
"This is a simple Toast message",
Toast.LENGTH_SHORT
).show();
| Constant | Duration |
|---|---|
Toast.LENGTH_SHORT | ~2 seconds |
Toast.LENGTH_LONG | ~3.5 seconds |
For richer UI, you can create a custom toast layout.
val inflater = layoutInflater
val layout = inflater.inflate(R.layout.toast_layout, null)
val toast = Toast(applicationContext)
toast.duration = Toast.LENGTH_LONG
toast.view = layout
toast.show()
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.toast_layout, null);
Toast toast = new Toast(getApplicationContext());
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
By default, Toast appears near the bottom center. You can adjust the position:
toast.setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL, 0, 200)
toast.show()
toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 200);
toast.show();
Use offsets (xOffset, yOffset) to fine-tune placement.
Toasts are ephemeral and non-interactive. Use Snackbars when you need user action (e.g., undo).
Too many toasts can annoy users. Display them only for meaningful events.
Use string resources (strings.xml) instead of hard-coded text.
Item saved successfully
Cause: Wrong Context
Fix: Use Activity context (this / MainActivity.this)
Cause: Android handles durations internally
Fix: Use LENGTH_LONG or custom timer logic
Use Dialogs or Snackbars for those cases.
The post Android Toast Example – Show Messages in Android Apps appeared first on javatechig.com.
]]>The post How to Get Device Information in Android – API & Examples appeared first on javatechig.com.
]]>This updated guide on javatechig.com explains how to access key device information using current Android APIs with Kotlin and Java examples.
You might need device info for:
Always respect privacy and avoid collecting personally identifiable information without proper consent.
These identify the device make and brand:
Kotlin
val manufacturer = Build.MANUFACTURER
val model = Build.MODEL
Java
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
Combine them to form a friendly name:
val deviceName = "$manufacturer $model"
val osVersion = Build.VERSION.RELEASE
val apiLevel = Build.VERSION.SDK_INT
This helps you identify the running Android release (e.g., “Android 14”) and program compatibility.
You can retrieve additional device build properties:
| Property | API |
|---|---|
| Device name | Build.DEVICE |
| Board | Build.BOARD |
| Hardware | Build.HARDWARE |
| Product | Build.PRODUCT |
Example:
val hardware = Build.HARDWARE
Understanding screen size and density is essential for responsive design.
val metrics = Resources.getSystem().displayMetrics
val width = metrics.widthPixels
val height = metrics.heightPixels
val density = metrics.density
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
float density = metrics.density;
Use these values for layout calculations or conditional logic for different form factors.
Locale settings help customize region‑specific behavior:
val locale = Locale.getDefault().toString()
val timeZone = TimeZone.getDefault().id
This is useful for formatting dates, numbers, and language‑specific features.
You can monitor battery level with BatteryManager:
val bm = getSystemService(BATTERY_SERVICE) as BatteryManager
val batteryLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
Knowing the battery helps tailor performance or sync jobs.
Due to privacy policies (Android 10+), hardware identifiers are restricted. Use scoped identifiers instead:
val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
This ID is unique per app installation and reset on factory reset.
Note: Avoid using IMEI, MAC, or serial number for analytics — they are restricted.
For more detailed build identification:
val buildTags = Build.TAGS
val fingerprint = Build.FINGERPRINT
Fingerprint is useful for crash analytics to differentiate builds.
val supportedABIs = Build.SUPPORTED_ABIS.joinToString(", ")
This lets you know what instruction sets the device supports (e.g., “arm64‑v8a”).
Use PackageManager to check support for features:
val hasCamera = packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
Check for multi‑touch, NFC, sensors, etc., before performing hardware‑specific actions.
Many identifiers are restricted for privacy. Android disallows:
Always:
val deviceInfo = JSONObject().apply {
put("model", Build.MODEL)
put("osVersion", Build.VERSION.RELEASE)
put("api", Build.VERSION.SDK_INT)
put("screen", "$width x $height")
put("locale", Locale.getDefault())
}
This is useful for sending diagnostics or environment info to analytics.
Cause: Restricted APIs
Fix: Use scoped identifiers (android_id) and safe fallbacks.
Cause: Running outside Activity context
Fix: Use proper context (applicationContext) when retrieving metrics.
The post How to Get Device Information in Android – API & Examples appeared first on javatechig.com.
]]>The post Android Asynchronous Image Loader in ListView appeared first on javatechig.com.
]]>ListView without blocking the UI thread is essential for smooth performance. Synchronous loading causes freezes, jank, and poor user experience. Modern Android development mandates asynchronous loading coupled with efficient caching.
This updated guide on javatechig.com explains how to implement asynchronous image loading in a ListView using modern tools, lifecycle-safe practices, and efficient memory handling with both Kotlin and Java.
When you load images on the main thread:
To fix this, images must load off the main thread, updated back onto the UI when ready.
Instead of reinventing the wheel with raw threads or custom AsyncTasks (now discouraged), use well-maintained, optimized libraries:
These handle threading, caching (memory & disk), and View recycling automatically.
In module build.gradle:
implementation 'com.github.bumptech.glide:glide:4.15.1'
kapt 'com.github.bumptech.glide:compiler:4.15.1'
class ImageListAdapter(private val context: Context, private val items: List)
: BaseAdapter() {
override fun getCount() = items.size
override fun getItem(position: Int) = items[position]
override fun getItemId(position: Int) = position.toLong()
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context)
.inflate(R.layout.list_item, parent, false)
val imageView = view.findViewById(R.id.imageView)
Glide.with(context)
.load(items[position])
.placeholder(R.drawable.placeholder)
.error(R.drawable.error_image)
.into(imageView)
return view
}
}
Kotlin libraries like Coil work similarly:
imageView.load(items[position]) {
placeholder(R.drawable.placeholder)
error(R.drawable.error_image)
}
public class ImageListAdapter extends BaseAdapter {
private Context context;
private List images;
public ImageListAdapter(Context context, List images) {
this.context = context;
this.images = images;
}
@Override
public int getCount() { return images.size(); }
@Override
public Object getItem(int position) { return images.get(position); }
@Override
public long getItemId(int position) { return position; }
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context)
.inflate(R.layout.list_item, parent, false);
}
ImageView imageView = convertView.findViewById(R.id.imageView);
Glide.with(context)
.load(images.get(position))
.placeholder(R.drawable.placeholder)
.error(R.drawable.error_image)
.into(imageView);
return convertView;
}
}
ListView reuses item views. Without caching and proper recycling:
Using libraries like Glide or Coil ensures images cancel old requests when views are reused.
class ViewHolder(view: View) {
val imageView: ImageView = view.findViewById(R.id.imageView)
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val holder: ViewHolder
val view: View
if (convertView == null) {
view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false)
holder = ViewHolder(view)
view.tag = holder
} else {
view = convertView
holder = convertView.tag as ViewHolder
}
Glide.with(context)
.load(items[position])
.into(holder.imageView)
return view
}
Even with ViewHolder, libraries handle background loading and caching for you.
Image loading libraries include:
Quick retrieval during scrolling
Persistent store for repeated requests
These significantly improve performance and reduce network overhead.
Cause: Heavy image decoding on main thread
Fix: Always use libraries with background decoding
Cause: View reuse before load finishes
Fix: Libraries cancel previous requests automatically
Cause: Large bitmaps kept in memory
Fix: Use placeholder, resizing, and proper caching
While RecyclerView is recommended for most modern apps due to flexibility and performance, legacy codebases may still use ListView. In such cases, using Glide or Coil ensures acceptable performance.
The post Android Asynchronous Image Loader in ListView appeared first on javatechig.com.
]]>