If you build mobile applications for the Android platform then you are no doubt already familiar with Android Studio, a popular tool and the official Integrated Development Environment (IDE) for Android app development.
Equally, you’ve likely heard of Github and Git. Git is a free and open-source, distributed version control system that lets you manage and track changes in files used in programming. GitHub, is one of many cloud-based hosting services that allows you to manage your Git repositories. Github enables collaborative project development, as multiple programmers can work on project code together. Developers can track and control code changes made by others and, if necessary, return to a state of code before any given change was made, making it an extremely helpful tool.
Android Studio has a user interface that makes it incredibly easy to work with Git without any additional software. In this article, we will show how to clone a repository and work with it using Android Studio and basic Git commands.
Make sure you have a Github account and Android Studio installed and then read on to discover the best way to work with these tools.
Glossary
Before we get started, here are some useful terms worth understanding:
Branch – Branches allow you to work on new features, fix bugs, or develop new ideas in an isolated space within your repository. Branches are needed to isolate development work, so that other branches in the repository are not affected. The existence of branches means that developers can work together on a project and not interfere with each other’s work.
Commit – A commit is like a snapshot (copy) of your repository at specific times. Commits include metadata in addition to the contents and message, like the author, timestamp, and more. The commit history allows you to track changes and if a mistake was made, you can easily find and return to the commit where it happened.
Repository – A repository is a central location where data for your project is stored and managed. A repository contains all of your project’s files and tracks and saves a history of all changes made to files in a project.
The local repository exists on the local machine and can be seen only by the local user.
The remote repository is stored in the cloud, and its local copies are located on the computers of developers. When a developer makes changes to the local version, it can be synchronized with the remote one.
Step 1: Cloning a repository
First you need to open Android Studio.
Press the More Actions command (3-dot icon) on the top row.
Select from the drop-down list “Get from Version Control.”
Then you need to copy the repository URL that you wish to use from Github.
Go to GitHub and select the project you want to clone and copy the repository URL. For example GitHub – QuickBlox Android Samples. QuickBlox code samples for Android provide an efficient way to add chat or video calling functionality to your application, saving you time from having to code from scratch.
Press the “Code” button.
Press the Copy URL icon button.
Now return to Android Studio to clone the repository.
Insert the URL of the repository that you want to clone.
Select the directory where the repository will be cloned.
Press the “Clone” button.
Step 2. Create A New Branch
Now you need to create a new branch where you will work on the code. This is a copy of your workspace in which you can fully work and make changes without affecting the code in the branch from which the copy was made. This allows you to isolate the workflow and not interfere with other developers working on this project.
Press “Git.”
Select from the drop-down list “Branches.”
This window contains a list of all local and remote branches.
Press “New Branch”
Enter the name of the new branch.
Press the “Create” button.
After that, Android Studio will automatically switch to the new branch.
Step 3. Switch branches
To switch between branches you need to go to the window with the list of branches.
Press the “Git” button.
Select from the drop-down list “Branches.”
Select a branch from the list and click on it.
Select “Checkout” from the drop-down list.
This allows you to change the workspace to the branch of your choice.
Step 4. The Commit Command
This is the command to write indexed changes to a Git repository. It saves (writes) states of the branch on the Git project timeline. The “Commit” command should be used after changing something in the project. For example, we have added a new file with the name “Example.java”, and after that, we can save changes to the local repository branch with the commit command.
Press the Commit command (green tick icon) on the toolbar.
Provide a brief summary of the changes that were added to this commit.
Press the “Commit” button.
Step 5. The Push Command
The “Push” command will send the local repository branch to the remote repository. It should be used after changes have been committed to a local repository branch. The “push” command can send one or more commits.
Storing data in a remote repository allows you to access content from any computer and other developers who are working on the project can download and apply the actual code to their local repository and stay in sync with the team.
Press the Push command (green arrow icon) on the toolbar.
Press the “Push” button.
Step 6. The Pull Command
The “Pull” command is used to retrieve and download content from a remote repository branch and update the local repository branch with that content. This command can be used, for example, if another developer has made changes to the branch code and pushed the changes to a remote repository. For you to have the actual code locally, you need to synchronize your local repository branch with the remote repository branch. After the “Pull” command, the local repository branch will be the same as on the remote.
Press the Update Project command (blue arrow icon) on the toolbar.
Select “Merge incoming changes into the current branch.”
Press the “OK” button.
Step 7. Using the Log
You can see the history of commits by pressing on the Git in the lower left-hand corner. Here you can find when and by whom the commit was made and what changes were made.
Conclusion
Android Studio has native integration with Git and GitHub to allow most actions via the Android Studio UI. This makes it much easier to track and manage your Git work, which saves development time. We hope that this article will help you learn how to use basic Git commands with Android Studio so that you can clone a repository and work easily with Git. Happy coding!
This article is an implementation of an intro showcase to highlights different features of the app using Jetpack Compose. The implementation is inspired by the TapTargetView which is useful for legacy views.
The implementation is also available as a standalone library Intro-showcase-view on github which you can directly add as a gradle dependency in your project.
I have divided the implementation into byte size steps. Feel free to skip the steps that you understand or jump directly to the step you are interested in. The final implementation will look like above GIF.
Alright, let’s start coding!!
Step 1: Add a feature and draw circles on Canvas
First, let’s add an email fab button aligned to the bottom of the screen.
Then, we’ll create two circles. We’ll use Canvas API to draw our circle.
@Composable fun IntroShowCase() { Canvas( modifier = Modifier .fillMaxSize() ) { drawCircle( color = Color.Black ) drawCircle( color = Color.White, radius = 150f ) } }
Pretty Simple…
But, that’s not what we want.
Step 2: Find LayoutCoordinates of a fab button and recenter circle.
We have to set a circle offset to highlight our Fab button. And for that, we need a position of our fab button. We’ll use OnGloballyPositionedModifier property of Modifier we’ll have view’s LayoutCoordinates in a callback.
A modifier whose onGlobalyPositioned is called with the final LayoutCoordinates of the Layout when the global position of the content may have changed. Note that it will be called after a composition when the coordinates are finalized.
Let’s modify the modifier of fab button to get its coordinates.
@Composable fun showcaseExample() {
var target by remember { mutableStateOf<LayoutCoordinates?>(null) } val context = LocalContext.current
Now let’s use this coordinate to recenter our circle. Here’s how,
@Composable fun IntroShowCase(targetCords: LayoutCoordinates) { val targetRect = targetCords.boundsInRoot()
Canvas( modifier = Modifier .fillMaxSize() ) { drawCircle( color = Color.Black, center = targetRect.center ) drawCircle( color = Color.White, radius = 150f, center = targetRect.center ) } }
We have used our target view Rect’s center offset to center our circle.
Oops!!, Where’s our fab button?. Our fab button is overlapped by circles.
Step 3: Blend the overlapped circle to set transparency
We have to set transparency where both circles are overlapped. We’ll use BlendMode to fix this and also let’s fix the radius of the inner circle based on our target view dimension.
BlendMode.Clear : Drop both the source and destination images, leaving nothing.
@Composable fun IntroShowCase(targetCords: LayoutCoordinates) { val targetRect = targetCords.boundsInRoot() val targetRadius = targetRect.maxDimension / 2f + 40f // 40f extra traget spacing
Canvas( modifier = Modifier .fillMaxSize().graphicsLayer (alpha = 0.99f) ) { drawCircle( color = Color.Black, center = targetRect.center ) drawCircle( color = Color.White, radius = targetRadius, center = targetRect.center, blendMode = BlendMode.Clear ) } }
Perfect!! Isn’t it?
Step: 4 Add circle reveals animation to highlight target
Now, Let’s add an animation around our target view
dys.forEach { dy -> drawCircle( color = Color.White, radius = targetRect.maxDimension * dy * 2f, center = targetRect.center, alpha = 1 - dy ) } ... }
Let’s try to understand this, We have used infiniteRepeatable , as we want our animation to run infinitely.
animatables is the array of Animatable, We have set up the initial delay for the second wave, We can not use delayMillis as that is considered for repeat animation as well. We just want to delay the initial animation, and then continue the loop without any delay.
we created an animation that will animate between 0 to 1 infinitely. Based on that, we have set up a radius and animated alpha between 1 to 0, which will make waves disappear at the end of an animation.
Let’s see what it look likes,
Step 5: Add texts to describe the feature
Let’s create a data class that holds the value of our targets coordinates, title, subtitle, colors, etc.
data class ShowcaseProperty( val index: Int, val coordinates: LayoutCoordinates, val title: String, val subTitle: String, val titleColor: Color = Color.White, val subTitleColor: Color = Color.White, )
Okay, for now, let’s ignore index field, we’ll use it later to manage the order of when we have multiple features. Let’s refactor our composable a bit.
@Composable fun IntroShowCase(target: ShowcaseProperty) { val targetRect = target.coordinates.boundsInRoot() val targetRadius = targetRect.maxDimension / 2f + 40f
We just have added two Text for title and subtitle, let’s see the output.
But that’s not even near to our circles.
Step 6: Set offset of Texts
Here we need to check the top and bottom space to set up our text in free space. So we’re going to do it in onGloballyPositionedwe’ll calculate the Y offset of our Text, based on total text height and the center of our target. Here’s how.
@Composable fun IntroShowCaseEx(target: ShowcaseProperty){ .... val targetRect = target.coordinates.boundsInRoot() val targetRadius = targetRect.maxDimension / 2f + 40f Box { ShowCaseText(target, targetRect, targetRadius) } .... } @Composable private fun ShowCaseText( currentTarget: ShowcaseProperty, targetRect: Rect, targetRadius: Float ) {
We have to estimate the rectangle which includes our text, and our target view including its spacing.
fun getOuterRadius(textRect: Rect, targetRect: Rect): Float {
val topLeftX = min(textRect.topLeft.x, targetRect.topLeft.x) val topLeftY = min(textRect.topLeft.y, targetRect.topLeft.y) val bottomRightX = max(textRect.bottomRight.x, targetRect.bottomRight.x) val bottomRightY = max(textRect.bottomRight.y, targetRect.bottomRight.y)
val expandedBounds = Rect(topLeftX, topLeftY, bottomRightX, bottomRightY)
val d = sqrt( expandedBounds.height.toDouble().pow(2.0) + expandedBounds.width.toDouble().pow(2.0) ).toFloat()
return (d / 2f) }
Okay, we just have found the rectangle of our content, and from that, we got the radius.
var textCoordinate: LayoutCoordinates? by remember { mutableStateOf(null) } var outerRadius by remember { mutableStateOf(0f) } textCoordinate?.let { textCoords -> val textRect = textCoords.boundsInRoot() outerRadius = getOuterRadius(textRect, targetRect) + targetRadius }Box { Canvas( modifier = Modifier .fillMaxSize() .graphicsLayer(alpha = 0.99f) ) { drawCircle( color = Color.Black, center = targetRect.center, radius = outerRadius, alpha = 0.9f )
dys.forEach { dy -> drawCircle( color = Color.White, radius = targetRect.maxDimension * dy * 2f, center = targetRect.center, alpha = 1 - dy ) }
drawCircle( color = Color.White, radius = targetRadius, center = targetRect.center, blendMode = BlendMode.Clear ) }
Nope, That’s not enough to cover the whole content.
Step 8: change the offset of our outer circle
Now, let’s find the center offset of the outer circle which includes our target and texts.
var outerOffset by remember { mutableStateOf(Offset(0f, 0f)) }textCoordinate?.let { textCoords -> val textRect = textCoords.boundsInRoot() val textHeight = textCoords.size.height
if (isInGutter) { outerCenterY = targetBound.center.y }
return Offset(outerCenterX, outerCenterY) }
If our target is in Gutter we just set targetBound.center.ytoouterCenterY and our outerCenterX would be the same as the center X of our content rectangle in both cases.
Let’s check the output now.
Perfect!!
And last but not least…
Step 10: Add circle reveals animation to our outer circle.
val outerAnimatable = remember { Animatable(0.6f) }
We have created Animatablewith initial value 0.6 as we don’t want our circle to scale from 0.0. If you notice, we have used target here as a key of LaunchedEffect , this will only trigger the inner block when a key changes. Whenever key changes we have reset the current value to the initial value 0.6f using snapTo. Let’s use Animatable value with our outer circle radius.
Now let’s integrate it with multiple feature showcases. We’re not going to cover all the basic details.onGloballyPositioned may call multiple times so we’ll use an mutableStateMapOf of ShowcaseProperty to avoid duplications.
@Composable fun ShowcaseSample() { val targets = remember { mutableStateMapOf<String, ShowcaseProperty>() }
@Composable fun IntroShowCase( targets: SnapshotStateMap<String, ShowcaseProperty>, backgroundColor: Color = Color.Black, onShowcaseCompleted: () -> Unit ) { val uniqueTargets = targets.values.sortedBy { it.index } var currentTargetIndex by remember { mutableStateOf(0) }
val currentTarget = if (uniqueTargets.isNotEmpty() && currentTargetIndex < uniqueTargets.size) uniqueTargets[currentTargetIndex] else null
Similarly, you can add rest of the views as aShowcaseProperty to make it look like the video shown at the beginning of the article. Full source code is available here.
As I mentioned earlier, the implementation is also available as alibrary, which you can integrate easily. Feel free to use it in your app and if you want to customize it you’re free to fork.
Here’s an example of an Android application that allows users to choose an image from the camera or gallery and perform cropping functionality using the “Android Image Cropper” library.
Step 1: Set up the project
Start by creating a new Android project in Android Studio. Make sure you have the necessary dependencies and permissions in your project’s build.gradle file:
That’s it! Now you have an Android application that allows users to choose an image from the camera or gallery and perform cropping functionality. Remember to replace the placeholder image with an actual placeholder image of your choice.
Note: Don’t forget to add the necessary runtime permissions check in your code to handle permissions for accessing the camera and gallery.
Android Bottom Navigation stays at the bottom of the screen providing navigation between top-level views in the app. This is introduced in design support library with backward compatibility. Bottom Navigation should be used when the app has three to five top-level navigations.
This article explains the basics of Bottom Navigation, combining it with Fragments. We also going to learn how to load the first fragment with grid data (using RecyclerView) by fetching JSON through HTTP call.
1. Bottom Navigation
The Bottom Navigation can be easily added using BottomNavigationView component. You have to use gravitation or relative attributes to make it appear at the bottom of the screen.
app:menu — The menu resource file to display the navigation items along with icon and text. app:itemBackground — Applies background color to bottom navigation. app:itemTextColor — The text color of bottom navigation item. app:itemIconTint — The icon color of bottom navigation item.
When to use Bottom Navigation? As per the design specs, the below navigations should be used depending on the criteria.
>Navigation Drawer – Use when top-level navigation has more than six destinations.
>Tabs – Use when there are two navigational destinations.
>Bottom Navigation – Use when there are three to five top-level destinations.
Before going further, have a quick look at the design specifications of Bottom Navigation.
Now let’s try it by creating a new project in Android Studio.
2. Creating New Project
1. Create a new project in Android Studio from File ⇒ New Project and select Basic Activity from templates.
2. Download this res folder and add the drawables to your project’s res. This folder contains necessary drawables required for bottom navigation items.
3. Make sure you have design support library in your build.gradle.
6. Open the layout file of main activity i.e activity_main.xml and add BottomNavigationView widget. Here we are also adding a FrameLayout to load the Fragments when the navigation item is selected.
7. Now open MainActivity.java and modify it as below.
> Here, OnNavigationItemSelectedListener will be called when the bottom navigation item is selected. For now we are just changing the toolbar title upon selecting the navigation item.
If you run the app, you can see the bottom navigation displayed as shown below.
3. Adding Fragments
As we have the Bottom Navigation ready, let’s see how to switch views when the navigation menu item is selected. This can be done easily by using the Fragments.
Note: ViewPager shouldn’t be used when using Bottom Navigation as per design specs (Avoid using lateral motion to transition between views)
I am creating four fragments named StoreFragment, GiftsFragment, CartFragment and ProfileFragment.
8. Create new Fragment by going to File ⇒ New ⇒ Fragment ⇒ Fragment (Blank) and name it as StoreFragment.java. Likewise create other three fragments too.
9. Open MainActivity.java and modify bottom navigation listener as below to load the fragments in FrameLayout.
>loadFragment() – loads the Fragment into FrameLayout. The same method is called in OnNavigationItemSelectedListener callback by passing appropriate fragment instance. > The logic needed for specific module goes into appropriate Fragment keeping the MainActivity clean.
MainActivity.java
package info.androidhive.bottomnavigation;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import info.androidhive.bottomnavigation.fragment.CartFragment;
import info.androidhive.bottomnavigation.fragment.GiftsFragment;
import info.androidhive.bottomnavigation.fragment.ProfileFragment;
import info.androidhive.bottomnavigation.fragment.StoreFragment;
import info.androidhive.bottomnavigation.helper.BottomNavigationBehavior;
public class MainActivity extends AppCompatActivity {
private ActionBar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = getSupportActionBar();
// load the store fragment by default
toolbar.setTitle("Shop");
loadFragment(new StoreFragment());
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
Fragment fragment;
switch (item.getItemId()) {
case R.id.navigation_shop:
toolbar.setTitle("Shop");
fragment = new StoreFragment();
loadFragment(fragment);
return true;
case R.id.navigation_gifts:
toolbar.setTitle("My Gifts");
fragment = new GiftsFragment();
loadFragment(fragment);
return true;
case R.id.navigation_cart:
toolbar.setTitle("Cart");
fragment = new CartFragment();
loadFragment(fragment);
return true;
case R.id.navigation_profile:
toolbar.setTitle("Profile");
fragment = new ProfileFragment();
loadFragment(fragment);
return true;
}
return false;
}
};
private void loadFragment(Fragment fragment) {
// load fragment
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
}
Now if you run the project you can see the fragments loaded when navigation is selected.
4. Implementing ShopFragment – Displaying Items in Grid
Now we’ll see how to implement the first fragment i.e ShopFragment which displays the shop items in a Grid fashion. For demonstration, I have created a sample json which contains few movies for sale. To implement this, all we have to do is, fetch json and display the data in RecyclerView in a grid format. To make the task simpler, follow my other article which explains the same.
14. Create an xml layout named store_item_row.xml under res ⇒ layout. This layout file will be used in RecyclerView adapter class to render single item.
15. Create a class named Movie.java. This POJO class will be useful while parsing the json.
Movie.java
package info.androidhive.bottomnavigation;
public class Movie {
String title;
String image;
String price;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
16. Now open StoreFragment.java and add below code. For simplicity the RecyclerView adapter class StoreAdapter included in the same fragment.
>fetchStoreItems() Method fetches the movies json using Volley and serializes it using Gson.
>StoreAdapter class renders the movies in RecyclerView.
StoreFragment.java
package info.androidhive.bottomnavigation.fragment;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.DefaultItemAnimator;
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.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.bumptech.glide.Glide;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.json.JSONArray;
import java.util.ArrayList;
import java.util.List;
import info.androidhive.bottomnavigation.Movie;
import info.androidhive.bottomnavigation.app.MyApplication;
import info.androidhive.bottomnavigation.R;
public class StoreFragment extends Fragment {
private static final String TAG = StoreFragment.class.getSimpleName();
private static final String URL = "https://api.androidhive.info/json/movies_2017.json";
private RecyclerView recyclerView;
private List<Movie> movieList;
private StoreAdapter mAdapter;
public StoreFragment() {
// Required empty public constructor
}
public static StoreFragment newInstance(String param1, String param2) {
StoreFragment fragment = new StoreFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_store, container, false);
recyclerView = view.findViewById(R.id.recycler_view);
movieList = new ArrayList<>();
mAdapter = new StoreAdapter(getActivity(), movieList);
RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(getActivity(), 3);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.addItemDecoration(new GridSpacingItemDecoration(2, dpToPx(8), true));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(mAdapter);
recyclerView.setNestedScrollingEnabled(false);
fetchStoreItems();
return view;
}
private void fetchStoreItems() {
JsonArrayRequest request = new JsonArrayRequest(URL,
new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray response) {
if (response == null) {
Toast.makeText(getActivity(), "Couldn't fetch the store items! Pleas try again.", Toast.LENGTH_LONG).show();
return;
}
List<Movie> items = new Gson().fromJson(response.toString(), new TypeToken<List<Movie>>() {
}.getType());
movieList.clear();
movieList.addAll(items);
// refreshing recycler view
mAdapter.notifyDataSetChanged();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// error in getting json
Log.e(TAG, "Error: " + error.getMessage());
Toast.makeText(getActivity(), "Error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
}
});
MyApplication.getInstance().addToRequestQueue(request);
}
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
private boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view); // item position
int column = position % spanCount; // item column
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
if (position < spanCount) { // top edge
outRect.top = spacing;
}
outRect.bottom = spacing; // item bottom
} else {
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount) {
outRect.top = spacing; // item top
}
}
}
}
/**
* Converting dp to pixel
*/
private int dpToPx(int dp) {
Resources r = getResources();
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()));
}
class StoreAdapter extends RecyclerView.Adapter<StoreAdapter.MyViewHolder> {
private Context context;
private List<Movie> movieList;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView name, price;
public ImageView thumbnail;
public MyViewHolder(View view) {
super(view);
name = view.findViewById(R.id.title);
price = view.findViewById(R.id.price);
thumbnail = view.findViewById(R.id.thumbnail);
}
}
public StoreAdapter(Context context, List<Movie> movieList) {
this.context = context;
this.movieList = movieList;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.store_item_row, parent, false);
return new MyViewHolder(itemView);
}
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
final Movie movie = movieList.get(position);
holder.name.setText(movie.getTitle());
holder.price.setText(movie.getPrice());
Glide.with(context)
.load(movie.getImage())
.into(holder.thumbnail);
}
@Override
public int getItemCount() {
return movieList.size();
}
}
}
Now if you run the app, you can see the ShopFragment displaying the movies in grid manner. Likewise you can implement other fragments too.
5. Hiding Bottom Navigation on Scroll
As per design specs, the Bottom Navigation has to be hidden when the content is scrolled giving more room to content on the screen. To achieve this, we need to attach the BottomNavigationBehavior to Bottom Navigation.
17. Create a class named BottomNavigationBehavior.java with the below code.