SJ cartoon avatar

Mobile Android Adventures - Saarberus with Saaripar

If you haven’t been reading Bryan’s last couple blog posts (here and here), then you’re missing out on the process of developing an Android app with all the right libraries - from scratch! I’ll wait here while you read over those posts… … Actually, no, I won’t - I’m going to roll right along and set him up for his next post, which has him mixing up pop culture and Greek mythology. Not sure how I feel about that just yet… But I do like Saaripar.

Enter Bryan

Welcome to Android Adventures Part Three - First Blood.

In part one, we used RetroFit and a simple data model to pull data from GitHub’s API and display it in a TextView.

In part two, we used Picasso to turn that data into a sultry, smoky-eyed, ListView (just look at those avatars…).

In part three, we’re going to turn our humble, coquettish single-Activity application into a hulking three-headed behemoth. I call it Saarberus.

Right now, our ListView doesn’t do much more than sit there and look pretty (and is there really any more to life than being really, really, ridiculously good-looking?) - that’s fine, but let’s try adding some real functionality!

We’re going to add two more components - a Repo WebView Activity and a User Profile Activity (where we’re going to see how Saaripar, an insanely powerful and easy-to-use library, can help simplify form validation down to just a couple of lines of code).

If you’ve been following along, you should already have some familiarity with Activities. Good. Let’s make some more.

Repo WebView

Let’s start with the WebView for two reasons - one, it’s very simple, and two, we want to save the Saaripar magic for after we already kind of understand how to create new Activities. First, let’s make our XML file (and, as always, we’re going to put it in our res/layout folder) - mine is below. We’re not going to use a Text or ListView, but another type called a WebView. I called mine single_list_item but taken_list_item would’ve worked just as well.

<WebView  xmlns:android="https://schemas.android.com/apk/res/android"
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

Pretty complicated, right? That’s our entire XML file. Now let’s set up our activity.

I called mine SingleListView - what can I say, I’m a bachelor at heart. Take a look!

public class SingleListItem extends Activity {
@Bind(R.id.webview)
WebView browser;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.single_list_item);
    ButterKnife.bind(this);

    Intent i = getIntent();
    String url = i.getStringExtra("url");
    browser.loadUrl(url);
   }
}

By now, you should be familiar with ButterKnife and how we’re using it to clean up our act(ivities). We @Bind our WebView, set our ContentView to our single list item, and bind - then we’re nearly done. To understand these lines -

Intent i = getIntent();
String url = i.getStringExtra("url");
browser.loadUrl(url);

we need to talk about how an Activity is called from within another. Broadly, calling an Activity is done like so -

//Create an intent to start an Activity
Intent i = new Intent(aContext, yourActivityClass.class);
aContext.startActivity(i);

We’re not actually going to be using a variable called ‘aContext’ (probably just ‘this’ - I’ll explain later) in our code, but I wanted to introduce the idea of Contexts in Android. At first, I had trouble understanding what exactly a Context was - essentially, a Context is a snapshot of the current state of your application that can be passed to new objects and provide information about what’s going on.

Where it gets interesting is that we can also put extra information into our Intent, to be picked up by our Activity on creation:

//Passing an object to the Intent to be picked up later
Intent i = new Intent(aContext, yourActivityClass.class);
i.putExtra("ObjectName", object);
aContext.startActivity();

As you can probably guess, we’re going to be passing a URL to our WebView and loading it within our WebView - that’s the whole activity!

Be sure to declare your new activities in your AndroidManifest.xml - put the following into the XML between your tags and you should be fine.

<activity android:name=".SingleListItem"
<android:label="Single Item Selected"></activity>

That’s that for our WebView! Enjoy the simplicity while it lasts. Let’s create our User Profile Activity - this one is going to get crazy.

Saaripar

Our UserProfile activity is going to use the three libraries we’ve covered and the one we’re introducing today in one fell swoop - RetroFit to grab User data from GitHub’s API, ButterKnife to instantiate our views, Picasso to load and set the User Profile avatar, and Saaripar to validate our forms!

Oh my.

Let’s start with our Gradle script - as expected, we won’t be able to use Saaripar without it!

compile 'com.mobsandgeeks:android-saripaar:2.0.2'

Once that’s done, we’re going to make a change to our GitApi class. We’re adding two lines -

@GET("/users/{user}")
public void getUser(@Path("user") String user,Callback<User> response);

As before, we’re making a GET request, but the difference this time is we’re also passing a ‘user’ String to our request (and so, the API) to get a JSON object encapsulating the user data. Our GitApi class, then, will look like

public interface GitApi {

    @GET("/repositories")
    public void getFeed(Callback<List<GitModel>> response);

    @GET("/users/{user}")
    public void getUser(@Path("user") String user,Callback<User> response);
}

But what is a ‘User’? Glad you asked, reader - that’s right, I’m talking to you. Our User class is going to be another Plain Old Java Object - just like our GitModel. Feel free to take a look at https://api.github.com/users/{some_user} if you don’t feel comfortable with JSON objects yet (you should feel comfortable, it was in Part One. You never listen to me.), but for now I’m going to assume you understand how this is supposed to go and just show my User code below:

    public class User {

    @Expose
    private String login;
    @Expose
    private String avatar_url;
    @Expose
    private String name;
    @Expose
    private String email;
    @Expose
    private String public_repos;
    @Expose
    private String public_gists;
    @Expose
    private int followers;
    @Expose
    private int following;

    public String getLogin(){
        return login;
    }
    public String getAvatar(){
        return avatar_url;
    }
    public String getName(){
        return name;
    }
    public String getEmail(){
        return email;
    }
    public String getPublicRepos(){
        return public_repos;
    }
    public String getPublicGists(){
        return public_gists;
    }
    public int getFollowers(){
        return followers;
    }
    public int getFollowing(){
        return following;
    }


    }

You’re probably wondering why I chose to @Expose the attributes I did, well, that’s because I wanted to display the following information -

  • Avatar
  • Username
  • Full name
  • Email
  • Number of public repositories
  • Number of public gists
  • Number of followers
  • Number of repositories followed

Which means our XML file, user_profile.xml, is going to look like this

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

    <RelativeLayout
        android:id="@+id/header"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/profile_image"
            android:layout_width="fill_parent"
            android:layout_marginTop="57dp"
            android:layout_height="wrap_content"/>

        <Button
            android:id="@+id/cancel_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:layout_alignParentTop="true"
            android:layout_weight="1"
            android:text="Cancel" />

        <Button android:id="@+id/done_button"
            style="?android:textAppearanceSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_alignParentEnd="true"
            android:layout_weight="1"
            android:text="Done" />

        </RelativeLayout>

    <TextView
        android:id="@+id/username"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/header"
        android:text="Username"/>

    <EditText
        android:id="@+id/username_edit"
        android:layout_width="400dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/username"
        android:background="@drawable/editbox"/>

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/username_edit"
        android:text="Name"/>

    <EditText
        android:id="@+id/name_edit"
        android:layout_width="400dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/name"
        android:background="@drawable/editbox"/>

    <TextView
        android:id="@+id/email"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/name_edit"
        android:text="Email"/>

    <EditText
        android:id="@+id/email_edit"
        android:layout_width="400dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/email"
        android:background="@drawable/editbox"/>

    />

    <TextView
        android:id="@+id/public_repos"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/email_edit"
        android:text="Public Repositories"/>

    <EditText
        android:id="@+id/public_repos_edit"
        android:layout_width="400dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/public_repos"
        android:background="@drawable/editbox"/>

    <TextView
        android:id="@+id/public_gists"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/public_repos_edit"
        android:text="Public Gists"/>

    <EditText
        android:id="@+id/public_gists_edit"
        android:layout_width="400dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/public_gists"
        android:background="@drawable/editbox"/>

    <TextView
        android:id="@+id/followers"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/public_gists_edit"
        android:text="Followers"/>

    <EditText
        android:id="@+id/followers_edit"
        android:layout_width="400dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/followers"
        android:background="@drawable/editbox"/>


    <TextView
        android:id="@+id/following"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/followers_edit"
        android:text="Following"/>

    <EditText
        android:id="@+id/following_edit"
        android:layout_width="400dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="25dp"
        android:layout_below="@+id/following"
        android:background="@drawable/editbox"/>
    </RelativeLayout>

That is, one EditText and TextView for each field. We’ve also created a ‘header’ consisting of two Buttons and an avatar ImageView - the Cancel button will cancel the User Profile view, returning us to our comfy ListView, and the Done button will validate our forms and return us to the ListView if validation passes. The ImageView is to showcase the owners’ pretty faces.

Of course, we’re going to be @Bind-ing all of these Views.

We also need to set up our validation! Writing validations manually is tedious and messy - Saaripar handles both the validation side (with annotations - so tidy) and the error message side (a pain to do by hand).

So let’s do it!

First, our activity needs to implement the Validator.ValidationListener interface, like so -

public class UserProfile extends Activity implements Validator.ValidationListener {
...
}

We also need to instantiate a Validator object

Validator validator;

Then, we need to @Override two methods - onValidationSucceeded() and onValidationFailed(). In case it isn’t clear, the two methods determine what happens if validation succeeds and what happens if it fails. Here are my implementations -

@Override
public void onValidationSucceeded() {
    Toast.makeText(this, "Yay! we got it right!", Toast.LENGTH_SHORT).show();
    finish();
}

@Override
public void onValidationFailed(List<ValidationError> errors) {
    for (ValidationError error : errors) {
        View view = error.getView();
        String message = error.getCollatedErrorMessage(this);

        // Display error messages ;)
        if (view instanceof EditText) {
            ((EditText) view).setError(message);
        } else {
            Toast.makeText(this, message, Toast.LENGTH_LONG).show();
        }
    }
}

What is a Toast? It can be poorly explained a number of ways (hot bread, drunken speech, spicy verbal disrespect) but the best explanation I found is straight from the Android Developer’s Guide, which at least gives poor explanations certified by professionals.

A toast provides simple feedback about an operation in a small popup. It only fills the amount of space required for the message and the current activity remains visible and interactive.

I couldn’t have said it better myself (I’m not qualified). In any case, a congratulatory Toast will pop up if validation succeeds, and will violently fail to continue if it does not (get toasted bro, but not in a delicious way).

The last thing we need to use Saaripar’s powerful validation is to instantiate a new Validator and set a ValidationListener with our Context after onCreate(). That’s achieved with two tiny lines of code:

validator = new Validator(this);
validator.setValidationListener(this);

Notice we use ‘this’ instead of getApplicationContext() - that would still work, but that method call is really only necessary if we want to have the context of the entire application - which we don’t. Whoa. TMI.

We’re also going to want to call the

validator.validate();

method on the Done button click, as well as close out the application on the Cancel button click. That code is below.

cancel_button.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
       finish();
          }
 });

done_button.setOnClickListener(new View.OnClickListener() {
     public void onClick(View v) {
          validator.validate();
                }
 });

So, now that we have the all the bones in place, how does Saaripar actually validate these Views? In the sickest, cleanest, most readable way ever - with annotations.

Annotate THIS!

There’s a whole list of possible validation rules you can set up with Saaripar, and you can make custom ones too. For my application, though, I just wanted to make sure there were no empty fields and that the email field wasn’t invalid. The two relevant annotations are @NotEmpty and @Email, and all you have to do is place them above your EditText views (just nestle them right on top of the ButterKnife @Binds - aren’t Android libraries amazing?). It should look something like this:

@Bind(R.id.profile_image)
ImageView image;

@NotEmpty
@Bind(R.id.username_edit)
EditText username_edit;

@NotEmpty
@Bind(R.id.name_edit)
EditText name_edit;

@NotEmpty
@Email
@Bind(R.id.email_edit)
EditText email_edit;

@NotEmpty
@Bind(R.id.public_repos_edit)
EditText public_repos_edit;

@NotEmpty
@Bind(R.id.public_gists_edit)
EditText public_gists_edit;

@NotEmpty
@Bind(R.id.followers_edit)
EditText followers_edit;

@NotEmpty
@Bind(R.id.following_edit)
EditText following_edit;

Don’t forget we also need to instantiate a GitApi call and get our Callback response! I chose to set everything only if the call was successful (that includes the Buttons) which may be bad but this is my blog post and that’s what you get for reading it.

Putting it all together (finally), our User Profile Activity looks like…

public class UserProfile extends Activity implements Validator.ValidationListener {
    static final String API = "https://api.github.com";        //BASE URL
    Validator validator;


    @Bind(R.id.profile_image)
    ImageView image;

    @NotEmpty
    @Bind(R.id.username_edit)
    EditText username_edit;

    @NotEmpty
    @Bind(R.id.name_edit)
    EditText name_edit;

    @NotEmpty
    @Email
    @Bind(R.id.email_edit)
    EditText email_edit;

    @NotEmpty
    @Bind(R.id.public_repos_edit)
    EditText public_repos_edit;

    @NotEmpty
    @Bind(R.id.public_gists_edit)
    EditText public_gists_edit;

    @NotEmpty
    @Bind(R.id.followers_edit)
    EditText followers_edit;

    @NotEmpty
    @Bind(R.id.following_edit)
    EditText following_edit;


    @Bind(R.id.done_button)
    Button done_button;

    @Bind(R.id.cancel_button)
    Button cancel_button;

    @Override
    public void onValidationSucceeded() {
        Toast.makeText(this, "Yay! we got it right!", Toast.LENGTH_SHORT).show();
        finish();
    }

    @Override
    public void onValidationFailed(List<ValidationError> errors) {
        for (ValidationError error : errors) {
            View view = error.getView();
            String message = error.getCollatedErrorMessage(this);

            // Display error messages ;)
            if (view instanceof EditText) {
                ((EditText) view).setError(message);
            } else {
                Toast.makeText(this, message, Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.user_profile);
        ButterKnife.bind(this);

        validator = new Validator(this);
        validator.setValidationListener(this);
        // Code


        Intent i = getIntent();
        String name = i.getStringExtra("name");

        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(API).build();                                        //create an adapter for retrofit with base url

        GitApi git = restAdapter.create(GitApi.class);


        git.getUser(name, new Callback<User>() {
            @Override
            public void failure(RetrofitError error) {

            }

            @Override
            public void success(User user, Response response) {

                Picasso.with(getApplicationContext())
                        .load(user.getAvatar())
                        .into(image);
                username_edit.setText(user.getLogin());
                name_edit.setText(user.getName());
                email_edit.setText(user.getEmail());
                public_repos_edit.setText(user.getPublicRepos());
                public_gists_edit.setText(user.getPublicGists());
                followers_edit.setText(String.valueOf(user.getFollowers()));
                following_edit.setText(String.valueOf(user.getFollowing()));
                cancel_button.setOnClickListener(new View.OnClickListener() {
                    public void onClick(View v) {
                        finish();
                    }
                });

                done_button.setOnClickListener(new View.OnClickListener() {
                    public void onClick(View v) {
                        validator.validate();
                    }
                });

            }


        });
    }
}

As you can see, we set all of our views in the success() method of our API call. You may also have noticed that we get grab the username from the Intent used to create the Activity (remember?).

Don’t forget to add the Activity to the AndroidManifest!

<activity android:name=".UserProfile"
android:label="User Profile"></activity>

By the way, that’s the last time we touch our AndroidManifest, so I’ve included the whole thing here, just in case you’re confused -

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="course.examples.retrofittest" >
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".SingleListItem"
            android:label="Single Item Selected"></activity>
        <activity android:name=".UserProfile"
            android:label="User Profile"></activity>
    </application>

</manifest>

Wow. This is getting into one huge blog post, and we’re not even done yet.

So we have our Activities ready to go, layouts cocked and onCreates blazing - how do we put it all together?

Let’s take a look at our ListViewAdapter.

The Final Stretch

Because we want each ListView element to have its own Activity instantiations and clickable zones, we’re not going to be able to implement this functionality within our MainActivity. Instead, we’re going to configure and set our clickable elements on ListView inflation.

Before we get too deep into our ListViewAdapter, we do need to make a tiny change to our MainActivity. Recall that our ListViewAdapter constructor takes two parameters - a List and an Inflater. If we want to make buttons that can call Activities, we’re also going to need to pass in a Context.

But, rather than having to pass in three arguments, we can use our newfound Context knowledge to change things up a little bit!

We can change our previous Adapter instantiation from this…

listAdapter = new ListViewAdapter(inflater, placeholderModel);

to this…

listAdapter = new ListViewAdapter(placeholderModel,this);

!

We only need to pass the List and the Context into the Adapter creation within MainActivity. Then, in our ListViewAdapter…

public class ListViewAdapter extends BaseAdapter {

    LayoutInflater inflater;
    List<GitModel> gitModelList;
    Context context;

    public ListViewAdapter(List<GitModel> gitModelList, Context context){
        this.inflater = LayoutInflater.from(context);
        this.gitModelList = gitModelList;
        this.context = context;
    }

we can create an Inflater right then and there from the input Context! That means we can do away with the inflater variable within the MainActivity entirely.

In order to make our ListView items clickable (we’re going to have the User Profile open when the avatar image is clicked and the Repo WebView open when the text is clicked. Fun fact: I had it the other way around this whole time and only just now realized how ridiculously unintuitive it was), we need to set the android:clickable attribute to true in our XML.

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="https://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp"
        android:background="@android:color/white">
        <ImageView
            android:id="@+id/image_in_item"
            android:layout_width="100dp"
            android:clickable="true"
            android:layout_height="100dp"/>
        <TextView
    android:id="@+id/textview_in_item"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textSize="18sp"
    android:clickable="true"
    android:layout_toRightOf="@+id/image_in_item"
    android:layout_marginLeft="10dp"/>
    <TextView
        android:id="@+id/textview_in_item_two"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:textSize="12sp"
        android:layout_toRightOf="@+id/image_in_item"
        android:clickable="true"
        android:layout_below="@+id/textview_in_item"
        android:layout_marginLeft="10dp"/>
    </RelativeLayout>

We also need to set an onClickListener to each of these Views, as well as create our own custom onClickListener!

We’re going to create the onClickListener within our ListViewAdapter class so we can use the same data model, position, and Views.

    private class MyOnClickListener implements View.OnClickListener {
        List<GitModel> gitModelList;
        int position;

        private MyOnClickListener(List<GitModel> gitModelList, int position) {
            this.gitModelList = gitModelList;
            this.position = position;
        }

        @Override
        public void onClick(View view) {

            if (view instanceof ImageView) {
                String name = gitModelList.get(position).getOwner().getLogin();

       // Launching new Activity on selecting User Profile Item
                Intent i = new Intent(context, UserProfile.class);
                // sending data to new activity
                i.putExtra("name", name);
                view.getContext().startActivity(i);

            } else if(view instanceof TextView){
                String url = gitModelList.get(position).getOwner().getHtml_url();

            // Launching new Activity on selecting single List Item
                Intent i = new Intent(context, SingleListItem.class);
                // sending data to new activity
                i.putExtra("url", url);
                view.getContext().startActivity(i);

            }

        }
    }

As you can see, based on the type of View that is being clicked, the behaviour changes (also note that, for our WebView, we pass the URL into the Intent, but for our User Profile we pass the username). This lets us use the same onClickListener for different Views.

All that’s left is for us to set our onClickListeners in our getView method!

holder.image.setOnClickListener(new MyOnClickListener(gitModelList, position));
holder.text.setOnClickListener(new MyOnClickListener(gitModelList, position));
holder.texttwo.setOnClickListener(new MyOnClickListener(gitModelList, position));

Put all together, our ListViewAdapter class looks like this:

public class ListViewAdapter extends BaseAdapter {

    LayoutInflater inflater;
    List<GitModel> gitModelList;
    Context context;

    public ListViewAdapter(List<GitModel> gitModelList, Context context){
        this.inflater = LayoutInflater.from(context);
        this.gitModelList = gitModelList;
        this.context = context;
    }

    @Override
    public int getCount() {
        return gitModelList.size();
    }

    @Override
    public Object getItem(int position) {
        return gitModelList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder holder;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.activity_list_view, parent, false);
            holder = new ViewHolder(convertView);
            convertView.setTag(holder);

        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        Picasso.with(inflater.getContext())
                .load(gitModelList.get(position).getOwner().getAvatar_url()) //""+gitmodelList.get(position).getOwner().getAvatar_url()
                .into(holder.image);

        holder.text.setText(" Name: "+gitModelList.get(position).getName()
                +"\t id: "+gitModelList.get(position).getId()+"\n");

        holder.texttwo.setText(gitModelList.get(position).getOwner().getAvatar_url());

        holder.image.setOnClickListener(new MyOnClickListener(gitModelList, position));
        holder.text.setOnClickListener(new MyOnClickListener(gitModelList, position));
        holder.texttwo.setOnClickListener(new MyOnClickListener(gitModelList, position));
        return convertView;

    }
    static class ViewHolder{
        @Bind(R.id.image_in_item)
        ImageView image;
        @Bind(R.id.textview_in_item)
        TextView text;
        @Bind(R.id.textview_in_item_two)
        TextView texttwo;

        public ViewHolder(View view){
            ButterKnife.bind(this, view);
        }
    }

    public void setGitmodel(List<GitModel> gitModelList){
        clearGitmodel();
        this.gitModelList = gitModelList;
        notifyDataSetChanged();
    }

    public void clearGitmodel(){
        this.gitModelList.clear();

    }

    private class MyOnClickListener implements View.OnClickListener {
        List<GitModel> gitModelList;
        int position;

        private MyOnClickListener(List<GitModel> gitModelList, int position) {
            this.gitModelList = gitModelList;
            this.position = position;
        }

        @Override
        public void onClick(View view) {

            DialogTask loadDialog = new DialogTask(gitModelList,position,view);

            loadDialog.execute();
            if (view instanceof ImageView) {

        String name = gitModelList.get(position).getOwner().getLogin();

                // Launching new Activity on selecting single List Item
                Intent i = new Intent(context, UserProfile.class);
                // sending data to new activity
                i.putExtra("name", name);
                view.getContext().startActivity(i);



            } else if(view instanceof TextView){
               String url = gitModelList.get(position).getOwner().getHtml_url();

                // Launching new Activity on selecting single List Item
                Intent i = new Intent(context, SingleListItem.class);
                // sending data to new activity
                i.putExtra("url", url);
                view.getContext().startActivity(i);
           }

        }
    }

    }

And that is that!

Wow. What an absolute monster of a blog post. Next time, we’re going to add what is arguably one of the most important aspects of the user experience - polish!

Join us next time, for Android Adventures Part Four: Wall of Text 2 - Wall Harder.

Feature Photo credit: Poppywright / CC BY-NC-ND