1

I am creating simple image gallery app in which images are shown on MainActivity inside RecyclerView and on click of Any Image it will take us to SlideActivity in which I want to show images on ViewPager and then we swipe all images should be shown on ViewPager. For retrieving images i am using Glide. Images Load on RecyclerView Perfectly Fine but when i click on any Image i get this Exception "java.lang.ArrayIndexOutOfBoundsException: length=15; index=-1". I know there are many same questions like this I saw all codes but did not understood so finally decided to ask question I am pasting code whatever i tried till now. Any Suggestion or advice will be helpful bcz I work alone I have no Mentor and I am Learning. Thank you!

Error :

java.lang.ArrayIndexOutOfBoundsException: length=15; index=-1
        at java.util.ArrayList.get(ArrayList.java:439)
        at com.starmoonsolutions.imagegallery.RecycleViewAdapter$ViewHolder$1.onClick(RecycleViewAdapter.java:63)
        at android.view.View.performClick(View.java:6256)
        at android.view.View$PerformClick.run(View.java:24697)
        at android.os.Handler.handleCallback(Handler.java:789)
        at android.os.Handler.dispatchMessage(Handler.java:98)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6541)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

ImageUrl.java

public class ImageUrl {
    String imageUrl;

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}

MainActivity:

public class MainActivity extends AppCompatActivity {

    private ImageView imageView;
    RecyclerView recyclerView;
    GridLayoutManager gridLayoutManager;
    ArrayList<ImageUrl> imageUrlList;

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

        imageView = findViewById(R.id.imageView);
        recyclerView = findViewById(R.id.recyclerView);
        gridLayoutManager = new GridLayoutManager(getApplicationContext(), 2);
        recyclerView.setLayoutManager(gridLayoutManager);

        imageUrlList = prepareData();
        RecycleViewAdapter galleryAdapter = new RecycleViewAdapter(getApplicationContext(), imageUrlList);
        recyclerView.setAdapter(galleryAdapter);
    }
    private ArrayList prepareData(){
        // here you should give your image URLs and that can be a link from the Internet
        String imageUrls[] = {
                "https://images.pexels.com/photos/358457/pexels-photo-358457.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260",
                "https://images.pexels.com/photos/934964/pexels-photo-934964.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
                "https://images.pexels.com/photos/1202887/pexels-photo-1202887.jpeg?auto=compress&cs=tinysrgb&h=650&w=940",
                "https://images.pexels.com/photos/337909/pexels-photo-337909.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
                "https://images.pexels.com/photos/894443/pexels-photo-894443.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
                "https://images.pexels.com/photos/951325/pexels-photo-951325.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
                "https://images.pexels.com/photos/1534411/pexels-photo-1534411.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
                "https://images.pexels.com/photos/266436/pexels-photo-266436.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
                "https://images.pexels.com/photos/1894350/pexels-photo-1894350.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
                "https://images.pexels.com/photos/87009/earth-soil-creep-moon-lunar-surface-87009.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
                "https://images.pexels.com/photos/1820563/pexels-photo-1820563.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
                "https://images.pexels.com/photos/1274260/pexels-photo-1274260.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
        };
        imageUrlList = new ArrayList<>();
        for (int i = 0; i < imageUrls.length; i++) {
            ImageUrl imageUrl = new ImageUrl();
            imageUrl.setImageUrl(imageUrls[i]);
            imageUrlList.add(imageUrl);
        }
        Log.d("MainActivity", "List count: " + imageUrlList.size());
        return  imageUrlList;
    }
}

RecyclerViewAdapter:

    public class RecycleViewAdapter extends RecyclerView.Adapter<RecycleViewAdapter.ViewHolder>{

        private ArrayList<ImageUrl> imageUrls;
        private Context context;

        public RecycleViewAdapter(Context context, ArrayList<ImageUrl> imageUrls) {
            this.imageUrls = imageUrls;
            this.context = context;
        }

        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.image_layout, viewGroup, false);
            return new ViewHolder(view);
        }

        /**
         * gets the image url from adapter and passes to Glide API to load the image
         *
         * @param viewHolder
         * @param i
         */
        @Override
        public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
            Glide.with(context).load(imageUrls.get(i).getImageUrl()).into(viewHolder.img);
        }

        @Override
        public int getItemCount() {
            return imageUrls.size();
        }

        public class ViewHolder extends RecyclerView.ViewHolder{

            RecyclerView recyclerView;
            ImageView img;

            public ViewHolder(@NonNull final View itemView) {
                super(itemView);
                img = itemView.findViewById(R.id.imageView);
                recyclerView = itemView.findViewById(R.id.recyclerView);
                final int pos = getAdapterPosition();
                itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Intent intent = new Intent(context, SlideActivity.class);
error point here->      intent.putExtra("image", String.valueOf(imageUrls.get(pos).getImageUrl())); <- error point here 
                        intent.putExtra("position", pos);
                        intent.putExtra("list", imageUrls.get(pos).imageUrl);
                        context.startActivity(intent);
                    }
                });
            }
        }
    }

SlideActivity(ViewPager):

public class SlideActivity extends AppCompatActivity {
    private ViewPager viewPager;
    private Context context = SlideActivity.this;
    private ViewPagerAdapter adapter;
    private ArrayList<String> list;
    String image;
    private int position;

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

        viewPager = findViewById(R.id.viewPager);
        getIntentInfo();

        adapter = new ViewPagerAdapter(list,context);
        viewPager.setAdapter(adapter);
        viewPager.setCurrentItem((position));
    }

    private void getIntentInfo() {
        image = getIntent().getStringExtra("image");
        list  = getIntent().getStringArrayListExtra("list");
        position = getIntent().getIntExtra("position",0);
    }
}

ViewPagerAdapter:

public class ViewPagerAdapter extends PagerAdapter {
    private ArrayList<String> imageUrls;
    private android.content.Context context;
    private LayoutInflater inflater;
    private ImageView wallpaper;

    public ViewPagerAdapter() {

    }

    public ViewPagerAdapter(ArrayList<String> imageUrls, android.content.Context context) {
        this.imageUrls = imageUrls;
        this.context = context;
    }

    @Override
    public int getCount() {
        return imageUrls.size();
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
        return view.equals(o);
    }

    @NonNull
    @Override
    public Object instantiateItem(@NonNull final ViewGroup container, final int position) {

        inflater = LayoutInflater.from(container.getContext());
        View view = inflater.inflate(R.layout.pager_item, container, false);
        wallpaper = view.findViewById(R.id.walpaperImage);
        Glide.with(context).asBitmap().load(imageUrls.get(position)).into(wallpaper);
        container.addView(view, 0);
        return view;
    }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        container.removeView((View) object);
    }
}
Rakesh
  • 53
  • 1
  • 8

1 Answers1

0

The problem is actually in getting the adapter position.

public ViewHolder(@NonNull final View itemView) {
                super(itemView);
                img = itemView.findViewById(R.id.imageView);
                recyclerView = itemView.findViewById(R.id.recyclerView);

                // remove the below line
                //final int pos = getAdapterPosition();

                itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Intent intent = new Intent(context, SlideActivity.class);

                        // you need to freshly get the adapter position here.
                        int pos = getAdapterPosition();

error point here->      intent.putExtra("image", String.valueOf(imageUrls.get(pos).getImageUrl())); <- error point here 
                        intent.putExtra("position", pos);
                        intent.putExtra("list", imageUrls.get(pos).imageUrl);
                        context.startActivity(intent);
                    }
                });
            }

Since ViewHolder gets recycled, adapter position can change therefore you need to get the latest adapter position inside your click listener.

And regarding the Bundle data, pass the list of URLs of 'String' type instead of the custom object,

public void onClick(View v) { 
Intent intent = new Intent(context, SlideActivity.class); 
error point here-> intent.putExtra("image", String.valueOf(imageUrls.get(pos).getImageUrl())); <- error point here 
intent.putExtra("position", pos); 

// imageUrls is of type ArrayList<String>
intent.putStringArrayListExtra("list", imageUrls);

context.startActivity(intent); }

or else use the Parcelable library to serialize the custom object.

Binary Baba
  • 1,953
  • 2
  • 16
  • 23
  • Did you try changing the above code? I have added 2 lines above the error point. – Binary Baba Apr 18 '19 at 07:30
  • I have edited the code (added empty lines around the new changes). Let me know if you are still facing any problem. – Binary Baba Apr 18 '19 at 07:33
  • yes now i got nullpointer exception in viewpageradapter class on this method-- @Override public int getCount() { return imageUrls.size(); } – Rakesh Apr 18 '19 at 07:37
  • Seems like this line `list = getIntent().getStringArrayListExtra("list");` is returning `null`. – Binary Baba Apr 18 '19 at 07:59
  • Whatever data you are putting in the `Intent`, you need to use the corresponding data type method to get the data. In you code, you have put the key `intent.putExtra("list", imageUrls.get(pos).imageUrl);` and while getting it you are using `getIntent().getStringArrayListExtra("list");`. There is a mismatch in the data type. – Binary Baba Apr 18 '19 at 08:12
  • Read more about passing/consuming bundle data [here](https://zocada.com/using-intents-extras-pass-data-activities-android-beginners-guide/) and [here](https://stackoverflow.com/questions/10107442/android-how-to-pass-parcelable-object-to-intent-and-use-getparcelable-method-of). and if this answer solves your original problem, can you mark it as correct please. – Binary Baba Apr 18 '19 at 08:12
  • if its a mismatch then how to solve this? what i shud pass from recyclerview adapter to viewpager list of images or images url ? – Rakesh Apr 18 '19 at 08:26
  • If you can maintain array of strings (url strings) instead `ImageUrl`, then you can use `putStringArrayListExtra("list", ..)` and while getting use: `getIntent().getStringArrayListExtra("list")`. If not, you will have to use `parcelable` for custom objects like `ImageUrl` which is not so trivial. Learn more about it [here](https://medium.com/@nikhildhyani365/understand-parcelable-in-android-27ce420d695b) – Binary Baba Apr 18 '19 at 08:53
  • Ok and there is a need of passing image url or result can be achieved by passing position only? – Rakesh Apr 18 '19 at 09:32
  • In your `prepareData()` method, can you directly create a list of strings instead of `ImageUrl`? – Binary Baba Apr 18 '19 at 09:45
  • ohh tysm brother now its working but can you explain me little bit about this why its working now and how can i achieve same results with model class also? – Rakesh Apr 18 '19 at 10:32
  • If I have answered your problem, can you please upvote/mark this answer. The reason it works with `String` is because we already have handy methods to process for basic types like `int`, `String` etc. For model classes we need to do extra work. There is also a [library](https://github.com/johncarl81/parceler) which makes it easier to work with `Parcelable`. Thanks. – Binary Baba Apr 18 '19 at 10:44
  • I have edited the answer to incorporate the changes discussed in the comments. – Binary Baba Apr 18 '19 at 18:03