I found a free Android app that makes deleting photos as easy as swiping left
Back to Tutorials
techTutorialintermediate

I found a free Android app that makes deleting photos as easy as swiping left

June 10, 202613 views6 min read

Learn to build an Android photo deletion utility with swipe-to-delete functionality similar to the Sponge app, using RecyclerView and MediaStore APIs.

Introduction

In this tutorial, we'll explore how to build a photo deletion utility for Android that mimics the swipe-to-delete functionality found in apps like Sponge. This project will teach you how to work with Android's touch handling, RecyclerView components, and media management APIs to create an efficient photo cleanup solution.

While the original article mentions a free app that makes photo deletion easy, we'll create our own implementation that demonstrates the core concepts behind such functionality.

Prerequisites

Before diving into this tutorial, you should have:

  • Basic understanding of Android development with Java or Kotlin
  • Android Studio installed and configured
  • Familiarity with RecyclerView and Adapter patterns
  • Experience with Android's MediaStore API
  • Android device or emulator with sample photos

Step-by-Step Instructions

1. Create a new Android project

Start by creating a new Android project in Android Studio with an Empty Activity. Name it "PhotoSwipeDeleter" and ensure the minimum SDK is set to API 21 or higher.

2. Add required dependencies

Open your app-level build.gradle file and add the necessary dependencies:

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.3.0'
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
    implementation 'com.github.bumptech.glide:glide:4.15.1'
    implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.2'
    implementation 'androidx.lifecycle:lifecycle-livedata:2.6.2'
}

Why: We need RecyclerView for displaying photos, Glide for image loading, and Lifecycle components for proper memory management.

3. Create the photo model class

Create a new Java class called Photo to represent each photo in our gallery:

public class Photo {
    private String id;
    private String path;
    private long dateTaken;
    private boolean isDeleted;
    
    public Photo(String id, String path, long dateTaken) {
        this.id = id;
        this.path = path;
        this.dateTaken = dateTaken;
        this.isDeleted = false;
    }
    
    // Getters and setters
    public String getId() { return id; }
    public String getPath() { return path; }
    public long getDateTaken() { return dateTaken; }
    public boolean isDeleted() { return isDeleted; }
    public void setDeleted(boolean deleted) { isDeleted = deleted; }
}

Why: This model class will help us track photo information and deletion status throughout our application.

4. Design the photo item layout

Create a new layout file item_photo.xml in the res/layout folder:

<androidx.cardview.widget.CardView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:layout_margin="8dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="4dp"
    >
    
    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        >
        
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:scaleType="centerCrop"
            />
            
        <TextView
            android:id="@+id/dateTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:textSize="12sp"
            android:textColor="#666666"
            />
            
    </LinearLayout>
    
</androidx.cardview.widget.CardView>

Why: This layout defines how each photo will appear in our RecyclerView, including the image and date information.

5. Create the RecyclerView adapter

Create a new class PhotoAdapter that extends RecyclerView.Adapter:

public class PhotoAdapter extends RecyclerView.Adapter {
    private List photos;
    private OnPhotoSwipeListener swipeListener;
    
    public PhotoAdapter(List photos) {
        this.photos = photos;
    }
    
    public void setSwipeListener(OnPhotoSwipeListener listener) {
        this.swipeListener = listener;
    }
    
    @NonNull
    @Override
    public PhotoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_photo, parent, false);
        return new PhotoViewHolder(view);
    }
    
    @Override
    public void onBindViewHolder(@NonNull PhotoViewHolder holder, int position) {
        Photo photo = photos.get(position);
        holder.bind(photo);
        
        // Set up swipe gesture detection
        holder.itemView.setOnTouchListener(new View.OnTouchListener() {
            private float startX;
            private float startY;
            private boolean isSwiping = false;
            
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        startX = event.getX();
                        startY = event.getY();
                        isSwiping = false;
                        break;
                    
                    case MotionEvent.ACTION_MOVE:
                        if (!isSwiping) {
                            float deltaX = event.getX() - startX;
                            float deltaY = event.getY() - startY;
                            if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 50) {
                                isSwiping = true;
                                if (deltaX > 0) {
                                    // Swipe right - undo
                                    holder.itemView.animate().translationX(0).setDuration(200);
                                } else {
                                    // Swipe left - delete
                                    holder.itemView.animate().translationX(-holder.itemView.getWidth())
                                            .setDuration(200)
                                            .withEndAction(() -> {
                                                if (swipeListener != null) {
                                                    swipeListener.onPhotoDeleted(position);
                                                }
                                            });
                                }
                            }
                        }
                        break;
                    
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_CANCEL:
                        if (isSwiping) {
                            holder.itemView.animate().translationX(0).setDuration(200);
                        }
                        break;
                }
                return true;
            }
        });
    }
    
    @Override
    public int getItemCount() {
        return photos.size();
    }
    
    public void removePhoto(int position) {
        photos.remove(position);
        notifyItemRemoved(position);
    }
    
    static class PhotoViewHolder extends RecyclerView.ViewHolder {
        ImageView imageView;
        TextView dateTextView;
        
        public PhotoViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView = itemView.findViewById(R.id.imageView);
            dateTextView = itemView.findViewById(R.id.dateTextView);
        }
        
        public void bind(Photo photo) {
            // Load image using Glide
            Glide.with(itemView.getContext())
                    .load(photo.getPath())
                    .placeholder(R.drawable.placeholder_image)
                    .into(imageView);
            
            // Format date
            SimpleDateFormat sdf = new SimpleDateFormat("MMM dd, yyyy", Locale.getDefault());
            dateTextView.setText(sdf.format(new Date(photo.getDateTaken())));
        }
    }
    
    public interface OnPhotoSwipeListener {
        void onPhotoDeleted(int position);
    }
}

Why: This adapter handles the swipe gestures and photo display, making it easy to delete photos with a simple swipe motion.

6. Implement the main activity

In your MainActivity.java, set up the RecyclerView and load photos:

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private PhotoAdapter adapter;
    private List photoList;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        initViews();
        loadPhotos();
        setupRecyclerView();
    }
    
    private void initViews() {
        recyclerView = findViewById(R.id.recyclerView);
        photoList = new ArrayList<>();
    }
    
    private void loadPhotos() {
        // Query MediaStore for photos
        Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        String[] projection = {
            MediaStore.Images.Media._ID,
            MediaStore.Images.Media.DATA,
            MediaStore.Images.Media.DATE_TAKEN
        };
        
        Cursor cursor = getContentResolver().query(
            uri, projection, null, null, null);
        
        if (cursor != null) {
            while (cursor.moveToNext()) {
                String id = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID));
                String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
                long dateTaken = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN));
                
                photoList.add(new Photo(id, path, dateTaken));
            }
            cursor.close();
        }
    }
    
    private void setupRecyclerView() {
        adapter = new PhotoAdapter(photoList);
        adapter.setSwipeListener(new PhotoAdapter.OnPhotoSwipeListener() {
            @Override
            public void onPhotoDeleted(int position) {
                deletePhoto(position);
            }
        });
        
        recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
        recyclerView.setAdapter(adapter);
    }
    
    private void deletePhoto(int position) {
        Photo photo = photoList.get(position);
        File file = new File(photo.getPath());
        
        if (file.exists()) {
            boolean deleted = file.delete();
            if (deleted) {
                // Remove from list and update UI
                photoList.remove(position);
                adapter.notifyItemRemoved(position);
                
                Toast.makeText(this, "Photo deleted", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "Failed to delete photo", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

Why: This activity coordinates the photo loading, RecyclerView setup, and deletion functionality, creating the core swipe-to-delete experience.

7. Update the main layout

Modify your activity_main.xml to include the RecyclerView:

<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />
    
</androidx.constraintlayout.widget.ConstraintLayout>

Why: This layout provides a full-screen RecyclerView container for our photo gallery.

Summary

In this tutorial, we've built a photo deletion utility that mimics the swipe-to-delete functionality of apps like Sponge. We created a RecyclerView-based gallery that allows users to swipe left on photos to delete them, implemented touch gesture detection for swipe recognition, and connected to Android's MediaStore API to access and delete photos. This project demonstrates core Android development concepts including RecyclerView adapters, touch event handling, and media management APIs.

The key learning points include understanding how to implement swipe gestures for user interactions, managing photo data with custom models, and working with Android's media storage system. This foundation can be extended with features like undo functionality, batch deletion, or cloud backup integration.

Source: ZDNet AI

Related Articles