Monday, October 3, 2016

How to build an image gallery app – full tutorial with code

P1000207

There are many reasons you might find yourself needing to create an image gallery – whether it's to show off album covers for a music app, to present feature images for articles in a feed, or to showcase your work in a portfolio. To make the right impression though, these apps should allow users to effortlessly swipe through multiple images without slowdown and that's where things get a little tricky.

This tutorial will show you how to create a seamless gallery filled with nice big images and then adapt that for a number of different applications. Along the way, we'll see how to use RecyclerViews, adapters and Picasso – so hopefully it will make for a great learning exercise, whatever you end up doing with it! Full code and project included below…

recyclerview-featuredSee also: Using RecyclerView to build lists in Android

Introducing RecyclerView

To create our Android gallery, we're going to use something called a RecyclerView. This is a handy view that acts very much like a ListView but with the advantage of allowing us to scroll quickly through large data sets. It does this by only loading the images that are currently in view at any given time. This means we can load more images without the app becoming very slow. There's a lot more that you can do with this view and it's used all over Google's own apps, so check out the full explanation to using RecyclerView to find out more.

The good news is that this is all we really need to create our gallery – a RecyclerView filled with images. The bad news is that the RecyclerView is a little more complicated than most other views. Because of course it is.

RecyclerView is not, for starters, available to drag and drop using the design view. So we'll just have to add it to the activity_main.xml, like so:

  <android.support.v7.widget.RecyclerView              android:id="@+id/imagegallery"              android:layout_width="match_parent"              android:layout_height="match_parent"              />  

Notice that we're referencing the Android Support Library. This means we also need to modify our build.gradle in order to include the dependency. Just add this line to the app level file:

  compile 'com.android.support:recyclerview-v7:24.2.1'  

And if that's not installed, then you're going to have to open the SDK manager and install it. Fortunately, Android Studio is pretty smart about prompting you to do all this. I just got a new computer, so I can play along with you!

Support repository

Head back to the XML and it should now be working just fine. Except the list is not populated except with 'item 1, item 2, item 3'. What we need to do, is load our images into here.

working list

Creating your list of images

As mentioned, populating our recycler view is a little more complicated than using a regular list. By which, I mean it's way more complicated… but it's a great chance for us to learn some handy new skills. So there's that.

For a RecyclerView, we're also going to need a layout manager and an adapter. This is what's going to allow us to organize the information in our view and add the images. We'll start by initializing our views and attaching an adapter in the onCreate of MainActivity.java. This looks like so:

  setContentView(R.layout.activity_main);    RecyclerView recyclerView = (RecyclerView)findViewById(R.id.imagegallery);  recyclerView.setHasFixedSize(true);    RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getApplicationContext(),2);  recyclerView.setLayoutManager(layoutManager);  ArrayList<CreateList> createLists = prepareData();  MyAdapter adapter = new MyAdapter(getApplicationContext(), createLists);  recyclerView.setAdapter(adapter);  

We're setting the layout as activity_main, then we're finding the RecyclerView and initializing it. Notice that we use HasFixedSize to make sure that it won't stretch to accommodate the content. We're also creating the layout manager and the adapter here. There are multiple types of layout manager but true to gallery-form, we're going to pick a grid rather than a long list. Remember to import the GridLayoutManager and the RecyclerView as Android Studio prompts you to do so. Meanwhile, when you highlight MyAdapter, you'll be given the option to 'Create Class MyAdapter'. Go for it – make your own MyAdapter.Java and then switch back. We'll come back to this later.

Before we can use the new adapter class, we first need to create our data set. This is going to take the form of an array list. So in other words, we're going to place a list of all our images in here, which the adapter will then read and use to fill out the RecyclerView.

Just to make life a little more complicated, creating the Array List is also going to require a new class. First though, create a string array and an integer array in the same MainActivity.Java:

  private final String image_titles[] = {          "Img1",          "Img2",          "Img3",          "Img4",          "Img5",          "Img6",          "Img7",          "Img8",          "Img9",          "Img10",          "Img11",          "Img12",          "Img13",  };    private final Integer image_ids[] = {          R.drawable.img1,          R.drawable.img2,          R.drawable.img3,          R.drawable.img4,          R.drawable.img5,          R.drawable.img6,          R.drawable.img7,          R.drawable.img8,          R.drawable.img9,          R.drawable.img10,          R.drawable.img11,          R.drawable.img12,          R.drawable.img13,  };  

The strings can be anything you want – these will be the titles of your images. As for the integers, these are image IDs. This means they need to point to images in your Drawables folder. Drop some images into there that aren't too massive and make sure the names are all correct.

Remember: a list is a collection of variables (like strings or integers), whereas an array is more like a filing cabinet of variables. By creating an ArrayList then, we're basically creating a list of filing cabinets, allowing us to store two collections of data in one place. In this case, the data is a selection of image titles and image IDs.

Now create a new Java Class called CreateList and add this code:

  public class CreateList {        private String image_title;      private Integer image_id;        public String getImage_title() {          return image_title;      }        public void setImage_title(String android_version_name) {          this.image_title = android_version_name;      }        public Integer getImage_ID() {          return image_id;      }        public void setImage_ID(Integer android_image_url) {          this.image_id = android_image_url;      }  }  

What we have here is a method we can use to add new elements (setImage_title, setImage_ID) and retrieve them (getImage_title, getImage_ID). This will let us run through the two arrays we made and stick them into the ArrayList. You'll need to import array lists.

We do this, like so:

      private ArrayList<CreateList> prepareData(){            ArrayList<CreateList> theimage = new ArrayList<>();          for(int i = 0; i< image_titles.length; i++){              CreateList createList = new CreateList();              createList.setImage_title(image_titles[i]);              createList.setImage_ID(image_ids[i]);              theimage.add(createList);          }          return theimage;      }  }  

So we're performing a loop while we go through all the image titles and adding them to the correct array in the ArrayList one at a time. Each time, we're using the same index (i), in order to add the image ID to its respective location.

P1000208

Confused yet?

Using the adapter

Before you head over to MyAdapter.java, you first need to create a new XML layout in the layout directory. I've called mine cell_layout.xml and it looks like so:

  <LinearLayout xmlns:android="http://ift.tt/nIICcg"      android:orientation="vertical"      android:background="#FFFFFF"      android:layout_width="match_parent"      android:layout_height="wrap_content">      <ImageView          android:id="@+id/img"          android:adjustViewBounds="true"          android:layout_width="match_parent" />      <TextView          android:id="@+id/title"          android:layout_gravity="center"          android:textColor="#000"          android:textStyle="bold"          android:layout_width="wrap_content"          android:layout_height="wrap_content" />  </LinearLayout>  

All this is, is the layout for the individual cells in our grid layout. Each one will have an image at the top, with text just underneath. Nice.

Now you can go back to your MyAdapter.java. This is where we're going to take the list, take the cell layout and then use both those things to fill the RecyclerView. We already attached this to the RecyclerView in MainActivity.Java, so now all that's left is… lots and lots of complex code.

It's probably easiest if I just show you…

  public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {      private ArrayList<CreateList> galleryList;      private Context context;        public MyAdapter(Context context, ArrayList<CreateList> galleryList) {          this.galleryList = galleryList;          this.context = context;      }        @Override      public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {          View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.cell_layout, viewGroup, false);          return new ViewHolder(view);      }        @Override      public void onBindViewHolder(MyAdapter.ViewHolder viewHolder, int i) {          viewHolder.title.setText(galleryList.get(i).getImage_title());          viewHolder.img.setScaleType(ImageView.ScaleType.CENTER_CROP);          viewHolder.img.setImageResource((galleryList.get(i).getImage_ID()));              }        @Override      public int getItemCount() {          return galleryList.size();      }        public class ViewHolder extends RecyclerView.ViewHolder{          private TextView title;          private ImageView img;          public ViewHolder(View view) {              super(view);                title = (TextView)view.findViewById(R.id.title);              img = (ImageView) view.findViewById(R.id.img);          }      }  }  

So what we're doing here is to get our ArrayList and then create a ViewHolder. A ViewHolder makes it easier for us to iterate lots of views without having to write findViewByID every time – which would be impractical for a very long list.

We create the VewHolder by referencing the cell_layout file we created earlier, and then bind it with the data from our ArrayList. We find the TextView first and set that to be the relevant string, then we find the ImageView and use the image ID integer to set the image resource. Notice that I've also setScaleType to CENTER_CROP. This means that the image will be centered but cropped to fill the enter cell in a relatively attractive manner. There are other scale types but I believe that this is by far the most attractive for our purposes.

Don't forget to import the ImageView and TextView classes. And remember you need to add some images to your drawables folder. Once you've done that though you should be ready to go!

Give it a try and you should end up with something that looks a little like this:

Screenshot_20160929-124324

Except without all the pictures of me… This is just what I happened to have to hand, don't judge!

Not working as expected? Don't worry – this is a pretty complicated app for beginners. You can find the full thing over at GitHub here and then just work through each step while referring to the code.

displaying-data-in-cardviews-and-gridviews-16x9-720pSee also: Displaying large amounts of data with GridView and CardView2

Making this into a useful app

So right now we have a strange slideshow of photos of me. Not really a great app…

So what might you use this code for? Well, there are plenty of apps that essentially boil down to galleries – this would be a great way to create a portfolio for your business for example, or perhaps a visual guide of some sort.

In that case, we might want to add an onClick so that we can show some information, or perhaps a larger version of the image when someone taps their chosen item. To do this, we just need to import the onClickListener and then add this code to onBindViewHolder:

  viewHolder.img.setOnClickListener(new OnClickListener() {      @Override      public void onClick(View v) {          Toast.makeText(context,"Image",Toast.LENGTH_SHORT).show();      }  });  

If we wanted to load a selection of photos on the user's device meanwhile, we'd simply have to list files in a particular directory. To do that, we'd just need to use listFiles to take the file names and load them into our ListArray list, using something list this:

  String path = Environment.getRootDirectory().toString();  File f = new File(path);  File file[] = f.listFiles();  for (int i=0; i < file.length; i++)  {      CreateList createList = new CreateList();      createList.setImage_Location(file[i].getName());  }  

Except you'll be changing your path string to something useful, like the user's camera roll (rather than the root directory). Then you can load the bitmaps from the images on an SD card or internal storage by using the image name and path, like so:

  Bitmap bmp = BitmapFactory.decodeFile(pathName);  ImageView img;  img.setImageBitmap(bmp);  

You'll probably want to get thumbnails from them too. This way, the list will be populated dynamically – so that when new photos are added to that directory, you're gallery will update to show them each time you open it. This is how you might make a gallery app for displaying the images on a user's phone, for example.

Or alternatively, another way we could make this app a little fancier, would be to download images from the web.

This might sound like a whole extra chapter but it's actually pretty simple as well. You just need to use the Picasso library, which is very easy and completely free. First, add the dependency like we did earlier:

  compile 'com.squareup.picasso:picasso:2.5.0'  

Then, change your ArrayList to contain two string arrays instead of a string and an integer. Instead of image IDs, you're going to fill this second string array with URLs for your images (in inverted commas). Now you just swap out the line in your onBindViewHolder to:

  Picasso.with(context).load(galleryList.get(i).getImage_ID()).resize(240, 120).into(viewHolder.img);  

Remember to add the relevant permission and it really is that easy – you can now download your images right from a list of URLs and that way update them on the fly without having to update the app! Picasso will also cache images to keep things nice and zippy for you.

Note as well that if you wanted to have more than two images per row, you would simply swap:

  RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getApplicationContext(),2);  

For:

  RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getApplicationContext(),3);  

This will give you something like the following:

Screenshot_20160929-125455

If you don't like the text and you just want images, then you can easily remove the string array from proceedings. Or for a quick hack if you don't want to stray too far from my code, you can just make the TextView super thin.

Screenshot_20160929-130924-16x9-720p

Closing comments

And there you have it – your very own basic image gallery. There are plenty of uses for this and hopefully you've learned a few useful bits and pieces along the way. Stay tuned for more tutorials just like this one!

And remember, the full project can be found here for your reference.

P1000209



from Android Authority http://ift.tt/2doICCf
via IFTTT

No comments:

Post a Comment