In this article, we are going to look at Android Parcelable example by passing objects via bundle between activities and fragments. Although there are no prerequisites for this tutorial, here I am going to take the Movie Board project that we’ve been building in our previous posts and implement Parcelable to it and pass the objects via bundle.
The completed project can be downloaded from the following github repository link:
So lets dive into the required steps to complete the Android Parcelable Example.
1. Making our Model Class implement Android Parcelable:
[widget id=”text-15″]
Our previous project of Movie Board consists of a Model class named Movie.java which consists of the following code previously:
package com.coderefer.firebasedatabaseexample.models; /** * Created by vamsi tallapudi on 18-Jul-16 for Android Parcelable Eg. */ public class Movie { public String movieName; public String moviePoster; public float movieRating; public Movie(){ } public Movie(String movieName,String moviePoster,float movieRating){ this.movieName = movieName; this.moviePoster = moviePoster; this.movieRating = movieRating; } public String getMovieName() { return movieName; } public void setMovieName(String movieName) { this.movieName = movieName; } public String getMoviePoster() { return moviePoster; } public void setMoviePoster(String moviePoster) { this.moviePoster = moviePoster; } public float getMovieRating() { return movieRating; } public void setMovieRating(float movieRating) { this.movieRating = movieRating; } }
Now lets implement Android Parcelable class to the above class so that we can pass the Object for the Movie class via Bundle between activities/ fragments.
[widget id=”text-3″]
The above class after implementing Android parcelable will be as follows:
package com.coderefer.firebasedatabaseexample.models; import android.os.Parcel; import android.os.Parcelable; /** * Created by vamsi on 18-Jul-16 for Android Parcelable Eg. */ //implementation of Android Parcelable public class Movie implements Parcelable{ public String movieName; public String moviePoster; public float movieRating; public Movie(){ } public Movie(String movieName,String moviePoster,float movieRating){ this.movieName = movieName; this.moviePoster = moviePoster; this.movieRating = movieRating; } protected Movie(Parcel in) { movieName = in.readString(); moviePoster = in.readString(); movieRating = in.readFloat(); } public static final Creator<Movie> CREATOR = new Creator<Movie>() { @Override public Movie createFromParcel(Parcel in) { return new Movie(in); } @Override public Movie[] newArray(int size) { return new Movie[size]; } }; public String getMovieName() { return movieName; } public void setMovieName(String movieName) { this.movieName = movieName; } public String getMoviePoster() { return moviePoster; } public void setMoviePoster(String moviePoster) { this.moviePoster = moviePoster; } public float getMovieRating() { return movieRating; } public void setMovieRating(float movieRating) { this.movieRating = movieRating; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(movieName); dest.writeString(moviePoster); dest.writeFloat(movieRating); } }
2. Creating our second screen:
Now lets create our second screen for which we will pass our data into.
[widget id=”text-3″]
First, I am going to create a layout using our previous example on Android Collapsing Toolbar Layout. I am going to name the required layout files as content_collapsible_toolbar.xml and movie_detail_fragment.xml and write the following codes to them.
content_collapsible_toolbar.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="?android:windowBackground"> <TextView android:id="@+id/tv_detail_heading" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/com_facebook_likeboxcountview_caret_width" android:gravity="center" android:textColor="@android:color/holo_red_light" android:textColorLink="@color/colorPrimary" android:textSize="24sp" android:textStyle="bold" tools:text="Sample text Heading" /> <RatingBar android:id="@+id/rating_detail" android:layout_marginTop="150dp" android:layout_width="wrap_content" android:layout_gravity="center_horizontal" android:layout_height="wrap_content" android:numStars="5" tools:rating="2.5" android:isIndicator="true" /> </LinearLayout>
movie_detail_fragment.xml:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout 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/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context="com.coderefer.firebasedatabaseexample.MainActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/app_bar_layout" android:fitsSystemWindows="true" app:elevation="0dp" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.design.widget.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/collapsing_toolbar" android:fitsSystemWindows="true" app:contentScrim="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways"> <ImageView android:layout_width="match_parent" android:layout_height="350dp" android:scaleType="centerCrop" android:fitsSystemWindows="true" android:id="@+id/iv_detail" android:src="@drawable/placeholder" app:layout_collapseMode="parallax"/> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin"/> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <include layout="@layout/content_collapsible_toolbar"/> </android.support.v4.widget.NestedScrollView> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" app:layout_anchor="@id/app_bar_layout" app:layout_anchorGravity="bottom|right|end" app:srcCompat="@android:drawable/ic_menu_share"/> </android.support.design.widget.CoordinatorLayout>
Lets wire the above layout in java code by using a class extending Fragment, named MovieDetailFragment.java and write the following code:
package com.coderefer.firebasedatabaseexample.fragments; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.RatingBar; import android.widget.TextView; import com.coderefer.firebasedatabaseexample.MainActivity; import com.coderefer.firebasedatabaseexample.R; import com.coderefer.firebasedatabaseexample.models.Movie; import com.squareup.picasso.Picasso; /** * Created by vamsitallapudi on 5/22/17 for Android Parcelable Eg. */ public class MovieDetailFragment extends Fragment { private Movie movie; private ImageView ivDetailBanner; private TextView tvDetailHeading; private RatingBar mRatingBar; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.movie_detail_fragment, container, false); if (getArguments() != null) { movie = getArguments().getParcelable(MainActivity.MOVIE_KEY); } if (movie != null) { initializeViews(view); Picasso.with(getActivity()).load(movie.getMoviePoster()).into(ivDetailBanner); tvDetailHeading.setText(movie.getMovieName()); mRatingBar.setRating(movie.getMovieRating()); } return view; } private void initializeViews(View view) { ivDetailBanner = (ImageView) view.findViewById(R.id.iv_detail); tvDetailHeading = (TextView) view.findViewById(R.id.tv_detail_heading); mRatingBar = (RatingBar) view.findViewById(R.id.rating_detail); } }
3. Implementing Listener to go to second screen:
The only thing remaining is to implement a touch listener to our recyclerview so that we can navigate to second screen along with the bundle which consists Movie Object of the recycler item which was clicked.
[widget id=”text-15″]
Lets call the method addOnItemTouchListener of our RecyclerView in our MainActivity.java.
mRecyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this, new RecyclerItemClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, "Card at " + position + " is clicked", Toast.LENGTH_SHORT).show(); Movie movie = adapter.getItem(position); //creating a bundle in order to pass data to other component Bundle bundle = new Bundle(); bundle.putParcelable(MOVIE_KEY,movie); MovieDetailFragment fragment = new MovieDetailFragment(); fragment.setArguments(bundle);//passing data to fragment getSupportFragmentManager().beginTransaction() .replace(R.id.frame_container, fragment) .addToBackStack(null) .commit(); } }));
Notice how bundle is created and is passed from activity to fragment. Now my Complete Activity will be as follows:
MainActivity.java:
[widget id=”text-3″]
package com.coderefer.firebasedatabaseexample; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.View; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; import android.widget.ImageView; import android.widget.RatingBar; import android.widget.TextView; import android.widget.Toast; import com.coderefer.firebasedatabaseexample.fragments.AddMovieFragment; import com.coderefer.firebasedatabaseexample.fragments.MovieDetailFragment; import com.coderefer.firebasedatabaseexample.models.Movie; import com.coderefer.firebasedatabaseexample.util.RecyclerItemClickListener; import com.firebase.ui.database.FirebaseRecyclerAdapter; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.squareup.picasso.Picasso; public class MainActivity extends AppCompatActivity { private FloatingActionButton fab; ScaleAnimation shrinkAnim; private RecyclerView mRecyclerView; private StaggeredGridLayoutManager mLayoutManager; private TextView tvNoMovies; //Getting reference to Firebase Database FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference mDatabaseReference = database.getReference(); private static final String USER_ID = "53"; public static final String MOVIE_KEY = "movie_key"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Initializing our Recyclerview mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view); tvNoMovies = (TextView) findViewById(R.id.tv_no_movies); //scale animation to shrink floating actionbar shrinkAnim = new ScaleAnimation(1.15f, 0f, 1.15f, 0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); if (mRecyclerView != null) { //to enable optimization of recyclerview mRecyclerView.setHasFixedSize(true); } //using staggered grid pattern in recyclerview mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(mLayoutManager); //Say Hello to our new Firebase UI Element, i.e., FirebaseRecyclerAdapter final FirebaseRecyclerAdapter<Movie,MovieViewHolder> adapter = new FirebaseRecyclerAdapter<Movie, MovieViewHolder>( Movie.class, R.layout.movie_board_item, MovieViewHolder.class, //referencing the node where we want the database to store the data from our Object mDatabaseReference.child("users").child(USER_ID).child("movies").getRef() ) { @Override protected void populateViewHolder(MovieViewHolder viewHolder, Movie model, int position) { if(tvNoMovies.getVisibility()== View.VISIBLE){ tvNoMovies.setVisibility(View.GONE); } viewHolder.tvMovieName.setText(model.getMovieName()); viewHolder.ratingBar.setRating(model.getMovieRating()); Picasso.with(MainActivity.this).load(model.getMoviePoster()).into(viewHolder.ivMoviePoster); } }; mRecyclerView.setAdapter(adapter); mRecyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this, new RecyclerItemClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, "Card at " + position + " is clicked", Toast.LENGTH_SHORT).show(); Movie movie = adapter.getItem(position); //creating a bundle in order to pass data to other component using Android Parcelable. Bundle bundle = new Bundle(); bundle.putParcelable(MOVIE_KEY,movie); MovieDetailFragment fragment = new MovieDetailFragment(); fragment.setArguments(bundle);//passing data to fragment getSupportFragmentManager().beginTransaction() .replace(R.id.frame_container, fragment) .addToBackStack(null) .commit(); } })); fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { getSupportFragmentManager().beginTransaction() .replace(R.id.frame_container, new AddMovieFragment()) .addToBackStack(null) .commit(); //animation being used to make floating actionbar disappear shrinkAnim.setDuration(400); fab.setAnimation(shrinkAnim); shrinkAnim.start(); shrinkAnim.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { //changing floating actionbar visibility to gone on animation end fab.setVisibility(View.GONE); } @Override public void onAnimationRepeat(Animation animation) { } }); } }); } @Override public void onBackPressed() { super.onBackPressed(); if (fab.getVisibility() == View.GONE) fab.setVisibility(View.VISIBLE); } public static class MovieViewHolder extends RecyclerView.ViewHolder{ TextView tvMovieName; RatingBar ratingBar; ImageView ivMoviePoster; public MovieViewHolder(View v) { super(v); tvMovieName = (TextView) v.findViewById(R.id.tv_name); ratingBar = (RatingBar) v.findViewById(R.id.rating_bar); ivMoviePoster = (ImageView) v.findViewById(R.id.iv_movie_poster); } } }
Run the app now to see that the object is passed from Activity to Fragment using Android Parcelable!
[widget id=”text-15″]