Introduction
- This series is going to be dedicated to the basic to Android development. Join me and let us try to build and understand some cool stuff.
YouTube version
YouTube Version of the blog post.
Setup
- Before you start this tutorial, you should already have android studio installed and have a fresh project started. From this point on I will assume you have met this criteria.
Creating a fragment
- Normally you would go,
file-->new-->fragment
in android studio and this is how you would do things in the real world. However, we are going to break these steps down and do things separately. I will now post the code and walk us through it.
public class CrimeFragment extends Fragment {
}
The first thing that we do is,
file-->new-->class
. The name of the class can be anything. The only reason that the name of the class is CrimeFragment is because I am following along with the Android Programming: the big nerd ranch guid and that is the naming conventions that it uses. The book is a little outdated but we can use the official Android documentation to supplement where to book is lacking.As you can see a fragment in Android is just a normal Java class that extends the Fragment class. As we know, the
extend
keyword gives us access to all public fields in the Fragment class.
Creating a model
- Before we move on, we are going to make a class called Crime. This is going to hold basic information that will later be used to create our crime object.
public class Crime {
private UUID id;
private String title;
public Crime(){
this.id = UUID.randomUUID();
}
public UUID getId(){
return this.id;
}
public String getTitle(){
return this.title;
}
public void setTitle(String title){
this.title = title;
}
}
- The code above is pretty standard
POJO
(plain old Java object), so nothing special to explain. Just make sure that this class is in its own java file and that it is in the same package as theCrimeFragment
.
Updating the Crime Fragment
- Next we are going to update the CrimeFragment class so that we add some new instance variables and and override the onCreate() method.
public class CrimeFragment extends Fragment {
private Crime crime;
private EditText titleField;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.crime = new Crime();
}
}
- Notice that we added two new instance variables, one to hold our Crime object and another to hold an object called titleField. You might be wondering about the type EditText, we will talk more about that later. Then we add the
@Override
, we add this to indicate to the compiler that we are overriding a method(it will notify us if the method does not exist). It is not mandatory but it is considered best practice when overriding inherited methods.
What is overriding?
- Well overriding is the ability of a subclass(any class to the left of the extends keyword) to override a method that gets inherited from the superclass(any class to the right of the extends keyword). We do this because the superclass has a method where its behaviour is "close enough" and then we modify its behaviour to fit our needs. You might of also of seen the super keyword being used.
super
allows us to access the superclass's method of onCreate(), which means the method still gets called.
Creating the .XML files
- If you are unfamiliar, fragments do not exists on their own, they actually exists inside of activities or other fragment. So we need to create a place inside of the activities, this will be done with the frame layout.
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
tools:context=".MainActivity">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
- This is done inside of the main activity .XML file. You can think of .XML files as being the HTML files of Android development. The first thing that you probably noticed is that we have defined a
Constraintlayout
to hold the FrameLayout.
What is a layout?
- A layout defines the structure for a user Interface in our app. All elements in the layout are built using a heiarchy of View and View Group objects. A View being something the user can see and interact with like a button. A ViewGroup is just an invisible container that defines the layout structure for Views and other View Group objects.
What is Constraint Layout?
- A constraint layout is just a ViewGroup which allows us to create more complex layouts through more detailed layout params. Layout params are used by views to tell their layouts how they are structured.
What is FrameLayout?
A FrameLayout is designed to block out an area on the screen to display a single item. A FrameLayout should be used to hold a single child view. This is what is going to hold our fragment.
The FrameLayout that we have placed in our main activity is going to be our host environment for our fragment. Also make sure you noticed the
android:id="@+id/fragment_container"
, this is how we define an id in Android and we will use it to access this layout later on.
Creating the fragment
- Now that we have created a place for our fragment to live, we can now create our fragment XML file. This file will act as the template for how our fragment is going to look. Make sure to create this file inside the res/layout folder, then go file-->new-->layout resource file . Define its name and make sure the root element is LinearLayout. After that hit ok and you have a proper XML resource file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText android:id="@+id/crime_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:hint="@string/crime_title_hint"
/>
</LinearLayout>
- A LinearLayout is just a normal layout but it gives us the ability to define the orientation of views inside of it, through the
android:orientation="vertical"
. That means all of the elements should be positioned vertically.EditText
is a View that provides us with a user interface for entering and modifying text. Notice that we have definedandroid:id="@+id/crime_title"
, which we will use later. We also definedandroid:inputType="text"
which we use to say that this EditText view only accepts text input.
Inflating the Fragment
public class CrimeFragment extends Fragment {
private Crime mCrime;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCrime = new Crime();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime,
container, false);
return v;
}
}
- As you can see we are updating the CrimeFragment by adding the onCreateView() method. The onCreateView() method is very important because it is the method that gets called in order to inflate(create) the view and then return that view to the host activity. The star of this section is the inflate() method.
What is the inflate method?
- This method takes 3 parameters. The first parameter is the resource id, for us the resource id is
R.layout.fragment_crime
and that is the XML file that we used to define the structure for our fragment. The second parameter I found a little confusing but the best example of what it is can be found Here. It states that the second parameter is, "the parent view that the fragment's UI should be attached to.". I believe that this means that the second parameter will be theFrameLayout
of our main activity. If you have forgotten, the FrameLayout is the part of our main activity in which the fragment will live. The third parameter is a boolean that states if we want to add this fragment to the parent view. We pass in false because we will later use the Fragment Manager to add the view.
Wiring up the EditText View
public class CrimeFragment extends Fragment {
private Crime crime;
private EditText titleField;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup
container,Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime,
container, false);
titleField =
(EditText)v.findViewById(R.id.crime_title);
titleField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(
CharSequence s, int start, int count, int after) {
// This space intentionally left blank
}
@Override
public void onTextChanged(
CharSequence s, int start, int before, int count) {
crime.setTitle(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
// This one too
}
});
return v;
}
}
- The first thing that you should of noticed is that we added a new Reference type and that type is
EditText
.
What is EditText?
- EditText is a user interface element for entering and modifying text. Then we get a reference to the EditText and assign it to the titleField variable
titleField = (EditText)v.findViewById(R.id.crime_title)
. The findViewById lets us find our view(which is EditText) by its id which iscrime_title
.
Adding a TextWatcher to our EditText View
- So EditText uses a TextWatcher to watch over any changes made and when we add a TextWatcher with the addTextChangedListener method we have to implement 3 methods. ** 1. beforeTextChanged , ** 2. onTextChanged ** and ** 3. afterTextChanged. As the names of the methods suggest they get called, before the text is changed, after the text is changed and during the text change. Of those 3 methods that only one that we care about is the onTextChanged. Anytime a text change is detected we are going to update our crime's title with
crime.setTitle(s.toString());
. We have to use toString() because if we don't thes
remains a character sequence. Also, thes
is what holds the characters that are being changed.
Adding Fragments to the Fragment Manager
- The first step in this section is go to your main activity and copy in the code.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if(fragment == null){
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragment_container,fragment)
.commit();
}
}
}
- In this code section, the first thing that we do is get a instance of the activities Fragment Manager(FM) with
getSupportFragmentManager();
. This method just returns the FM for interacting with fragments associated with this activity. We then try to find the fragment withfindFragmentById(R.id.fragment_container)
initially on startup it will not find a fragment so it will be null. Then we hit the conditional, if our fragment is null(on startup it is), so we create a new CrimeFragment object withnew CrimeFragment();
. Then we begin a transaction on the FM... wait, what is atransaction
?
What is a Transaction?
- At runtime a FM can add, remove, replace and do other actions with fragments in response to user interactions and each of these actions is called a transaction. To begin a transaction we call
beginTransaction()
this lets the FM know that a transaction is about to take place. The meat of this transaction is the add() method. We provide this method with the fragment container, where it is going to go and we also provide it with the actual fragment. It will then add the fragment to the FM. Finally we close the transaction with the .commit() method.
Top comments (0)