TheMovieDb API – Part 9 – Details awake!

Alrighty mighty! Welcome back to this series on implementing TheMovieDb API. As always remember all the code for this series will be in this GitHub repo today we are finishing up the movie details view and make it all show up:

Let’s go!

First create a MovieDetailsFragment and make it implement your previously created MovieDetailsContract interface:

public class MoviesFragment extends Fragment implements MovieItemClickListener,
        MoviesContract.View {

    public static final String TAG = "MoviesFragment";

    private ProgressBar progressBar;
    private MoviesAdapter moviesAdapter;
    private MoviesContract.UserActionsListener mActionsListener;

    public MoviesFragment() {
        // Required empty public constructor
    }

    public static MoviesFragment newInstance() {
        return new MoviesFragment();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        moviesAdapter = new MoviesAdapter(getContext(), this);
        mActionsListener = new MoviesPresenter(ApiClient.createService(MovieServiceInterface.class), this);

        if (savedInstanceState == null) {
            mActionsListener.loadMovies();
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View root = inflater.inflate(R.layout.fragment_movies, container, false);
        progressBar = (ProgressBar) root.findViewById(R.id.progress_bar);
        RecyclerView recyclerView = (RecyclerView) root.findViewById(R.id.movies_recycler_view);
        recyclerView.setAdapter(moviesAdapter);
        recyclerView.setHasFixedSize(true);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.addItemDecoration(new MovieItemDecorator(getContext()));

        LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
        recyclerView.setLayoutManager(layoutManager);

        return root;
    }

    @Override
    public void onResume() {
        if (mActionsListener != null) {
            mActionsListener.setProgressBar(false);
            mActionsListener.loadMovies();
        }
        super.onResume();
    }

    @Override
    public void onMovieActionClick(View v, int position) {
        if (v.getId() == R.id.details) {
            //show details view
            Bundle args = new Bundle();
            args.putParcelable(Movie.PARCELABLE_KEY, moviesAdapter.get(position));
            getFragmentManager().beginTransaction()
                    .addToBackStack(MoviesFragment.TAG)
                    .replace(R.id.main_container, MovieDetailsFragment.newInstance(args))
                    .commit();
        }
    }

    @Override
    public void showProgressBar(boolean active) {
        if (active)
            progressBar.setVisibility(View.VISIBLE);
        else
            progressBar.setVisibility(View.INVISIBLE);
    }

    @Override
    public void showMovieDetailUi(Bundle movieArgs) {
        //nothing for now.
    }

    @Override
    public void setMovies(List<Movie> movies) {
        moviesAdapter.addAll(movies);
    }

    @Override
    public void clearMovies() {
        moviesAdapter.clear();
    }

    @Override
    public void showMessage(String msg, int duration) {
        if (getView() != null)
            Snackbar.make(getView(), msg, duration).show();
    }
}

See how clean is our fragment? That is thanks to our interface and its simple methods. Now how about the layout? Well mine is simply enough as you can see it only shows the backdrop image, poster image, title, release year and a overview of the movie, but hey does it look awesome!

<ScrollView 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="wrap_content"
    tools:context="com.jrosa.moviezone.details.MovieDetailsFragment">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/movie_backdrop_image_view"
            android:layout_width="match_parent"
            android:layout_height="250dp"
            android:contentDescription="@string/content.desc.movie.details.backdrop.image"
            android:scaleType="centerCrop"
            tools:background="@drawable/test_movie_poster2" />

        <android.support.v7.widget.CardView
            android:layout_width="90dp"
            android:layout_height="120dp"
            android:layout_marginLeft="18dp"
            android:layout_marginRight="18dp"
            android:layout_marginTop="100dp"
            app:cardCornerRadius="4dp"
            app:cardElevation="8dp">

            <ImageView
                android:id="@+id/movie_poster_image_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:contentDescription="@string/content.desc.movie.details.poster.image"
                android:scaleType="centerCrop"
                tools:background="@drawable/test_movie2" />

        </android.support.v7.widget.CardView>

        <TextView
            android:id="@+id/title"
            style="@style/TextAppearance.AppCompat.Title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/movie_backdrop_image_view"
            android:layout_marginTop="16dp"
            android:gravity="center"
            android:textColor="@color/colorAccent"
            tools:text="Star Wars - The force awakens" />


        <TextView
            android:id="@+id/release_date"
            style="@style/TextAppearance.AppCompat.Caption"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/title"
            android:gravity="center"
            tools:text="2015" />

        <android.support.v7.widget.AppCompatRatingBar
            android:id="@+id/popularity"
            style="?ratingBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/release_date"
            android:layout_centerHorizontal="true"
            android:isIndicator="true"
            android:numStars="10"
            tools:rating="8" />

        <TextView
            android:id="@+id/overview"
            style="@style/TextAppearance.AppCompat.Small"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/popularity"
            android:fontFamily="sans-serif-condensed"
            android:padding="16dp"
            tools:text="@string/movie.details.overview.base" />


    </RelativeLayout>
</ScrollView>

As you can observe, only a few components were used, the scrollview in my case is if the overview text is too long then users can swipe down to continue reading.

Finally let’s make the MoviesFragment show this awesome view! Go the current onMovieActionClick interface implementation that you previously placed and edit it to the following:

@Override
public void onMovieActionClick(View v, int position) {
    if (v.getId() == R.id.details) {
        //show details view
        Bundle args = new Bundle();
        args.putParcelable(Movie.PARCELABLE_KEY, moviesAdapter.get(position));
        getFragmentManager().beginTransaction()
                .addToBackStack(MoviesFragment.TAG)
                .replace(R.id.main_container, MovieDetailsFragment.newInstance(args))
                .commit();
    }
}

Notice how simple it is to pass a serialized version of our movie using the putParcelable() method. Remember also to .addToBackStack(fragmentName/Tag) the current fragment so we can have backwards navigation. Run it and delight with the result!

Final movie details
Final movie details

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Next time we are visiting the Reviews action button, stay tuned we are almost over!

Joel

TheMovieDb API – Part 4 – The Movie Presenter

Welcome back to this series on implementing TheMovieDb API. As always remember all the code for this series will be in this GitHub repo today we are going to setup and test our Movie Presenter, remember the MVP pattern? Awesome so let’s get right to it!

First let’s define what we want to do with a simple interface. We need to be able from the View to: show the progress bar, show details of the movie later on, set the movie lists to our adapter, clear movies out and show messages. So it should look something like:

public interface MoviesContract {
    /**
     * Represents the methods that our view (Activity or Fragment)
     * will implement.
     */
    interface View {

        void showProgressBar(boolean active);

        void showMovieDetailUi(Bundle movieArgs);

        void setMovies(List<Movie> movies);

        void clearMovies();

        void showMessage(String msg, int duration);
    }
...

Continue reading “TheMovieDb API – Part 4 – The Movie Presenter”