Search code examples
androidandroid-recyclerviewimageviewandroid-imageview

How to properly rotate ImageView that belongs to a RecyclerView


I'm asking this question because I get a funny error by which I get two ImageView of my RecyclerView rotated when, in principle, I have only programmed it so that one does the rotation.

Background info: I have a total amount of 30 pictures that I uploaded to the drawable folder. They crashed my app when I run the code so I created a class to optimize them like the android developer guidelines advise:

public class ImagenOptimizada {


public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}


public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
                                                     int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}
}

This solved the OutOfMemoryError. But I still had the problem that some of the images I had uploaded appeared upside down (even though they appeared with the right orientation in desktop), so I decided to include the following code inside the onBindViewHolder method of the RecyclerAdapter class:

@Override
public void onBindViewHolder(@NonNull MyViewHolder viewHolder, int i) {

int image_id = images[i];

viewHolder.album.setImageBitmap(ImagenOptimizada.decodeSampledBitmapFromResource(viewHolder.album.getResources(), image_id, 250, 250));

if(image_id == R.drawable.img_2321 || image_id == R.drawable.img_2322 || image_id == R.drawable.img_2323 || image_id == R.drawable.img_2335 || image_id == R.drawable.img_2359 || image_id == R.drawable.img_2361) {

    viewHolder.album.setRotationX(180);

}


viewHolder.albumTitle.setText("Image: " + i);

}

This triggered a funny behavior as it did turn around the images I wanted but also other images with different ids too (I've realized it turns around two images, the one I want and another one, for each id that I give inside the if condition). And now I'm completely stuck because I don't know why this occurs.

Here is the complete code of RecyclerAdapter.java:

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {


    private int[] images;


    public RecyclerAdapter(int[]images){
        this.images = images;

    }



    public static class MyViewHolder extends RecyclerView.ViewHolder {

        ImageView album;
        TextView albumTitle;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);

            album = itemView.findViewById(R.id.album);                                                  // R.id.album porque album es el id que dimos a la ImageView en image_and_text_layout.xml
            albumTitle = itemView.findViewById(R.id.album_title);                                       // R.id.album_title porque album_title es el id que dimos al TextView en image_and_text_layout.xml
        }
    }



    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {

        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.image_and_text_layout, viewGroup, false);

        MyViewHolder myViewHolder = new MyViewHolder(view);

        return myViewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder viewHolder, int i) {

        int image_id = images[i];

        viewHolder.album.setImageBitmap(ImagenOptimizada.decodeSampledBitmapFromResource(viewHolder.album.getResources(), image_id, 250, 250));

        if(image_id == R.drawable.img_2321 || image_id == R.drawable.img_2322 || image_id == R.drawable.img_2323 || image_id == R.drawable.img_2335 || image_id == R.drawable.img_2359 || image_id == R.drawable.img_2361) {

            viewHolder.album.setRotationX(180);

        }


        viewHolder.albumTitle.setText("Image: " + i);

    }

    @Override
    public int getItemCount() {
        return images.length;
    }
}

This is my MainActivity.class:

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private int[] images = {R.drawable.img_2321, R.drawable.img_2322, R.drawable.img_2323, R.drawable.img_2325, R.drawable.img_2326, R.drawable.img_2328,
            R.drawable.img_2329, R.drawable.img_2333, R.drawable.img_2335, R.drawable.img_2356, R.drawable.img_2358, R.drawable.img_2359, R.drawable.img_2360,
            R.drawable.img_2361, R.drawable.img_2363, R.drawable.img_2365, R.drawable.img_2366, R.drawable.img_2383, R.drawable.img_2390, R.drawable.img_2393, R.drawable.img_2394,
            R.drawable.img_2396, R.drawable.img_2397, R.drawable.img_2398, R.drawable.img_2454, R.drawable.img_2455, R.drawable.img_2456, R.drawable.img_2457,
            R.drawable.img_2458, R.drawable.img_2465};

    private RecyclerView.LayoutManager layoutManager;
    private RecyclerView.Adapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setHasFixedSize(true);

        layoutManager = new GridLayoutManager(this, 2);
        recyclerView.setLayoutManager(layoutManager);

        adapter = new RecyclerAdapter(images);
        recyclerView.setAdapter(adapter);


    }
}

Thank you in advance to those who take the time to answer. I'm very grateful because I'm a newcomer to programming and I'm studying on my own which makes it hard at times.


Solution

  • RecyclerView will reuse ViewHolders that have scrolled off the screen, but it doesn't automatically reset them. So you might have an ImageView that you already applied a rotation to being used for an image you don't want rotated. The solution is to explicitly call setRotation(0) in that case. The easiest way to do that in your code is probably to add an else block to the if statement where you apply the rotation currently.

    if(...) {
        viewHolder.album.setRotationX(180);
    } else {
        viewHOlder.album.setRotationX(0)
    }