movie details screen

This commit is contained in:
danijoo
2015-10-29 10:11:12 +01:00
parent c1580748d0
commit 80afe2944a
11 changed files with 250 additions and 15 deletions

1
README.txt Normal file
View File

@@ -0,0 +1 @@
Api key must be added unter app/src/main/res/values/api_key.xml

View File

@@ -1,10 +1,16 @@
package net.headlezz.udacityproject1; package net.headlezz.udacityproject1;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
public class MainActivity extends AppCompatActivity { import net.headlezz.udacityproject1.tmdbapi.Movie;
/**
* Main activity is mainly for handling fragment transactions
*/
public class MainActivity extends AppCompatActivity implements MovieNavigation, FragmentManager.OnBackStackChangedListener {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -15,11 +21,41 @@ public class MainActivity extends AppCompatActivity {
setTitle(getString(R.string.app_name)); setTitle(getString(R.string.app_name));
if(savedInstanceState == null) { if(savedInstanceState == null) {
getSupportFragmentManager() getSupportFragmentManager().beginTransaction()
.beginTransaction()
.add(R.id.fragmentHolder, new MovieListFragment()) .add(R.id.fragmentHolder, new MovieListFragment())
.commit(); .commit();
} }
// activity needs to listen to fragment changes to adjust the home as up arrow
getSupportFragmentManager().addOnBackStackChangedListener(this);
shouldDisplayHomeUp();
} }
/**
* Replaces the list with a details view showing the selected movie
* @param movie Movie to show
*/
public void showDetailsFragment(Movie movie) {
MovieDetailsFragment frag = MovieDetailsFragment.newInstance(movie);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragmentHolder, frag)
.addToBackStack(MovieDetailsFragment.TAG)
.commit();
}
public void shouldDisplayHomeUp(){
boolean hasBackstack = getSupportFragmentManager().getBackStackEntryCount()>0;
getSupportActionBar().setDisplayHomeAsUpEnabled(hasBackstack);
}
@Override
public boolean onSupportNavigateUp() {
getSupportFragmentManager().popBackStack();
return true;
}
@Override
public void onBackStackChanged() {
shouldDisplayHomeUp();
}
} }

View File

@@ -0,0 +1,75 @@
package net.headlezz.udacityproject1;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import net.headlezz.udacityproject1.tmdbapi.Movie;
import net.headlezz.udacityproject1.tmdbapi.TMDBApi;
/**
* Fragment to show the details of a movie
*/
public class MovieDetailsFragment extends Fragment {
// TODO make me pretty
public static final String TAG = MovieDetailsFragment.class.getSimpleName();
public static final String BUNDLE_ARG_MOVIE = "movie";
ImageView ivPoster;
TextView tvTitle;
TextView tvOverview;
TextView tvReleaseDate;
TextView tvRating;
Movie mMovie;
public static MovieDetailsFragment newInstance(Movie movie) {
MovieDetailsFragment frag = new MovieDetailsFragment();
Bundle args = new Bundle();
args.putParcelable(BUNDLE_ARG_MOVIE, movie);
frag.setArguments(args);
return frag;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getArguments().containsKey(BUNDLE_ARG_MOVIE)) {
mMovie = getArguments().getParcelable(BUNDLE_ARG_MOVIE);
Log.d(TAG, "Bundled movie: " + mMovie.getTitle());
} else
throw new RuntimeException(TAG + " opened without bundled movie!");
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_movie_details, container, false);
ivPoster = (ImageView) view.findViewById(R.id.movie_details_ivPoster);
tvTitle = (TextView) view.findViewById(R.id.movie_details_tvTitle);
tvOverview = (TextView) view.findViewById(R.id.movie_details_tvOverview);
tvReleaseDate = (TextView) view.findViewById(R.id.movie_details_tvReleaseDate);
tvRating = (TextView) view.findViewById(R.id.movie_details_tvRating);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
TMDBApi.loadPoster(ivPoster, mMovie);
tvTitle.setText(mMovie.getTitle());
tvOverview.setText(mMovie.getOverview());
String formatedDate = DateUtils.formatDateTime(tvReleaseDate.getContext(), mMovie.getReleaseDate(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_YEAR);
tvReleaseDate.setText(formatedDate);
tvRating.setText(String.valueOf(mMovie.getAvRating()));
}
}

View File

@@ -1,12 +1,10 @@
package net.headlezz.udacityproject1; package net.headlezz.udacityproject1;
import android.content.Context;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.Toast;
import net.headlezz.udacityproject1.tmdbapi.Movie; import net.headlezz.udacityproject1.tmdbapi.Movie;
import net.headlezz.udacityproject1.tmdbapi.TMDBApi; import net.headlezz.udacityproject1.tmdbapi.TMDBApi;
@@ -15,11 +13,11 @@ import java.util.List;
public class MovieListAdapter extends RecyclerView.Adapter<MovieListAdapter.MovieViewHolder>{ public class MovieListAdapter extends RecyclerView.Adapter<MovieListAdapter.MovieViewHolder>{
Context context; MovieNavigation mMovieNavigation;
List<Movie> mMovies; List<Movie> mMovies;
public MovieListAdapter(Context context, List<Movie> movies) { public MovieListAdapter(MovieNavigation mn, List<Movie> movies) {
this.context = context; mMovieNavigation = mn;
mMovies = movies; mMovies = movies;
} }
@@ -53,7 +51,7 @@ public class MovieListAdapter extends RecyclerView.Adapter<MovieListAdapter.Movi
ivPoster.setOnClickListener(new View.OnClickListener() { ivPoster.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Toast.makeText(v.getContext(), movie.getTitle() + " clicked.", Toast.LENGTH_SHORT).show(); mMovieNavigation.showDetailsFragment(movie);
} }
}); });
} }

View File

@@ -1,5 +1,6 @@
package net.headlezz.udacityproject1; package net.headlezz.udacityproject1;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.GridLayoutManager;
@@ -28,13 +29,14 @@ import retrofit.Call;
public class MovieListFragment extends Fragment { public class MovieListFragment extends Fragment {
// TODO fix restore // TODO fix restore
// TODO show progressbar while loading
public static final String TAG = MovieListFragment.class.getSimpleName(); public static final String TAG = MovieListFragment.class.getSimpleName();
/** /**
* The number of columns our movie grid shows * The number of columns our movie grid shows
*/ */
private static final int NUM_COLUMNS = 2; private static final int NUM_COLUMNS = 3; // TODO adjust colum count for landscape
/** /**
* The currently selected sorting order of the list * The currently selected sorting order of the list
@@ -120,7 +122,7 @@ public class MovieListFragment extends Fragment {
} }
private void setListToGrid(List<Movie> movies) { private void setListToGrid(List<Movie> movies) {
mMovieGridView.setAdapter(new MovieListAdapter(getActivity(), movies)); mMovieGridView.setAdapter(new MovieListAdapter((MovieNavigation) getActivity(), movies));
} }
/** /**
@@ -138,4 +140,11 @@ public class MovieListFragment extends Fragment {
super.onStop(); super.onStop();
stopLoadingMovies(); stopLoadingMovies();
} }
@Override
public void onAttach(Context context) {
super.onAttach(context);
if(!(context instanceof MovieNavigation))
throw new RuntimeException("Activity must implement " + MovieNavigation.class.getSimpleName());
}
} }

View File

@@ -0,0 +1,9 @@
package net.headlezz.udacityproject1;
import net.headlezz.udacityproject1.tmdbapi.Movie;
public interface MovieNavigation {
void showDetailsFragment(Movie movie);
}

View File

@@ -1,15 +1,22 @@
package net.headlezz.udacityproject1.tmdbapi; package net.headlezz.udacityproject1.tmdbapi;
import android.os.Parcel;
import android.os.Parcelable;
/** /**
* Data object to represent a single movie * Data object to represent a single movie
* Only required data from the api is stored to save memory * Only required data from the api is stored to save memory
*/ */
public class Movie { public class Movie implements Parcelable {
// empty constructor for gson/retrofit
public Movie() {}
String title; String title;
String posterPath; String posterPath;
String overview; String overview;
double avRating; double avRating;
long releaseDate;
public String getTitle() { public String getTitle() {
return title; return title;
@@ -26,4 +33,44 @@ public class Movie {
public double getAvRating() { public double getAvRating() {
return avRating; return avRating;
} }
public long getReleaseDate() {
return releaseDate;
}
/**** Parcelable stuff *****/
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(title);
dest.writeString(posterPath);
dest.writeString(overview);
dest.writeDouble(avRating);
dest.writeLong(releaseDate);
}
public static final Parcelable.Creator<Movie> CREATOR = new Parcelable.Creator<Movie>() {
public Movie createFromParcel(Parcel src) {
return new Movie(src);
}
public Movie[] newArray(int size) {
return new Movie[size];
}
};
private Movie(Parcel src) {
title = src.readString();
posterPath = src.readString();
overview = src.readString();
avRating = src.readDouble();
releaseDate = src.readLong();
}
} }

View File

@@ -9,6 +9,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -17,6 +18,7 @@ public class MovieListDeserializer implements JsonDeserializer<MovieList> {
@Override @Override
public MovieList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { public MovieList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
List<Movie> movieList = new ArrayList<>(); List<Movie> movieList = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-DD");
for(JsonElement elem : json.getAsJsonObject().getAsJsonArray("results")) { for(JsonElement elem : json.getAsJsonObject().getAsJsonArray("results")) {
try { // TODO fix this and remove try/catch try { // TODO fix this and remove try/catch
JsonObject jsonObj = elem.getAsJsonObject(); JsonObject jsonObj = elem.getAsJsonObject();
@@ -24,8 +26,8 @@ public class MovieListDeserializer implements JsonDeserializer<MovieList> {
m.title = jsonObj.get("title").getAsString(); m.title = jsonObj.get("title").getAsString();
m.overview = jsonObj.get("overview").getAsString(); m.overview = jsonObj.get("overview").getAsString();
m.avRating = jsonObj.get("vote_average").getAsDouble(); m.avRating = jsonObj.get("vote_average").getAsDouble();
JsonElement posterPath = jsonObj.get("poster_path"); m.posterPath = jsonObj.get("poster_path").getAsString();
m.posterPath = posterPath.getAsString(); m.releaseDate = sdf.parse(jsonObj.get("release_date").getAsString()).getTime();
movieList.add(m); movieList.add(m);
} catch (Exception e) { } catch (Exception e) {
Log.e(MovieListDeserializer.class.getSimpleName(), e.toString()); Log.e(MovieListDeserializer.class.getSimpleName(), e.toString());

View File

@@ -20,7 +20,7 @@ import retrofit.http.Query;
public class TMDBApi { public class TMDBApi {
private static final String BASE_URL = "http://api.themoviedb.org/3/"; private static final String BASE_URL = "http://api.themoviedb.org/3/";
private static final String IMAGE_BASE_URL = "http://image.tmdb.org/t/p/w342"; // TODO select size depending on device screen ratios private static final String IMAGE_BASE_URL = "http://image.tmdb.org/t/p/w185"; // TODO select size depending on device screen ratios
/** /**
* These are the possible sorting orders * These are the possible sorting orders

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp">
<TextView
android:id="@+id/movie_details_tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
tools:text="Chappie" />
<ImageView
android:id="@+id/movie_details_ivPoster"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/movie_details_tvTitle"
android:layout_marginTop="16dp"
android:adjustViewBounds="true"
tools:src="@drawable/chappie_ver4" />
<TextView
android:textAppearance="?android:attr/textAppearanceLarge"
tools:text="2015"
android:id="@+id/movie_details_tvReleaseDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/movie_details_ivPoster"
android:layout_centerHorizontal="true" />
<TextView
android:textAppearance="?android:attr/textAppearanceLarge"
tools:text="5/10"
android:id="@+id/movie_details_tvRating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/movie_details_tvReleaseDate"
android:layout_alignEnd="@+id/movie_details_tvReleaseDate" />
<TextView
android:id="@+id/movie_details_tvOverview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/movie_details_ivPoster"
android:layout_marginTop="16dp"
android:textAppearance="?android:attr/textAppearanceMedium"
tools:text="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." />
</RelativeLayout>
</ScrollView>