movie details screen
This commit is contained in:
1
README.txt
Normal file
1
README.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Api key must be added unter app/src/main/res/values/api_key.xml
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package net.headlezz.udacityproject1;
|
||||||
|
|
||||||
|
import net.headlezz.udacityproject1.tmdbapi.Movie;
|
||||||
|
|
||||||
|
public interface MovieNavigation {
|
||||||
|
|
||||||
|
void showDetailsFragment(Movie movie);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
BIN
app/src/main/res/drawable/chappie_ver4.jpg
Normal file
BIN
app/src/main/res/drawable/chappie_ver4.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
58
app/src/main/res/layout/fragment_movie_details.xml
Normal file
58
app/src/main/res/layout/fragment_movie_details.xml
Normal 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>
|
||||||
Reference in New Issue
Block a user