Friday, 30 December 2016

Transferring data with JSON

Javascript Object Notation, in short JSON is likely the most popular way to transfer data across different systems. Its syntax, based on JavaScript, makes it easy for humans to read it and it simplifies the way we can extract information from data

In order to demonstrate what we can do with JSON we need a server to query. I found this website which is set up exactly for our purpose. We will use the date and time service and we will need to send our request to this address:  http://date.jsontest.com.

The server will respond with the information requested in JSON format, like so:

1
2
3
4
5
{
   "time": "03:53:25 AM",
   "milliseconds_since_epoch": 1362196405309,
   "date": "03-02-2013"
}

This is pretty much what objects look like in JavaScript. Also, we can visualize this like an HashMap, where, for example, on line 2, the key "time" will return the value "03:53:25 AM".

In our application we simply want to display the time and the date using two TextViews.

All can be achieved with a single class which, following our last tutorial, will extend AsyncTask once again in order to make our request asynchronous

Let's have a look at the class itself, called JSONDownloader:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class JSONDownloader extends AsyncTask<String, Void, String> {

    Activity act;
    TextView time,date;

    public JSONDownloader(Activity act)
    {
        this.act = act;
        time = (TextView) act.findViewById(R.id.textTime);
        date = (TextView) act.findViewById(R.id.textDate);
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);

        try {

            JSONObject j = new JSONObject(s);

            time.setText(j.get("time").toString());
            date.setText(j.get("date").toString());

        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected String doInBackground(String... params) {

        String result = "";

        try {

            URL url = new URL(params[0]);
            HttpURLConnection connection;
            connection = (HttpURLConnection) url.openConnection();

            InputStream in = connection.getInputStream();
            InputStreamReader reader = new InputStreamReader(in);

            int data = reader.read();

            while(data!=-1)
            {
            char c = (char)data;
                result += c;
                data = reader.read();
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }
}

First thing first, the constructor. We simply pass in the Activity which we use to get our TextViews references.

In the doInBackground method we send the request: first we create an URL object passing the first paramter of our var args which is going to be the address I linked above, you will see it in a minute in the MainActivity class. Calling the method open() form this object we get our HttpURLConnection object, which is used to get our InputStream and, consequently, our InputStreamReader. All the data collected is ultimately put into a String  variable, called result which is the value returned by the function.

Our String now contains the information we wanted in JSON, so we need to find a way to extract what we need which, in our case, is a String containing the time information and one for the date.

Thankfully for us, Java has a very easy way to work with JSON with a built-in class of the same name.

As you can see in the onPostExecute method, we can create JSON object and pass in the constructor the String we just filled with the information.
Once we have the JSON object ready we can extrapolate values using the method get(..). You can see on lines 21 and 22 that, in order to get our date and time, we call that method passing in the keys "time" and "date", which are the 2 keys that need to be used to get our data as we saw on the website. At this point we are returned String values which we can place as texts in our TextViews.

Finally, in the MainActivity, we create the class object and execute the task:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        JSONDownloader d = new JSONDownloader(this);

        d.execute("http://date.jsontest.com/");
    }
}

Conclusion

A very simple and effective example on how to use JSON to obtain data from the internet. We can of course send data as well and that is something that I will perhaps show later. JSON is rapidly becoming the standard used to send data across different systems and you can already find many websites that have APIs and they offer a wide variety of services (like news, weather information and more) that you can use in your app simply by sending http request and use JSON to process the data.

Tuesday, 29 November 2016

Download an image from the web using AsyncTask

One of the powerful things we can do with Android devices is to connect to the cloud and get information from it. A very common task is to download content from the web, like an image for example.

In order to simplify the operation, Android Studio has a built in class that allows us to perform these tasks in the background, without interrupting the user experience.

I'm talking about the AsyncTask class.

This class is the prefect tool to use whenever we need to perform background tasks. It is a simpler and more efficient way to run code in a separate thread.

Let's start by selecting an image from the web, any is fine. By typing "universe" in google, this is one of the pictures that came up:



And this is its address: https://i.ytimg.com/vi/uSJSPRQdLd8/maxresdefault.jpg.

In our project, first thing we need to do is to add internet permission in the manifest:

1
<uses-permission android:name="android.permission.INTERNET"></uses-permission>

This is necessary of course as we are going to need to access the web to connect to the image link.

Next we create our layout. Very simple, we will only need an ImageView and a ProgressBar, which will be used to show the download progress to the user.

This is the XML file of our MainActivity:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

    <ProgressBar
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:id="@+id/progressBar"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

We can now check the DownloadImage class, which is a child of AsyncTask.

This is the class responsible of downloading the content.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class DownloadImage extends AsyncTask<String, Integer, Bitmap> {

    Activity act;
    ProgressBar bar;
    ImageView image;

    public DownloadImage(Activity act)
    {
        this.act = act;
        image = (ImageView) act.findViewById(R.id.imageView);
        bar = (ProgressBar) act.findViewById(R.id.progressBar);
    }
    
    @Override
    protected void onPostExecute(Bitmap bitmap) {   

        bar.setVisibility(View.GONE);
        image.setImageBitmap(bitmap);
        
    }

    @Override
    protected void onPreExecute() {

        Toast.makeText(act,"Downloading...",Toast.LENGTH_SHORT).show();
        bar.setVisibility(View.VISIBLE);
        bar.setMax(100);
    }

    @Override
    protected void onProgressUpdate(Integer... values) {

    bar.setProgress(values[0]);

    }

    @Override
    protected Bitmap doInBackground(String... params) {

        Bitmap bmp = null;

        int count = 0;

        ByteArrayOutputStream baos = new ByteArrayOutputStream(8192);

        try {

            URL myUrl = new URL(params[0]);
            HttpURLConnection con = (HttpURLConnection) myUrl.openConnection();
            con.setRequestProperty("Accept-Encoding", "identity");  //Allow to know file size through getContentLength
            con.connect();

            byte[] b = new byte[1024];
            InputStream in = con.getInputStream();

            int total = con.getContentLength();

            while((count = in.read(b))!= -1)
            {
                baos.write(b, 0, count);
               
                publishProgress(100*baos.toByteArray().length/total);
            }

            bmp = BitmapFactory.decodeByteArray(baos.toByteArray(),0,baos.toByteArray().length);

        } catch (MalformedURLException e) {

            e.printStackTrace();
        } catch (IOException e) {

            e.printStackTrace();
        }
        return bmp;
    }
}

First thing we notice is that the AsyncTask class uses generics. The three parameters assigned can be read as such: AsyncTask<Parameters,Progress,Result>.


  • Parameters is what is sent to the task to work with. In my case, it's a String, as I will pass the link to the image, in String format.
  • Progress is the type of variable used as parameters in the onProgressUpdate method. This value (or values) are usually passed to a PrograssBar or a ProgressDialog.
  • Result: is the returned type of the task. This type will be the parameter of the method onPostExecute, as we will soon see.
When we extend this class we must implement at least one method: doInBackground(...).

Even though this appears to be a method like many others, it actually runs on a separate thread. Whatever code is between its brackets, it will not interfere with the UI thread. As you can see on line 38, this method accepts a parameter of type String, as I described previously when discussing generics. Also, we can see that this method is of type Bitmap as this is what we are going to return as our result.

Before exploring the funtion in details, let's have a quick look at the other two overridden methods.

The first one is onPreExecute(). This function is invoked immediately before the doInBackground and it runs on the main thread. Therefore, here we can manipulate UI elements. In my app, I simply display a Toast to let the user know that downloading has started.

The other method is onPostExecute(...). This one, not surprisingly, run immediately after the background thread has returned.  The parameters passed in the method is whatever is returned by the doInBackground(...) method and, at this point, we can process the result. this method also runs on the UI thread. In our example, I made the progress bar disappear (line 26) and I set the bitmap to our ImageView.

Lastly, the onPublishProgress method, which simply takes the values passed in and applies them to our progress bar. This parameter is calculated in the doInBackground method and passed in invoking publishProgress(...).

Obviously, the doInBackground method is where the downloading occurs. To perform such a task I used a ByteArrayOutputStream, which will hold all the bytes of the image. In order to connect to the link, we first grab the String which is passed in as a parameter and create a URL with it (line 48).
.
The HttpUrlConnection is used to create the actual connection (line 49). The next line is used to allow us to know the size of the content we are linking to, in our case the image. This is going to be used soon to calculate the download progress.

After launching the connection, we create a byte array, which acts as our buffer and we get the InputStream. Also, we have a new variable, called total, which represents the content size.

Between line 58 and 63 there's a while loop which reads the content from the InputStream. We use the read(buffer) method of the input stream to get the first set of bytes which are converted into a int (count) and we check whether the value is -1: if that's the case, we have reached the end of the content. If not, we can simply write those bytes into our ByteArrayOutputStream. 

Finally, on line 62, we invoke the method publishProgress(...) which will send the values passed in (an int in our case) to the onPublishProgress method we overrode earlier. The value passed in is nothing but a percentage of the bytes read to the total size of the content.

Once the loop has completed the task, we create a bitmap and return it.

Last thing we need to do start the whole process from the MainActivity class. To do so, we simply need 2 lines of code:


1
2
DownloadImage di = new DownloadImage(this);
        di.execute("https://i.ytimg.com/vi/uSJSPRQdLd8/maxresdefault.jpg");

Place these lines in your OnCreate method of the activity and that is all. The execute method is used to invoke doInBackground. DO NOT call the method directly otherwise it will not run in a saparate thread as described before.

Below, a gif showing the result:



Conclusion

Accessing web content is one of the most powerful things we can do in Android. Using AsyncTask greatly simplifies the process also allowing us to cancel ongoing tasks. When using this class, some important things to remember:

  • doInBackground must be implemented
  • doInBackground runs on a separate thread, no UI elements should be edited here
  • onPostExecute and onPreExecute run on the main thread
  • All the actions that should be performed one the task is done should be placed inside the onPostExecute method: placing them somewhere else in the activity class could interfere with the main thread giving you unwanted results. 


Tuesday, 22 November 2016

The "Password Keeper" app

I put together a small app following the previous tutorials on database. This is a very simple application which will store all your accounts and password, just like the one we saw in the previous posts.

It is just a small project I had fun working on.




The app will allow you to store multiple password, edit them and will require a master password to access the whole content.

The APK file is downloadable here.

Friday, 11 November 2016

Notifications

Notifications are one of the ways the app can communicate information to the user. What makes notifications different is that they appear outside of the application UI.

In this simple example, we create a button which will send a notification to the user. Once the notification is clicked on, the device will go back to the main activity.

Let's take a quick look to the layout file first:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Get Notified!"
        android:id="@+id/notButton"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

This is the button I was talking about earlier.

The whole experiment is in a single class, our MainActivity:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    Button b;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        b = (Button)findViewById(R.id.notButton);

        b.setOnClickListener(this);
    }

    @SuppressLint("NewApi")
    void NotifyMe()
    {

        Intent intent = new Intent(this, MainActivity.class);

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);

        stackBuilder.addParentStack(MainActivity.class);

        stackBuilder.addNextIntent(intent);
        PendingIntent pIntent =stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);

        Notification not = new Notification.Builder(this)
                .setContentTitle("This is the notification title")
                .setContentText("This is the content text")
                .setSmallIcon(R.drawable.icon)
                 .setContentIntent(pIntent)
                 .setAutoCancel(true).build();

        NotificationManager nm =(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        nm.notify(0,not);

    }

    @Override
    public void onClick(View v) {

        if(v.getId() == R.id.notButton)
            NotifyMe();
    }
}

In the onCreate() I simply assign the Button reference and set its OnClickListener to the current class.

When the button is pressed, the method NotifyMe() is called.

In order to create a notification we use the Notification class and its Builder, in a similar way we create dialogs. While creating the notification we can assign other properties to it (line28 onwards), for example, a title, a message an icon, which will appear on the notification slider and a content intent. The latter is a pending intent called whenever the user clicks on the notification itself.

Observe where the title and text will appear in the notification slider:



The intent is created on line 19 and it is a standard way to spawn a new activity. In our case, the MainActivity. What happens after (line 21 - 26) is that we create an artificial stack for the activities we are launching  and we prevent the new activity to spawn on top of the other one. By doing so, once the user has pressed the notification and it's redirected to the main activity, if the back button is pressed, the application will shut down and the user will be sent to the home screen.

Finally, in order to execute the notification, we get the NotificationManager (line 35) and call the method notify(...). The parameters passed in this functions are: the notification id, which is used, for example, to cancel a notification through the notification manager, and the notification object itself, created previously.


Conclusion

Notifications are one of the most popular ways to notify the user. There are other things we can do to notifications, like apply expanded layouts or add inline reply actions, however, it is often enough to use simple notifications in order to communicate to the user, launching other activities to handle the following actions.

Thursday, 1 September 2016

Custom listeners with interfaces

In my little experiments, I often find myself using custom dialogs to display information to the user or to get input. Therefore, I needed a way to create a callback method to detect what the user selected once the dialog has been dismissed. In order to do so, we can use interfaces to create custom listeners.

This will allow us to create callback functions for custom dialogs and set listener objects that, well listen to what the user selected and react accordingly. Very much how you set an click listener for buttons.

In this episode we are going to create a custom dialog and set a listener in order to follow the choices made by the user.

Let's first create a layout for our dialog:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Main text"
        android:id="@+id/custom_dialog_textview"
        android:layout_gravity="center_horizontal" />

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Confirm"
            android:layout_weight="0.5"
            android:id="@+id/custom_dialog_confirmbutton" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Cancel"
            android:layout_weight="0.5"
            android:id="@+id/custom_dialog_cancelbutton" />
    </LinearLayout>
</LinearLayout>

In your design window, will look like so:

We can now go on and create a class called CustomDialog, which will be responsible for spawning our dialog to the screen.


I will post the class code below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class CustomDialog implements View.OnClickListener {

    ICustomDialogListener listener;

    Button confirmButton;
    Button cancelButton;
    TextView maintext;

    Dialog dialog;

    public enum ButtonResults
    {
        BUTTON_CONFIRM,
        BUTTON_CANCEL

    }

    public CustomDialog(Context c, String text,ICustomDialogListener listener)
    {

        this.listener = listener;

        dialog = new Dialog(c);

        dialog.setContentView(R.layout.custom_dialog_layout);
        dialog.setCancelable(false);

        WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
        Window dialogWin = dialog.getWindow();

        lp.copyFrom(dialogWin.getAttributes());

        lp.width = WindowManager.LayoutParams.MATCH_PARENT;

        dialogWin.setAttributes(lp);

        maintext = (TextView) dialog.findViewById(R.id.custom_dialog_textview);
        maintext.setText(text);

        confirmButton = (Button)dialog.findViewById(R.id.custom_dialog_confirmbutton);
        cancelButton = (Button)dialog.findViewById(R.id.custom_dialog_cancelbutton);

    confirmButton.setOnClickListener(this);
        cancelButton.setOnClickListener(this);

        dialog.show();
    }

    @Override
    public void onClick(View v) {

        switch (v.getId())
        {

            case R.id.custom_dialog_confirmbutton:
                listener.OnDialog(ButtonResults.BUTTON_CONFIRM,dialog);
                break;

            case R.id.custom_dialog_cancelbutton:
                listener.OnDialog(ButtonResults.BUTTON_CANCEL,dialog);
                break;
        }
    }
}

The class itself is pretty simple. The dialog is created in the constructor (line 23) and we pass it in the custom layout we previously set up (line25). From line 28 to line 37 we simply tell the dialog window to extend its width as far as the parent view. I found that MATCH_PARENT doesn't work well when set in the xml file, so this is how you can achieve the same thing in Java.

As you can see, the constructor accepts a parameter of type ICustomDialogListener, which is an interface used to define a listener for this dialog. The object listener is then notified of the button(s) pressed by the user in the onClick method on line 50. To identify which button has been pressed, I used an enumeration. To notify the listener, we call the interface method OnDialog which accepts an enumeration type variable, which identifies the button, and the dialog itself.

Below is the interface ICustomDialogListener:


1
2
3
4
public interface ICustomDialogListener {

    void OnDialog(CustomDialog.ButtonResults result, Dialog dialog);
}

Simple and efficient.

The advantage to use interfaces is that we can implement this in every class which displays a dialog and requires a callback. We can also use it in an anonymous class.

Here's an example. In the MainActivity, I jusst create a dialog in the onCreate method, so the window will pop up immediately as soon as the app boots. You will see that, according to which button is pressed, an appropriate toast message will appear.

This is the code for the onCreate method in MainActivity:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        CustomDialog dialog = new CustomDialog(MainActivity.this, "Hi", new ICustomDialogListener() {
            @Override
            public void OnDialog(CustomDialog.ButtonResults result, Dialog dialog) {

                switch (result)
                {
                    case BUTTON_CONFIRM:
                        Toast.makeText(MainActivity.this,"You pressed Confirm",Toast.LENGTH_SHORT).show();
                        break;

                    case BUTTON_CANCEL:
                        Toast.makeText(MainActivity.this,"You pressed Cancel",Toast.LENGTH_SHORT).show();
                        break;

                }

              
            }
        });

    }

Remember, the dialog will show because in the constructor of CustomDialog we call dialog.show() on line 46. If you omit that, nothing will show up.

As you can see, in the constructor call for the dialog, we create an anonymous class for the listener (line 8), so we can deal with the callback function straight in the onCreate method.

We could very much pass this, which will require the MainActivity to implement ICustomDialogInterface:

1
   CustomDialog dialog = new CustomDialog(MainActivity.this,"Hi",this);


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class MainActivity extends AppCompatActivity implements ICustomDialogListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

//Rest of the code

.........
........
.......
}

And you would have to implement the method OnDialog(...) required by the interfrace:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void OnDialog(CustomDialog.ButtonResults result, Dialog dialog) {

        switch(result)
        {
            case BUTTON_CONFIRM:
                Toast.makeText(MainActivity.this,"You pressed Confirm",Toast.LENGTH_SHORT).show();
                break;

            case BUTTON_CANCEL:
                Toast.makeText(MainActivity.this,"You pressed Cancel",Toast.LENGTH_SHORT).show();
                break;

        }

    }

This works exactly like when you set a listener for a button or any other view.

Just as I did in the anonymous class earlier, I use a switch to deal with the result obtained.

Below, a gif will show the final result.



Notice how the dialog is not dismissed after pressing a button. That is because we simply did not tell it to. If you need the dialog to disappear after the user has chosen, add the line dialog.dismiss() in the switch and there you go.

Conclusion

Creating a good callback system is a good way to maintain the code readable and organized. Using interfaces gives us great flexibility as we can implement as many as we want in a single class, in case you would need many different listeners. Of course, this concept can be applied to different scenarios. Perhaps you will need a callback method for a GPS app which will callback when the user has reached a particular location or you could use this technique in a database class, to notify a ListView that the database has been edited and it need to update its views.

Finally, you can also set multiple listeners. Instead of having a single variable listener in the CustomDialog class, we could use a static ArrayList<ICustomDialogListener>. In the constructor we would. It needs to be static of course, as we do not want a new list for each dialog. We can then add new listeners any time the constructor is called and, when it's time to notify them, we simply iterate through the list itself calling the method OnDialog(..) for each of its elements.

Wednesday, 24 August 2016

Display records with a ListView and custom layout

This post is a follow up of the previous one. In this section, we are going to fetch records from the database and place them in a ListView.

A ListView is nothing but a container of views that are stacked on each other. Imagine something like your Facebook posts on your mobile phones.

We can create custom views for each item to place in the list, with all the components we need, like buttons, layouts, edit texts and so on.

In fact, the first thing I will show oyu is our list_adapter_item.xml, which contains all the information on how to display our items in the list:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FACAAA">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Account title"
        android:id="@+id/textView8"
        android:layout_gravity="center_horizontal" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:id="@+id/list_adapter_item_title"
        android:layout_gravity="center_horizontal"
        android:text=".........."
        android:textStyle="bold|italic" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Account login"
        android:id="@+id/textView9"
        android:layout_gravity="center_horizontal" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text=".............."
        android:id="@+id/list_adapter_item_loginID"
        android:layout_gravity="center_horizontal"
        android:textStyle="bold|italic" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Account password"
        android:id="@+id/textView5"
        android:layout_gravity="center_horizontal" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="..........."
        android:id="@+id/list_adapter_item_psw"
        android:layout_gravity="center_horizontal"
        android:textStyle="bold|italic" />

</LinearLayout>

You may now behold my amazing design skills:


This is what each view inside our ListView is going to be displayed.

Before we  go on and create our list adapter class, let's place the actual list on the display.

Simply go to your content_main.xml and drag in a ListView. After changing the colors to match the previous layout file, this is what you should see:


Although very simple, I will post the actual xml code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:background="#FACAAA"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/activity_main" tools:context=".MainActivity">


    <ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/listView"
        android:layout_gravity="top"
        android:layout_weight="1" />
</LinearLayout>

We can now go on and create a custom list adapter. When we use a ListView it is necessary to set a listAdapter, which is a class that contains all the settings regarding how to display the actual items, including which layout to use. You could use the default layouts that are available in the system but, since we are going to use our own, we will create our custom class.

This is it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class MyListAdapter extends BaseAdapter {

    Context context;
    String[] titles;

    TextView title_text_view;
    TextView loginID_text_view;
    TextView psw_text_view;


    public MyListAdapter(Context context, String[] titles)
    {
        this.titles = titles;
        this.context = context;

       
    }
    @Override
    public int getCount() {
        return titles.length;
    }

    @Override
    public Object getItem(int position) {
        return titles[position];
    }

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

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

        View view;

        Account a = PswDatabase.getInstance().getAccountByTitle(titles[position],false);

        LayoutInflater inflater= (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.list_adapter_item,null);

        title_text_view = (TextView) view.findViewById(R.id.list_adapter_item_title);
        loginID_text_view = (TextView) view.findViewById(R.id.list_adapter_item_loginID);
        psw_text_view = (TextView) view.findViewById(R.id.list_adapter_item_psw);

        title_text_view.setText(a.getTitle());
        loginID_text_view.setText(a.getId());
        psw_text_view.setText(a.getThePassword());

        return view;
    }
}

The class must extend BaseAdapter in order to be used for a ListView.

Most of the inherited methods are pretty self explanatory, but the real stuff happens in the getView function. Here is where the custom view for each item is created. On lines 40 and 41, we use the inflater object to inflate our custom layout in the previously created view. Once the view is ready, we can finally get all the references to our texview.

For each item we have an index, position, and we use that value to get each account object in the database by its title (line 38). We then assign their respective strings (title, login, password) to the textviews. This will run for as many views we need, which are going to be as many as the title array's elements.

At this point all we have to do in get the ListView reference and assign the adapter.

In the MainActivity, create a method called UpdateList() which is called in the onCreate. The method will do what its names suggests it would do, which is updating the listview.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    void UpdateList()
    {
        ListView listView;
        listView = (ListView)findViewById(R.id.listView);
        String[] titles = new String[PswDatabase.getInstance().getTitlesList().size()];
        titles = PswDatabase.getInstance().getTitlesList().toArray(titles);
    
        MyListAdapter adapter = new MyListAdapter(MainActivity.this,titles);
        listView.setAdapter(adapter);

    }

We firstly get our list view reference form the layout. We then get the list of titles from the database. the method used was not implemented in the previous post, and it is displayed below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
 public List<String> getTitlesList()
    {
        List<String> titles = new ArrayList<String>();

        SQLiteDatabase db = getReadableDatabase();

        Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_NAME,null);

        if(cursor.moveToFirst())
        {
            do{

                String s = cursor.getString(1);
                titles.add(s);
            }while(cursor.moveToNext());
        }
        
        return titles;
    }

We should now ready to go. You should place a call to UpdateList every time the database is changed, so the list will be updated as well..

Imagine we had added 2 accounts in our database. Using our PswDatabase class we wrote last time, we would proceeed like this:

1
2
  Account a1 = new Account("Gmail account","myusername@gmail.com","mypassword");
  Account a2 = new Account("Youtube account","myyoutube username","myyoutubePassw0rd");

Once the list is updated, you should see this:


Conclusion

We should now have a working database with a ListView that displays the information to the user. This will conclude the topic. It wold be possible to add buttons to edit and delete each record. All you would have to do is to add the actual Button view in the layout, reference it in the MyListAdapter class, set an on click listener that calls the deleteAccountByTitle method in the database class. That simple.And of course, a better design would not hurt, but this is a good example on how to use a list view efficiently.