Click here to Skip to main content
15,886,778 members
Articles / Mobile Apps / Android

Create and Publish Your First Android App – Part 2

Rate me:
Please Sign up or sign in to vote.
4.86/5 (5 votes)
2 Jun 2015CPOL7 min read 15K   8   5
How to create and publish your first Android app

This post is part 2 of my four part tutorial series on how to create and publish your first Android app. The demo app for this series is an Attendance app and in part 1, we ended with a skeleton project that has material design navigation drawer implemented. In this post, we will add navigation to that navigation drawer so that we can click on an item in the navigation drawer and have it actually take us to the selected screen.

Refactor

If you are following from part 1 which I encourage you to do, then you may want to know that I have renamed the ReportFragment to ProfileFragment. Right click the ReportFragment, select refactor and then select rename. Do the same for fragment_report.xml in the layout folder.

Implementing Navigation in Navigation Drawer

As you remembered, our navigation drawer was implemented with RecyclerView and unlike the humble ListView, the RecyclerView does not implement OnItemClick. That means with ListView, it is easy to handle the event what happens when a row in the list is clicked. That simplicity does bring some challenges such as what happens when you have other clickable items inside a row like buttons. The RecyclerView solved some of this problems with the concept of a LayoutManager instead of rows.

There are two ways in which we can handle clicks in the RecylerView – in the Adapter class and in the calling Activity or Fragment and we will use both approaches in this tutorial. To handle click for the Navigation Drawer follow this steps. To see a full tutorial about how to handle onItemTouch for RecylerView, please consult this blog post.

  1. Implement a method that opens another Activity: The goal of any navigation system is to take you from point A to point B. In the case of the navigation drawer, you want to go from one screen (Fragment or Activity) to another screen. This is a simple method below that accepts the name of the Fragment we want it to open and all it does is open that Fragment, copy and paste the code below to the end of your Main Activity.
    Java
    public void openFragment(final Fragment fragment) {
            getSupportFragmentManager()
                    .beginTransaction()
                    .replace(R.id.container, fragment)
                    .commit();
        }
  2. Create static names for the Fragments – At the top of your Main Activity, create static names to represent your Fragments like this:
    Java
    private final static int ATTENDANTS_FRAGMENT = 1;
        private final static int EVENTS_FRAGMENT = 2;
        private final static int REGISTRATION_FRAGMENT = 3;
        private final static int SETTINGS_FRAGMENT = 4;
  3. Create a switch: As you will see shortly, once an item is clicked in the navigation drawer, the RecyclerView returns the position of that item that was clicked and we need a simple switch statement that calls the openFragment() method based on the position number that was selected. Copy and paste the method below under your openFragment() method towards the bottom of your MainActivity:
    Java
    private void onTouchDrawer(final int position) {
            //  if (currentFragment == position) return;
            currentFragment = position;
    
            switch (position) {
                case ATTENDANTS_FRAGMENT:
                    openFragment(new AttendantsList());
                    break;
                case REGISTRATION_FRAGMENT:
                    openFragment(new RegistrationFragment());
                    break;
                case EVENTS_FRAGMENT:
                    openFragment(new EventListFragment());
                    break;
                case SETTINGS_FRAGMENT:
                    startActivity(new Intent(this, PreferenceActivity.class));
                default:
                    return;
            }
        }
  4. Create a GestureDetector: This object determines what type of touch was received whether it is a swipe up or regular tap. Add the code below towards the end of the onCreate method in your Main Activity.
    Java
    final GestureDetector mGestureDetector =
                    new GestureDetector
                        (MainActivity.this, new GestureDetector.SimpleOnGestureListener() {
                        @Override
                        public boolean onSingleTapUp(MotionEvent e) {
                            return true;
                        }
                    });
  5. AddOnItemTouchListener – This listens for a touch, and when an item is touched, we check with the GestureDetector to detect what kind of touch this is. We are only interested in the click event, we are not handling the swipe event here, and the GestureDetector returns true for click events. If the GetureDetector returns true, then we get the position of the item that was clicked and pass it to the switch statement method to determine what was clicked and open that Fragment. Here is the method.
    Java
    mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
                @Override
                public boolean onInterceptTouchEvent
                    (RecyclerView recyclerView, MotionEvent motionEvent) {
                    View child = recyclerView.findChildViewUnder
                    (motionEvent.getX(),motionEvent.getY());
    
                    if (child != null && mGestureDetector.onTouchEvent(motionEvent)){
                        Drawer.closeDrawers();
                        onTouchDrawer(recyclerView.getChildLayoutPosition(child));
                        return true;
                    }
                    return false;
                }
    
                @Override
                public void onTouchEvent(RecyclerView rv, MotionEvent e) {
    
                }
            });
  6. Call the switch statement to determine the Fragment that was clicked on – called from the OnItemTouchedListener above
  7. Open that Fragment – Called from the onTouchDrawer

The updated Main Activity should look like this. You should now be able to navigate to the different Fragments listed in the navigation drawer. To verify change the Hello World texts in each of the layouts of the Fragment to Hello Fragment name or change the background color of each Fragment layout to a different color so you will get visual feedback. Make sure that you added the Toolbar to the PreferenceActivity just the way you did for the Main Activity.

Model Classes

Now we need to begin to flesh out the model classes in this app. First, let's start with the Attendant, normally guests for an event do not like to give out too much information just to check in to an event, normally name and email address but I have added other properties like address for completion. The ProfileImagePath and ProfileImageId are duplicates. We will delete one later. Here is the Attendant model class:

Java
public class Attendant {
    private String Name;
    private String Email;
    private String Phone;
    private String StreetAddress;
    private String City;
    private String State;
    private String PostalCode;
    private String ProfileImagePath;
    private int ProfileImageId;
}

Next is the Attendance.java class, just have properties for tracking checkout and check in for each event and Attendant. Here is the content of that class:

Java
public class Attendance {
    private Long CheckInTime;
    private Long CheckOutTime;
    private Event Event;
    private Attendant Attendant;
}

And next is the Event:

Java
public class Event {
    private Long EventDate;
    private String Venue;
    private String City;
    private String SerializedAttendantsList;
    private String EventPicturePath;
    private String EventPictureId;

    private Organizer Organizer;   
    private List<Attendant> GuestList;

}

Notice the serialized attendants list, we will come back to that when we talk about database. The Organizer class just extends the Attendant class for now. For each of the classes, within the class file, click on Code on the Android Studio toolbar, then Generate -> Getter & Setters to generate the getters and setters. Alternatively, you can make all the properties public instead of private.

The properties of the model classes represent the information that we want to work with or save regarding each model in our app. For example for each attendant, we have their name, email, image, etc. and we can use that information to represent each attendant like this:

attendants_list

Attendance List Layout and Adapter

Let us go ahead and create the layout file for the list above and create the adapter that manages then. From the work we did on the navigation drawer, I am hoping that it is obvious to you that the above screen above is a List and we continue to use the RecyclerView since we want to adhere to modern Android development paradigm as was laid out in the material design guideline. And I hope that you should know that each list needs an adapter. So the standard component for any list is:

Custom layout to layout the rows

An Adapter that passes the dataset to the list

The ListView or the Recyclerview and these three are represented in the image below retrieved from Android developer site

RecyclerView

In your res/layout folder, add a file called attendants_list_row.xml and here are the contents of that file:

XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="wrap_content">

    <de.hdodenhof.circleimageview.CircleImageView
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_margin="10dp"
        android:layout_centerVertical="true"
        android:id="@+id/image_view_attendant_head_shot" /> 

    <LinearLayout
        android:id="@+id/linear_layout_name_and_buttons"
        android:layout_toRightOf="@+id/image_view_attendant_head_shot"
        android:paddingLeft="@dimen/margin_padding_large"
        android:layout_alignParentRight="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <LinearLayout
            android:id="@+id/linear_layout_attendant_name"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:orientation="vertical">

            <TextView
                android:id="@+id/text_view_attendants_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                />
            <TextView
                android:id="@+id/text_view_attendants_email"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"  />
        </LinearLayout>
        <LinearLayout
            android:id="@+id/linear_layout_checkin_button"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:paddingTop="@dimen/margin_padding_normal"
            android:orientation="horizontal">

            <ToggleButton
                android:id="@+id/togglebutton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:textOn="Check in"
                android:textOff="Check out"
                style="@style/UIButton"
                />
            <TextView
                android:layout_width="@dimen/margin_padding_normal"
                android:layout_height="wrap_content" />

            <Button
                android:id="@+id/button_profile"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_margin="3dp"
                android:text="@string/button_profile"
                style="@style/UIButton" />
        </LinearLayout>
    </LinearLayout>
</RelativeLayout>

Update your res/values/color.xml to this:

XML
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="primary">#F45F5F</color>
    <color name="primaryDark">#0288D1</color>
    <color name="accent">#FFC107</color>
    <color name="lightPrimary">#B3E5FC</color>
    <color name="textIcon">#FFFFFF</color>
    <color name="primaryText">#212121</color>
    <color name="secondaryText">#727272</color>
    <color name="divider">#B6B6B6</color>
    <color name="activated_color">#E8E8E8</color>
    <color name="theme_color">@color/primary</color>
    <color name="white_transparent">#D9FFFFFF</color>
    <color name="transparent">@android:color/transparent</color>
</resources>

Update your res/values/styles.xml to this:

XML
<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/primary</item>
        <item name="android:windowActionBar">false</item>
        <item name="android:textSize">@dimen/text_size_normal</item>
    </style>

    <style name="UIButton">
        <item name="android:textColor">@color/white_transparent</item>
        <item name="android:textStyle">bold</item>
        <item name="android:background">@drawable/button_selector</item>
        <item name="android:minHeight">@dimen/margin_padding_large</item>
        <item name="android:minWidth">@dimen/margin_padding_xxlarge</item>
        <item name="android:gravity">center</item>
        <item name="android:button">@null</item>
        <item name="android:paddingLeft">@dimen/margin_padding_small</item>
        <item name="android:paddingRight">@dimen/margin_padding_small</item>
    </style>

</resources>

In your Adapters package, add a Java class file called AttendantsAdapter.java and below is the content of that file:

Java
public class AttendantsAdapter extends RecyclerView.Adapter<AttendantsAdapter.ViewHolder>{
    private List<Attendant> mAttendants;
    private Context mContext;   
    View rowView;

    public static class ViewHolder extends RecyclerView.ViewHolder{
        ImageView attendantHeadshot;
        TextView attendantName, attendantEmail;
        ToggleButton checkInCheckOutButton;
        Button profileButton;

        public ViewHolder(View itemView) {
            super(itemView);
            attendantHeadshot = (ImageView) itemView.findViewById(R.id.image_view_attendant_head_shot);
            attendantName = (TextView)itemView.findViewById(R.id.text_view_attendants_name);
            attendantEmail = (TextView)itemView.findViewById(R.id.text_view_attendants_email);
            checkInCheckOutButton = (ToggleButton)itemView.findViewById(R.id.togglebutton);
            profileButton = (Button)itemView.findViewById(R.id.button_profile);
        }
    }

    public AttendantsAdapter(List<Attendant> attendantsList, Context context){
        mAttendants = attendantsList;
        mContext = context;       
    }

    // Create new views (invoked by the layout manager)
    @Override
    public AttendantsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        rowView = LayoutInflater.from(parent.getContext()).inflate(R.layout.attendants_list_row, parent, false);

        ViewHolder viewHolder = new ViewHolder(rowView);
        return viewHolder;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(AttendantsAdapter.ViewHolder holder, int position) {
        final Attendant selectedAttendant = mAttendants.get(position);

        holder.attendantName.setText(selectedAttendant.getName());
        holder.attendantEmail.setText(selectedAttendant.getEmail());
        Picasso.with(mContext).load
                (selectedAttendant.getProfileImageId()).into(holder.attendantHeadshot);

        if (position % 2 == 0){
            rowView.setBackgroundColor(mContext.getResources().getColor(R.color.activated_color));
        }

        holder.checkInCheckOutButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Is the toggle on?
                boolean on = ((ToggleButton) v).isChecked();

                if (on){
                    Toast.makeText(mContext, selectedAttendant.getName() + 
                                   " checked in ", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(mContext, selectedAttendant.getName() + 
                                   " checked out ", Toast.LENGTH_SHORT).show();
                }
            }
        });

        holder.profileButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               Toast.makeText(mContext, selectedAttendant.getName() + 
                              " profile clicked ", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return mAttendants.size();
    }
}

External Library – Buy it or Build It

You might notice the use of Picasso library in the adapter above or you might receive an error message if you copy and paste, the reason is that it is an external library. And the debate of whether to build everything from scratch or use an existing implementation where necessary is a common question that developers wrestle with all the time and it is often called the buy it or build it debate. For loading images in the Attendants list, I have opted to use the Picasso library from Square because this app can be extended such that attendants can register with their Facebook or Google account and we need a reliable library that can handle fetching their profile images from the web and display on the list.

Add Picasso library to your build.gradle file with this line:

compile 'com.squareup.picasso:picasso:2.5.2'

Add RecyclerView to the fragment_attendants_list.xml layout file like this:

XML
<FrameLayout 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"
    tools:context="okason.com.attendanceapp.Fragments.AttendantsList">

    <!-- A RecyclerView with some commonly used attributes -->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/attendants_recycler_view"
        android:scrollbars="vertical"
        android:background="@color/white_transparent"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</FrameLayout>

Add or update the file dimens.xml in res/values folder to hold common dimensions that we will need. And you can find the content of that file here. Now update the AttendantsListFragment.java like this:

Java
public class AttendantsList extends Fragment {
    private View mRootView;

    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;
    private List<Attendant> mAttendantsList;


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

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        mRootView = inflater.inflate(R.layout.fragment_attendants_list, container, false);

        mRecyclerView = (RecyclerView) mRootView.findViewById(R.id.attendants_recycler_view);

        mRecyclerView.setHasFixedSize(true);

        mLayoutManager = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(mLayoutManager);

        mAttendantsList = new ArrayList<Attendant>();
        //addTestGuessList();
        mAdapter = new AttendantsAdapter(mAttendantsList, getActivity());       
       
        mRecyclerView.setAdapter(mAdapter);
        return mRootView;
    }
}

Testing

To test this, you need to add some dummy attendants. First, you need to get any temporary images and add them to your drawables folder and you can name them anything. I named mine headshot_1.jpg, headshot_2.jpg, etc. up to 10 and with that, I now create a temp method like the one below and once you do it, you can now uncomment the line addTestGuestList() above:

JavaScript
private void addTestGuessList() {
        Attendant guest1 = new Attendant();
        guest1.setName("Debbie Sam");
        guest1.setEmail("deb@email.net");
        guest1.setProfileImageId(R.drawable.headshot_1);
        mAttendantsList.add(guest1);

        Attendant guest2 = new Attendant();
        guest2.setName("Keisha Williams");
        guest2.setEmail("diva@comcast.com");
        guest2.setProfileImageId(R.drawable.headshot_2);
        mAttendantsList.add(guest2);

        Attendant guest3 = new Attendant();
        guest3.setName("Gregg McQuire");
        guest3.setEmail("emailing@nobody.com");
        guest3.setProfileImageId(R.drawable.headshot_3);
        mAttendantsList.add(guest3);

        Attendant guest4 = new Attendant();
        guest4.setName("Nancy Watson");
        guest4.setEmail("nancy@hotmail.com");
        guest4.setProfileImageId(R.drawable.headshot_4);
        mAttendantsList.add(guest4);

        Attendant guest5 = new Attendant();
        guest5.setName("Sarah Domingo");
        guest5.setEmail("sarah@yahoo.com");
        guest5.setProfileImageId(R.drawable.headshot_5);
        mAttendantsList.add(guest5);

        Attendant guest6 = new Attendant();
        guest6.setName("Anthony Lopez");
        guest6.setEmail("toney@gmail.com");
        guest6.setProfileImageId(R.drawable.headshot_6);
        mAttendantsList.add(guest6);

        Attendant guest7 = new Attendant();
        guest7.setName("Chris VanHorn");
        guest7.setEmail("chris@worldmail.com");
        guest7.setProfileImageId(R.drawable.headshot_7);
        mAttendantsList.add(guest7);

        Attendant guest8 = new Attendant();
        guest8.setName("Frank Krueger");
        guest8.setEmail("frank@ymail.com");
        guest8.setProfileImageId(R.drawable.headshot_8);
        mAttendantsList.add(guest8);

        Attendant guest9 = new Attendant();
        guest9.setName("Bella Florentino");
        guest9.setEmail("bella@outlook.com");
        guest9.setProfileImageId(R.drawable.headshot_9);
        mAttendantsList.add(guest9);

        Attendant guest10 = new Attendant();
        guest10.setName("Donna Simons");
        guest10.setEmail("donna@company.com");
        guest10.setProfileImageId(R.drawable.headshot_10);
        mAttendantsList.add(guest10);
    }

Summary

We touched on quite a few concepts here that will take a much longer post to try to explain them one by one. If you have questions, use the comments box to ask. I will write a separate post about data persistence.

If you like this tutorial, please share it with someone who can benefit from it or through your social media. If you want to be notified when I release the next tutorial in the series, please use the opt-in form below to join my mailing list. If you need clarifications, use the comment button below to ask questions. If you have feedback for me on how I can improve my tutorials or what topic you want to learn more about, use the contact form to reach out to me.

The post Create and Publish Your First Android App – Part 2 appeared first on Val Okafor.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) ValOkafor.com
United States United States
My name is Val Okafor, I am a Senior Software Engineer with specialization in Android Development. Learning and problem solving is my passion and I share my Android development knowledge through my blog ValOkafor.com.

My Android courses are the courses I wish I had when I started. I teach Android development in the context of a fully developed Android app. I believe that new Android concepts are better understood if they are presented in the context of creating an app from scratch to finish.

I focus on creating Productivity Android apps and besides Android development I have 7 years’ experience as System Administrator supporting Enterprise applications and 2 years’ experience as a Web Developer building websites using PHP and ASP.Net.

I have worked for corporations such as The Home Depot, American Council on Exercise, Legend3D and HD Supply, Inc. I have a bachelor's degree in Information Technology from National University San Diego, California and a master's degree in Software Engineering from Regis University Denver, Colorado.

I enjoy sharing my extensive work experience through my blog, social media.

Comments and Discussions

 
AnswerGood article Pin
Sarah Lynn Buckhannon14-Jun-15 12:37
Sarah Lynn Buckhannon14-Jun-15 12:37 
GeneralNice one! Pin
Sandeep Singh Shekhawat3-Jun-15 2:58
professionalSandeep Singh Shekhawat3-Jun-15 2:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.