Here are My version of solution to “Challenge: Paging”.It is based on Challenge 1 due to the requirement to access page information.
Major changes that I made:
- Two Class “GalleryPage”, “PhotoPages” created and made static singleton so that the paging information can be access throughout the project.
- onScrollListener added for recyclerView
- page display text added to the bottom of the fragment.
- setupAdapter() is only called once when mPhotoRecyclerView.getAdapter()==null
- onPostExecute() is delegated to process extra UI updates
- FlickrFetchr.parseItems() no longer needed
GalleryPage.java
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class GalleryPage {
PhotoPages photos;
String stat;
public static GalleryPage sGalleryPage;
public static GalleryPage getGalleryPage() {
if (sGalleryPage == null) {
sGalleryPage = new GalleryPage();
}
return sGalleryPage;
}
private GalleryPage() { }
public List<GalleryItem> getGalleryItemList() {
return photos.getPhotoList();
}
public int getTotalPages() {
return photos.pages;
}
public int getItemPerPage() {
return photos.perpage;
}
}
class PhotoPages {
int page;
int pages;
int perpage;
int total;
@SerializedName("photo")
List<GalleryItem> photoList;
List<GalleryItem> getPhotoList() {
return photoList;
}
}
PhotoFragmentGallery.java
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.TextView;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class PhotoGalleryFragment extends Fragment {
private static String TAG = "PhotoGallery";
private RecyclerView mPhotoRecyclerView;
private TextView mCurrentPageText;
private GridLayoutManager mGridManager;
private List<GalleryItem> mItems = new ArrayList<>();
private int pageFetched = 0;
boolean asyncFetching = false;
int mCurrentPage = 1;
int mMaxPage = 1;
int mItemsPerPage = 1;
public static PhotoGalleryFragment newInstance(){
return new PhotoGalleryFragment();
}
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setRetainInstance(true);
new FetchItemTask().execute();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState){
View v =inflater.inflate(R.layout.fragment_photo_gallery,container,false);
mCurrentPageText = (TextView) v.findViewById(R.id.currentPageText);
mPhotoRecyclerView = (RecyclerView) v.findViewById(R.id.photo_recycler_view);
mPhotoRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mGridManager= new GridLayoutManager(getActivity(),3);
mPhotoRecyclerView.setLayoutManager(mGridManager);
mPhotoRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
mPhotoRecyclerView.addOnScrollListener( new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
int lastVisibleItem = mGridManager.findLastVisibleItemPosition();
int firstVisibleItem = mGridManager.findFirstVisibleItemPosition();
Log.d(TAG, "Scrolling, First Item : "+ firstVisibleItem +" Last Item : "+lastVisibleItem);
if (!(asyncFetching) && (dy > 0) && (mCurrentPage < mMaxPage) && (lastVisibleItem >= (mItems.size() - 1))) {
Log.d(TAG, "Fetching more items");
new FetchItemTask().execute();
} else updatePageText(firstVisibleItem);
}
});
if(mPhotoRecyclerView.getAdapter()==null)setupAdapter();
return v;
}
private void updatePageText(int pos){
mCurrentPage = pos / mItemsPerPage+1;
mCurrentPageText.setText("Page " + mCurrentPage + " of " + mMaxPage);
}
private void setupAdapter(){
if(isAdded()){
Log.d(TAG,"Setup Adapter");
mPhotoRecyclerView.setAdapter(new PhotoAdapter(mItems));
}
}
private class PhotoHolder extends RecyclerView.ViewHolder{
private TextView mTitleTextView;
public PhotoHolder(View itemView){
super(itemView);
mTitleTextView = (TextView) itemView;
}
public void bindGalleryItem(GalleryItem item){
mTitleTextView.setText(item.getTitle());
}
}
private class PhotoAdapter extends RecyclerView.Adapter<PhotoHolder>{
private List<GalleryItem> mGalleryItems;
public PhotoAdapter(List<GalleryItem> galleryItems){
mGalleryItems = galleryItems;
}
@NonNull
@Override
public PhotoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
TextView textView = new TextView(getActivity());
return new PhotoHolder(textView);
}
@Override
public void onBindViewHolder(@NonNull PhotoHolder photoHolder, int position) {
GalleryItem galleryItem = mGalleryItems.get(position);
photoHolder.bindGalleryItem(galleryItem);
}
@Override
public int getItemCount() {
return mGalleryItems.size();
}
}
private class FetchItemTask extends AsyncTask<Void,Void,List<GalleryItem>>{
@Override
protected List<GalleryItem> doInBackground(Void... params){
asyncFetching =true;
return new FlickrFetchr().fetchItems(pageFetched+1);
}
@Override
protected void onPostExecute(List<GalleryItem> items){
pageFetched++;
asyncFetching =false;
mItems.addAll(items);
GalleryPage pge=GalleryPage.getGalleryPage();
mMaxPage = pge.getTotalPages();
mItemsPerPage= pge.getItemPerPage();
if(mPhotoRecyclerView.getAdapter()==null)setupAdapter();
mPhotoRecyclerView.getAdapter().notifyDataSetChanged();
updatePageText(mGridManager.findFirstVisibleItemPosition());
}
}
}
fragment_photo_gallery.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/photo_recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/currentPageText"
app:layout_constraintTop_toTopOf="parent"
tools:context=".PhotoGalleryActivity">
</android.support.v7.widget.RecyclerView>
<TextView
android:id="@+id/currentPageText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:gravity="center_horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/photo_recycler_view" />
</android.support.constraint.ConstraintLayout>
FlickrFetchr.java
import android.net.Uri;
import android.util.Log;
import com.google.gson.Gson;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class FlickrFetchr {
private static final String TAG = "PhotoGallery";
private static final String API_KEY = "YOU_ACTUALL_KEY_HERE";
public byte[] getUrlBytes(String urlSpec) throws IOException {
URL url = new URL(urlSpec);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = connection.getInputStream();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException(connection.getResponseMessage() +
": with " +
urlSpec);
}
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
out.close();
return out.toByteArray();
} finally {
connection.disconnect();
}
}
public String getUrlString(String urlSpec) throws IOException {
return new String(getUrlBytes(urlSpec));
}
public List<GalleryItem> fetchItems(int page) {
List<GalleryItem> items = new ArrayList<>();
try {
String url = Uri.parse("https://api.flickr.com/services/rest/")
.buildUpon()
.appendQueryParameter("method", "flickr.photos.getRecent")
.appendQueryParameter("api_key", API_KEY)
.appendQueryParameter("format", "json")
.appendQueryParameter("nojsoncallback", "1")
.appendQueryParameter("page", Integer.toString(page))
.appendQueryParameter("extras", "url_s")
.build().toString();
String jsonString = getUrlString(url);
Log.i(TAG, "Received JSON: " + jsonString);
Gson gson = new Gson();
GalleryPage.getGalleryPage();
GalleryPage.sGalleryPage = gson.fromJson(jsonString,GalleryPage.class);
items = GalleryPage.sGalleryPage.getGalleryItemList();
} catch (IOException ioe) {
Log.e(TAG, "Failed to fetch items", ioe);
}
return items;
}
}
Anybody have any suggestions?