Posts

Android Calculator Tutorial and Source Code Example

android calculator tutorial

What if there’s an easy way to use a calculator for your android app? That would be great! Right? Well, I’ll try to help you guys with that.

Our post for today is about an android calculator tutorial and after the tutorial is the source code download, available in zip format.

Just a brief background on how this calculator works, we will have a TextView in which the user can click on. After clicking it, our calculator will be shown using a pop up or dialog.

The contents of this post include:

Read more

How To Get the User’s Current Location in Android? – GPS Example Help

Today I’m sharing you a working code on how to get the user’s current location in Android. There are many popular apps using this feature such as Google Maps and Facebook. Remember when you checked in a place?

Knowing exactly where your users are located is really cool, but you always have to let them know you are doing it. Asking permission to get user’s location is an ethical way.

One advantage of getting user’s current location is customizing their experience, just like what Twitter does. If you let twitter know your location, it will show top trends or news near you, how cool is that?

how to get user's current location in android?

Read more

Android Navigation Drawer Example Step by Step

android navigation drawer horizontal open

Android Navigation drawer is now a standard when creating an Android app.

It gives a better user experience since the user can easily access more app functionality – on a small screen smartphone.

Personally, it is really fun to use since I just have to swipe a finger to use or access a view.

This post is a step by step guide on how to create a navigation drawer for your Android app.

Read more

Android Action Bar Tutorial with 2.2+ Compatibility

I have an Android Action Bar Tutorial for you guys! Android ActionBar looks good on our smartphone apps and at the same time, it is really useful. Aside from displaying your app’s title or section, the ActionBar can also be used as a back function (upper left) or a viewable button or dropdown options (upper right).

android action bar tutorial

This is a step by step guide on how you can create an ActionBar for your awesome Android app. I’ll walk you through the process of creating a very simple but awesome android ActionBar. Let’s get started!

Read more

Android SQLite Transaction Example with INSERT Prepared Statement

Today I’m going to share an Android SQLite transaction example that I consider as one of the most useful test I made with Android SQLite. I’m really excited to share this since it helped me a lot and maybe it can help some more people as well.

This post will cover the following contents:

1.0 The Back Story
2.0 Insert Speed Problem
3.0 Android SQLite Transaction Example Source Code
4.0 Application Code Output
5.0 Other Tips On Improving Insert Speed

1.0 The Back Story

Recently, my app was required to download 30,000 records during sync. I think that’s a lot of data for a phone app, but that’s the way our app is.

The data were from a URL with data in JSON format. Our Android app has to read, parse and store the data on the device SQLite database.

30,000 records in one URL load is not advisable, we did several tests. I tried to parse it but failed, memory leaks occur, sometimes it was an out of memory error. So I tried some more test until I found the correct number of records per URL. 7,000 records and our app was able to read and parse it all. But to be safer, I made it to 5,000 records per page.

We had to paginate the download, so in our case, we had 6 pages. 6 pages x 5,000 records = 30,000. So yeah, it was very effective. All records were downloaded and inserted to the device SQLite database.

2.0 Insert Speed Problem

But before we were able to efficiently insert the records to the database, we run into the problem of “insert speed”. The usual insert command in Android is slow, so we had to use a transaction and prepared statement.

In our case, we use INSERT OR REPLACE INTO on the insert query since we want to update a row if it already exists, based on the trigger (INDEX) created.

If you’re using INSERT OR REPLACE INTO command, you have to create a trigger. This SQL trigger is executed after the table has been created (see DatabaseHandler.java below)

Another important factor in speeding up your insert is using prepared statements.

3.0 Android SQLite Transaction Example Source Code

Our source code will have three main files, the MainActivity.java, DatabaseHandler.java and activity_main.xml. I made the code as simple as possible for this example to be clear and easy to follow.

DOWNLOAD SOURCE CODE

MainActivity.java – our program’s first run, it also contains the AsyncTask that will be executed when the user clicks a button.

package com.example.androidsqlitetransaction;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity {

    final String TAG = "MainActivity.java";
    EditText editTextRecordNum;
    TextView tvStatus;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        View.OnClickListener handler = new View.OnClickListener() {
            public void onClick(View v) {

                switch (v.getId()) {

                case R.id.buttonNormalInsert:
                    new AsyncInsertData("normal").execute();
                    break;
                case R.id.buttonFastInsert:
                    new AsyncInsertData("fast").execute();
                    break;
                }
            }
        };

        // EditText for entering desired number of records to be inserted
        editTextRecordNum = (EditText) findViewById(R.id.editTextRecordNum);
        
        // Button for normal and fast insert
        findViewById(R.id.buttonNormalInsert).setOnClickListener(handler);
        findViewById(R.id.buttonFastInsert).setOnClickListener(handler);
        
        // status TextView
        tvStatus = (TextView) findViewById(R.id.textViewStatus);

    }

    // we used AsyncTask so it won't block the UI thread during inserts.
    class AsyncInsertData extends AsyncTask<String, String, String> {

        DatabaseHandler databaseHandler;
        String type;
        long timeElapsed;
        
        protected AsyncInsertData(String type){
            this.type  = type;
            this.databaseHandler = new DatabaseHandler(MainActivity.this);
        }
        
        // @type - can be 'normal' or 'fast'
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            tvStatus.setText("Inserting " + editTextRecordNum.getText() + " records...");
        }

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

            try {

                // get number of records to be inserted
                int insertCount = Integer.parseInt(editTextRecordNum.getText().toString());
                
                // empty the table
                databaseHandler.deleteRecords();

                // keep track of execution time
                long lStartTime = System.nanoTime();
                
                if (type.equals("normal")) {
                    databaseHandler.insertNormal(insertCount);
                } else {
                    databaseHandler.insertFast(insertCount);
                }

                // execution finised
                long lEndTime = System.nanoTime();

                // display execution time
                timeElapsed = lEndTime - lStartTime;
                
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        protected void onPostExecute(String unused) {
            tvStatus.setText("Done inserting " + databaseHandler.countRecords() + " records. Time elapsed: " + timeElapsed / 1000000 + " ms."); 
        }
        
    }
}

DatabaseHandler.java – handles the database operations such as table creation, emptying the database, counting database records and the inserting our data using a loop.

package com.example.androidsqlitetransaction;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
import android.util.Log;

public class DatabaseHandler extends SQLiteOpenHelper {

    // for our logs
    public static final String TAG = "DatabaseHandler.java";

    // database version
    private static final int DATABASE_VERSION = 7;

    // database name
    protected static final String DATABASE_NAME = "NinjaDatabase2";

    // table details
    public String tableName = "locations";
    public String fieldObjectId = "id";
    public String fieldObjectName = "name";
    public String fieldObjectDescription = "description";
    
    // constructor
    public DatabaseHandler(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // creating table
    @Override
    public void onCreate(SQLiteDatabase db) {

        String sql = "";

        sql += "CREATE TABLE " + tableName;
        sql += " ( ";
        sql += fieldObjectId + " INTEGER PRIMARY KEY AUTOINCREMENT, ";
        sql += fieldObjectName + " TEXT, ";
        sql += fieldObjectDescription + " TEXT ";
        sql += " ) ";

        db.execSQL(sql);

        // create the index for our INSERT OR REPLACE INTO statement.
        // this acts as the WHERE name="name input" AND description="description input"
        // if that WHERE clause is true, I mean, it finds the same name and description in the database,
        // it will be REPLACEd. 
        // ELSE, what's in the database will remain and the input will be INSERTed (new record)
        String INDEX = "CREATE UNIQUE INDEX locations_index ON " 
                        + tableName + " (name, description)";
        
        db.execSQL(INDEX);
    }

    
    // When upgrading the database, it will drop the current table and recreate.
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        String sql = "DROP TABLE IF EXISTS " + tableName;
        db.execSQL(sql);

        onCreate(db);
    }

    // insert data using transaction and prepared statement
    public void insertFast(int insertCount) {

        // you can use INSERT only
        String sql = "INSERT OR REPLACE INTO " + tableName + " ( name, description ) VALUES ( ?, ? )";
        
        SQLiteDatabase db = this.getWritableDatabase();
        
        /*
         * According to the docs http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
         * Writers should use beginTransactionNonExclusive() or beginTransactionWithListenerNonExclusive(SQLiteTransactionListener) 
         * to start a transaction. Non-exclusive mode allows database file to be in readable by other threads executing queries.
         */
        db.beginTransactionNonExclusive();
        // db.beginTransaction();
        
        SQLiteStatement stmt = db.compileStatement(sql);
        
        for(int x=1; x<=insertCount; x++){
            
            stmt.bindString(1, "Name # " + x);
            stmt.bindString(2, "Description # " + x);
            
            stmt.execute();
            stmt.clearBindings();
            
        }

        db.setTransactionSuccessful();
        db.endTransaction();
        
        db.close();
    }
    
    // inserts the record without using transaction and prepare statement
    public void insertNormal(int insertCount){
        try{
            
            SQLiteDatabase db = this.getWritableDatabase();
            
            for(int x=1; x<=insertCount; x++){
                
                ContentValues values = new ContentValues();
                values.put(fieldObjectName, "Name # " + x);
                values.put(fieldObjectDescription, "Description # " + x);
                
                db.insert(tableName, null, values);
                
            }
            
            db.close();
            
        }catch(Exception e){
            e.printStackTrace();
        } 
    }
    
    // deletes all records
    public void deleteRecords(){
        
        SQLiteDatabase db = this.getWritableDatabase();
        db.execSQL("delete from "+ tableName);
        db.close();
    }
    
    // count records
    public int countRecords(){
        
        SQLiteDatabase db = this.getWritableDatabase();
        
        Cursor cursor = db.rawQuery("SELECT count(*) from " + tableName, null);
        cursor.moveToFirst();
        
        int recCount = cursor.getInt(0);
        
        cursor.close();
        db.close();
        
        return recCount;
    }
    
}

activity_main.xml – the layout so we can enter the desired number of records to be inserted, choose whether we want it to be a ‘normal’ or ‘fast’ insert, and the status of the operation.

<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/editTextRecordNum"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:inputType="number"
        android:singleLine="true"
        android:ems="10" >

        <requestFocus />
    </EditText>

    <Button
        android:id="@+id/buttonNormalInsert"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/editTextRecordNum"
        android:layout_below="@+id/editTextRecordNum"
        android:text="Normal Insert" />

    <Button
        android:id="@+id/buttonFastInsert"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/buttonNormalInsert"
        android:layout_alignBottom="@+id/buttonNormalInsert"
        android:layout_toRightOf="@+id/buttonNormalInsert"
        android:text="Fast Insert" />

    <TextView
        android:id="@+id/textViewStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/buttonNormalInsert"
        android:layout_below="@+id/buttonNormalInsert"
        android:padding="10dp"
        android:text="Status" />

</RelativeLayout>

4.0 Android SQLite Transaction Example Code Output

See our code’s output screenshots below…

When you entered 1000 as number of records to be inserted and pressed either the “Normal Insert” or “Fast Insert” button.

android sqlite transaction example

After inserting 1000 the “Normal Insert” way.

the normal insert way

After inserting 1000 the “Fast Insert” way.

the fast insert way - sqlite prepared statement

See the huge difference in insert speed? Inserting 1,000 records were from 54,615 milliseconds (almost 1 minute) down to 322 milliseconds!

5.0 Other Tips On Improving Insert Speed

Some other important points:

1. Do not put a “log” inside a loop, it affects the program execution and slows down the performance. For instance, Log.v(TAG, “Record was created.”); was inside a loop where you also insert the data.

2. Do not instantiate an object or class inside a loop, that’s the worst. The “new” keywords will slow down the performance. For instance, ValueConverter valueConverter = new ValueConverter(); will make you lonely for the rest of your life if it was inside a loop where you also insert the data.

If you have other thoughts or want to correct me, or something, please drop it in the comments section below. I’m willing to update this post for new information, ideas and tips that you can give. Thanks for reading this Android SQLite transaction example!

Android Intents Tutorial to Share Your Social Media Links

The following Android intents tutorial will help increase your app’s Facebook likes or twitter followers. Almost every app has their own social media accounts or links that you can like, follow, +1, or even send an email to. There’s always an app that can connect you to that product or service.

Android Intents Tutorial to Share Your Social Media Links

Now, from that app, a user must be able to view your accounts effortlessly and in a standard, native and beautiful way. Don’t just throw a browser with a link to a social media account, impress your users by opening it in the native app! The most commonly used social media apps are Facebook, Twitter and Google Plus.

Those are covered in this post, but with the additional Browser and GMail intents! For this code’s demo, see our Android Intents video demo below.

Video Demo Permalink

DOWNLOAD APP

By the way, the app on the demo above is this blog’s official browser app. You might want to download it on Google Play. I didn’t have enough time to develop that app due to my busy schedule. But it is working fine, try it out!

Contents:

1.0 Facebook Intent in Android
2.0 Google Plus Intent in Android
3.0 Twitter Intent in Android
4.0 Browser Intent in Android
5.0 GMail Intent in Android
6.0 How to Check If An App Is Installed in Android?

1.0 Facebook Intent in Android

Here’s how my app show that Facebook page.
Step 1: Go to the FB page and get the Facebook page name using the URL, in my case, it is “CodeOfANinja”
https://www.facebook.com/CodeOfANinja
You can also get the Facebook page ID using the graph API URL, for example
http://graph.facebook.com/CodeOfANinja
…and see the “id”: “107786425949934″ on that JSON string.

Step 2: Use the method below on your Listener, just replace the Facebook page name or ID. You can also use some parameters on this method if you’re opening multiple Facebook pages.

public void openFacebookPage() {
Intent i = null;

try {
context.getPackageManager().getPackageInfo(“com.facebook.katana”, 0);

// replace 107786425949934 with your page ID
i = new Intent(Intent.ACTION_VIEW, Uri.parse(“fb://profile/107786425949934″));

} catch (Exception e) {

// replace CodeOfANinja with your page name
i = new Intent(Intent.ACTION_VIEW, Uri.parse(“https://www.facebook.com/CodeOfANinja”));
}

checkIfAppExists(i, “Facebook”);

}

2.0 Google Plus Intent in Android

Calling the official Google Plus app to show the Google+ page of your customers would be great. That’s one feature I already worked with and now I’m ready to show you the working code I used.
Here’s how I did it:
Step 1: Go to the Google+ Page and copy the page ID. In my case, it is: 101266899643014043497
https://plus.google.com/101266899643014043497/posts

Step 2: Add the following code in your file or utility class.

public void openGooglePlusPage(String googlePlusId){
Intent i = new Intent(Intent.ACTION_VIEW);
i.setClassName(“com.google.android.apps.plus”, “com.google.android.apps.plus.phone.UrlGatewayActivity”);
i.putExtra(“customAppUri”, googlePlusId);
startActivity(i);

checkIfAppExists(i, “Google Plus”);
}

How to use? Just call the function with the ID you got in step 1.

openGooglePlusPage("101266899643014043497");

3.0 Twitter Intent in Android

Here’s how my app show that Twitter page.
Step 1: Get the Twitter page username, in my case, it is “ninjazhai”
https://twitter.com/ninjazhai

Step 2: Add the following method on your project or utility class.

public void openTwitterPage(String username){
Intent i = null;

try {

// use the twitter app
i = new Intent(Intent.ACTION_VIEW, Uri.parse(“twitter://user?screen_name=” + username));

}catch (Exception e) {

// try to use other intent
i = new Intent(Intent.ACTION_VIEW, Uri.parse(“https://twitter.com/#!/” + username));
}

checkIfAppExists(i, “Twitter”);
}

How to use the method above? Remember the twitter username you got on step 1 and put code below on your click listener:

openTwitterPage("ninjazhai");

4.0 Browser Intent in Android

This time we are going to take a look at the browser intent filter in android. I used this one when I want my users have the ability to view the subscribe form of this blog. This code basically answers the question “How do I show a web page from my app to a web browser?”
Here’s the working code I used. Just follow this two step guide.
Step 1: Of course, prepare the URL you wanted to be shown. In my case, it was:
http://feedburner.google.com/fb/a/mailverify?uri=TheCodeOfANinja

Step 2: Add this method to your JAVA file or utility class.

public void openUrl_InBrowser(String url){
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));

checkIfAppExists(i, “Web Browser”);

}

How to use? easy, you can do it this way.

openUrl_InBrowser("http://feedburner.google.com/fb/a/mailverify?uri=TheCodeOfANinja");

5.0 Gmail Intent in Android

You can use this code when you want your users to have the ability to send email from your app, for example is the app feature where the users can send feedback, comments or suggestions directly to any given email address.

This code will open the Gmail App with the help of android intent filter.

Now you can add this code to your project or utility class.

public void openGmailApp(String[] emailAddresses, String subject, String text){
Intent i = new Intent(Intent.ACTION_VIEW);
i.setType(“plain/text”);
i.setClassName(“com.google.android.gm”, “com.google.android.gm.ComposeActivityGmail”);
i.putExtra(Intent.EXTRA_EMAIL, emailAddresses);
i.putExtra(Intent.EXTRA_SUBJECT, subject);
i.putExtra(Intent.EXTRA_TEXT, text);
checkIfAppExists(i, “Gmail”);

}

And you can use the method above this way:

// you can try to add more email in this array: new String[] { "ninjazhai30@gmail.com", "email2@yahoo.com" }
// if you're going to send the message to multiple people
openGmailApp(
new String[] { “ninjazhai30@gmail.com” },
“Feedback for The Code of a Ninja Programming Blog”,
“Sent using The Code of a Ninja Android App:nn“
);

6.0 How to Check If An App Is Installed in Android?

Before a specific app will run, we have to verify if that app exists or installed in the device. This will enable us to tell the user that a certain app must be installed before using a feature. This prevents showing an error or force close message, so it is good for the user experience. The method below will show us the way.

Checking if an app is installed in the Android device is required if you are going to start another applications’s intent. There are other ways to do this but the code below is what’s working for me.

I used and tested this code when I wanted to start an intent to some apps like:

  • Facebook
  • Twitter
  • Google Plus
  • Browser
  • Gmail
// method to check whether an app exists or not
public void checkIfAppExists(Intent appIntent, String appName){

if (appIntent.resolveActivity(context.getPackageManager()) != null) {

// start the activity if the app exists in the system
startActivity(appIntent);

} else {

// tell the user the app does not exist
Toast.makeText(context, appName + ” app does not exist!”, Toast.LENGTH_LONG).show();
}
}

Sample Usage: Below is the code I used when I want to open my google plus page with the Google+ official android app.

Intent i = new Intent(Intent.ACTION_VIEW);
i.setClassName("com.google.android.apps.plus", "com.google.android.apps.plus.phone.UrlGatewayActivity");
i.putExtra("customAppUri", "101266899643014043497");
startActivity(i);
checkIfAppExists(i, “Google Plus”);

You can read more about Android intents here.

Android AutocompleteTextView with Custom Adapter Example

Before this Android AutocompleteTextView with custom adapter example, we made the code on Android AutocompleteTextView with suggestions from SQLite database, that’s really fine and useful, but here’s another problem:

What if you want to customize the appearance of the drop-down suggestions?

This is where the custom ArrayAdapter will come to the scene. Using this post, you will be able to customize the look and feel of your AutocompleteTextView drop down – according to your app design theme or visual needs.

We will cover the following content:

1.0 AutocompleteTextView with Custom Adapter Video Demo
2.0 Android AutoComplete EditText Program Files
3.0 Android AutocompleteTextView with Custom Adapter Example Codes
3.1 to 3.8 Main Files Description with Codes Explained via Comments

DOWNLOAD SOURCE CODE

1.0 AutocompleteTextView with Custom Adapter Video Demo

Here’s a video I shoot so you can see the final output of our code for today.

2.0 Android AutoComplete EditText Program Files

You may click on each of the files below to see the code and some insights or explanations. File # 1 and 2 were found in your res/layout/ folder, the rest is of course in the src/your.package.name

  1. activity_main.xml
  2. list_view_row.xml
  3. MainActivity.java
  4. AutocompleteCustomArrayAdapter.java
  5. CustomAutoCompleteView.java
  6. DatabaseHandler.java
  7. CustomAutoCompleteTextChangedListener.java
  8. MyObject.java

3.0 Android AutocompleteTextView with Custom Adapter Example Codes

Now here’s the exciting part. We’ll dig deeper with each main files of the program, see the codes and some explanation. Enjoy!

3.1 activity_main.xml – see how our custom AutoCompleteTextView were put in the XML layout.

The tag looks like com.example.autocompletetextviewdb.CustomAutoCompleteView because it was referenced to our CustomAutoComputeView.java file.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="" />

    <com.example.autocompletetextviewcustomadapter.CustomAutoCompleteView
        android:id="@+id/myautocomplete"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:completionThreshold="1" >
    </com.example.autocompletetextviewcustomadapter.CustomAutoCompleteView>

</LinearLayout>

3.2 list_view_row.xml – helps customize the appearance of AutoCompleteTextView suggestions, you can add an ImageView or something suits your app theme or design.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="10dp" >

    <TextView
        android:id="@+id/textViewItem"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:text="Item name here..."
        android:textSize="15dp" />

</RelativeLayout>

3.3 MainActivity.java – where the program will try to insert sample data, initialize some variables and will be shown when you the program runs.

package com.example.autocompletetextviewcustomadapter;

import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class MainActivity extends Activity {

    /*
     * Change to type CustomAutoCompleteView instead of AutoCompleteTextView 
     * since we are extending to customize the view and disable filter
     * The same with the XML view, type will be CustomAutoCompleteView
     */
    CustomAutoCompleteView myAutoComplete;
    
    // adapter for auto-complete
    ArrayAdapter<MyObject> myAdapter;
    
    // for database operations
    DatabaseHandler databaseH;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        try{
            
            // instantiate database handler
            databaseH = new DatabaseHandler(MainActivity.this);
            
            // put sample data to database
            insertSampleData();
            
            // autocompletetextview is in activity_main.xml
            myAutoComplete = (CustomAutoCompleteView) findViewById(R.id.myautocomplete);
            
            myAutoComplete.setOnItemClickListener(new OnItemClickListener() {

                @Override
                public void onItemClick(AdapterView<?> parent, View arg1, int pos, long id) {
                    
                    RelativeLayout rl = (RelativeLayout) arg1;
                    TextView tv = (TextView) rl.getChildAt(0);
                    myAutoComplete.setText(tv.getText().toString());
                    
                }

            });
            
            // add the listener so it will tries to suggest while the user types
            myAutoComplete.addTextChangedListener(new CustomAutoCompleteTextChangedListener(this));
            
            // ObjectItemData has no value at first
            MyObject[] ObjectItemData = new MyObject[0];
            
            // set the custom ArrayAdapter
            myAdapter = new AutocompleteCustomArrayAdapter(this, R.layout.list_view_row_item, ObjectItemData);
            myAutoComplete.setAdapter(myAdapter);
        
        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void insertSampleData(){
        
        // CREATE
        databaseH.create( new MyObject("January") );
        databaseH.create( new MyObject("February") ); 
        databaseH.create( new MyObject("March") );
        databaseH.create( new MyObject("April") );
        databaseH.create( new MyObject("May") );
        databaseH.create( new MyObject("June") );
        databaseH.create( new MyObject("July") );
        databaseH.create( new MyObject("August") );
        databaseH.create( new MyObject("September") );
        databaseH.create( new MyObject("October") );
        databaseH.create( new MyObject("November") );
        databaseH.create( new MyObject("December") );
        databaseH.create( new MyObject("New Caledonia this is just to make and see if the text will go down") ); 
        databaseH.create( new MyObject("New Zealand this is just to make and see if the text will go down") );
        databaseH.create( new MyObject("Papua New Guinea this is just to make and see if the text will go down") );
        databaseH.create( new MyObject("COFFEE-1K") );
        databaseH.create( new MyObject("coffee raw") );
        databaseH.create( new MyObject("authentic COFFEE") );
        databaseH.create( new MyObject("k12-coffee") );
        databaseH.create( new MyObject("view coffee") );
        databaseH.create( new MyObject("Indian-coffee-two") );
        
    }
}

3.4 AutocompleteCustomArrayAdapter.java – where you can customize the appearance of the suggestion drop-down. If your suggestions will contain a large list, I suggest using a ViewHolder pattern to make it smooth.

package com.example.autocompletetextviewcustomadapter;

import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class AutocompleteCustomArrayAdapter extends ArrayAdapter<MyObject> {

    final String TAG = "AutocompleteCustomArrayAdapter.java";
        
    Context mContext;
    int layoutResourceId;
    MyObject data[] = null;

    public AutocompleteCustomArrayAdapter(Context mContext, int layoutResourceId, MyObject[] data) {

        super(mContext, layoutResourceId, data);
        
        this.layoutResourceId = layoutResourceId;
        this.mContext = mContext;
        this.data = data;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        
        try{
            
            /*
             * The convertView argument is essentially a "ScrapView" as described is Lucas post 
             * http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/
             * It will have a non-null value when ListView is asking you recycle the row layout. 
             * So, when convertView is not null, you should simply update its contents instead of inflating a new row layout.
             */
            if(convertView==null){
                // inflate the layout
                LayoutInflater inflater = ((MainActivity) mContext).getLayoutInflater();
                convertView = inflater.inflate(layoutResourceId, parent, false);
            }
            
            // object item based on the position
            MyObject objectItem = data[position];

            // get the TextView and then set the text (item name) and tag (item ID) values
            TextView textViewItem = (TextView) convertView.findViewById(R.id.textViewItem);
            textViewItem.setText(objectItem.objectName);
            
            // in case you want to add some style, you can do something like:
            textViewItem.setBackgroundColor(Color.CYAN);
            
        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return convertView;
        
    }
}

3.5 CustomAutoCompleteView.java – since we have to display everything the database gets, we have to display the AutoCompleteTextView’s default filtering function. This is also what was called in the XML layout. See activity_main.xml code above.

package com.example.autocompletetextviewcustomadapter;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.AutoCompleteTextView;

public class CustomAutoCompleteView extends AutoCompleteTextView {

    public CustomAutoCompleteView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }
    
    public CustomAutoCompleteView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    public CustomAutoCompleteView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
    }

    // this is how to disable AutoCompleteTextView filter
    @Override
    protected void performFiltering(final CharSequence text, final int keyCode) {
        String filterText = "";
        super.performFiltering(filterText, keyCode);
    }
}

3.6 DatabaseHandler.java – where we insert sample data and query the database for autocomplete suggestions.

package com.example.autocompletetextviewcustomadapter;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DatabaseHandler extends SQLiteOpenHelper {

    // for our logs
    public static final String TAG = "DatabaseHandler.java";

    // database version
    private static final int DATABASE_VERSION = 5;

    // database name
    protected static final String DATABASE_NAME = "NinjaDatabase2";

    // table details
    public String tableName = "locations";
    public String fieldObjectId = "id";
    public String fieldObjectName = "name";

    // constructor
    public DatabaseHandler(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // creating table
    @Override
    public void onCreate(SQLiteDatabase db) {

        String sql = "";

        sql += "CREATE TABLE " + tableName;
        sql += " ( ";
        sql += fieldObjectId + " INTEGER PRIMARY KEY AUTOINCREMENT, ";
        sql += fieldObjectName + " TEXT ";
        sql += " ) ";

        db.execSQL(sql);

    }

    
    // When upgrading the database, it will drop the current table and recreate.
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        String sql = "DROP TABLE IF EXISTS " + tableName;
        db.execSQL(sql);

        onCreate(db);
    }

    
    // create new record
    // @param myObj contains details to be added as single row.
    public boolean create(MyObject myObj) {

        boolean createSuccessful = false;

        if(!checkIfExists(myObj.objectName)){
                    
            SQLiteDatabase db = this.getWritableDatabase();
            
            ContentValues values = new ContentValues();
            values.put(fieldObjectName, myObj.objectName);
            createSuccessful = db.insert(tableName, null, values) > 0;
            
            db.close();
            
            if(createSuccessful){ 
                Log.e(TAG, myObj.objectName + " created.");
            }
        }
        
        return createSuccessful;
    }
    
    // check if a record exists so it won't insert the next time you run this code
    public boolean checkIfExists(String objectName){
        
        boolean recordExists = false;
                
        SQLiteDatabase db = this.getWritableDatabase();
        Cursor cursor = db.rawQuery("SELECT " + fieldObjectId + " FROM " + tableName + " WHERE " + fieldObjectName + " = '" + objectName + "'", null);
        
        if(cursor!=null) {
            
            if(cursor.getCount()>0) {
                recordExists = true;
            }
        }

        cursor.close();
        db.close();
        
        return recordExists;
    }

    // Read records related to the search term
    public MyObject[] read(String searchTerm) {

        // select query
        String sql = "";
        sql += "SELECT * FROM " + tableName;
        sql += " WHERE " + fieldObjectName + " LIKE '%" + searchTerm + "%'";
        sql += " ORDER BY " + fieldObjectId + " DESC";
        sql += " LIMIT 0,5";

        SQLiteDatabase db = this.getWritableDatabase();

        // execute the query
        Cursor cursor = db.rawQuery(sql, null);

        int recCount = cursor.getCount();
        
        MyObject[] ObjectItemData = new MyObject[recCount];
        int x = 0;
        
        // looping through all rows and adding to list
        if (cursor.moveToFirst()) {
            do {

                String objectName = cursor.getString(cursor.getColumnIndex(fieldObjectName));
                Log.e(TAG, "objectName: " + objectName);
                
                MyObject myObject = new MyObject(objectName);

                ObjectItemData[x] = myObject;
                
                x++;
                
            } while (cursor.moveToNext());
        }

        cursor.close();
        db.close();

        return ObjectItemData;
        
    }

}

3.7 CustomAutoCompleteTextChangedListener.java – each time the user types a character, it queries the database and updates our customized ArrayAdapter.

package com.example.autocompletetextviewcustomadapter;

import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;

public class CustomAutoCompleteTextChangedListener implements TextWatcher{

    public static final String TAG = "CustomAutoCompleteTextChangedListener.java";
    Context context;
    
    public CustomAutoCompleteTextChangedListener(Context context){
        this.context = context;
    }
    
    @Override
    public void afterTextChanged(Editable s) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void onTextChanged(CharSequence userInput, int start, int before, int count) {

        try{
            
            // if you want to see in the logcat what the user types
            Log.e(TAG, "User input: " + userInput);
    
            MainActivity mainActivity = ((MainActivity) context);
            
            // update the adapater
            mainActivity.myAdapter.notifyDataSetChanged();
            
            // get suggestions from the database
            MyObject[] myObjs = mainActivity.databaseH.read(userInput.toString());
            
            // update the adapter
            mainActivity.myAdapter = new AutocompleteCustomArrayAdapter(mainActivity, R.layout.list_view_row_item, myObjs);
            
            mainActivity.myAutoComplete.setAdapter(mainActivity.myAdapter);
            
        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    

}

3.8 MyObject.java – used for the sample data to appear in the auto-complete suggestion.

package com.example.autocompletetextviewcustomadapter;

public class MyObject {

    public String objectName;

    // constructor for adding sample data
    public MyObject(String objectName){
        
        this.objectName = objectName;
    }

}

I recommend reading more about Android AutocompleteTextView. Feel free to drop your questions or suggestions to improve this Android AutocompleteTextView with custom adapter example. Thanks!

Android AutocompleteTextView with Database Data as Suggestions

Our code example for today is about a very useful Android widget called AutocompleteTextView. Specifically, we’ll code an Android AutocompleteTextView with database data as drop-down suggestions.

If you’re not yet familiar with AutocompleteTextView, this widget looks like an EditText but shows completion suggestions automatically while the user is typing.

The list of suggestions is displayed in a drop down menu from which the user can choose an item to replace the content of the edit box with.

I recently played with this widget for my project, but it requires that the auto-complete suggestions must come from SQLite database. I know this could be helpful for your projects too.

In this post, we will cover the following content:

1.0 Android AutocompleteTextView with Database Video Demo
2.0 Android AutoComplete EditText Program Files
3.0 Android AutocompleteTextView with Database Example Codes
3.1 to 3.6 Main Files Description with Codes Explained via Comments

1.0 Android AutocompleteTextView with Database Video Demo

Visualizing our code output is important so here’s a video I shoot for you to show the code’s final output.

2.0 Android AutoComplete EditText Program Files

Our example project for today contains only 5 java files. Click the file names below to read its purpose and see the code inside.

  1. activity_main.xml
  2. MainActivity.java
  3. CustomAutoCompleteView.java
  4. DatabaseHandler.java
  5. CustomAutoCompleteTextChangedListener.java
  6. MyObject.java

3.0 Android AutocompleteTextView with Database Example Codes

3.1 activity_main.xml – see how the AutoCompleteTextView widget was put in the XML layout.

The tag looks like com.example.autocompletetextviewdb.CustomAutoCompleteView because it was referenced to our CustomAutoComputeView.java file.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="" />

    <com.example.autocompletetextviewdb.CustomAutoCompleteView
        android:id="@+id/myautocomplete"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:completionThreshold="1" >
    </com.example.autocompletetextviewdb.CustomAutoCompleteView>

</LinearLayout>

3.2 MainActivity.java – this is where we initialize everything and insert sample data to the database.

package com.example.autocompletetextviewdb;

import java.util.List;

import android.os.Bundle;
import android.app.Activity;
import android.widget.ArrayAdapter;

public class MainActivity extends Activity {

    /*
     * Change to type CustomAutoCompleteView instead of AutoCompleteTextView 
     * since we are extending to customize the view and disable filter
     * The same with the XML view, type will be CustomAutoCompleteView
     */
    CustomAutoCompleteView myAutoComplete;
    
    // adapter for auto-complete
    ArrayAdapter<String> myAdapter;
    
    // for database operations
    DatabaseHandler databaseH;
    
    // just to add some initial value
    String[] item = new String[] {"Please search..."};
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        try{
            
            // instantiate database handler
            databaseH = new DatabaseHandler(MainActivity.this);
            
            // put sample data to database
            insertSampleData();
            
            // autocompletetextview is in activity_main.xml
            myAutoComplete = (CustomAutoCompleteView) findViewById(R.id.myautocomplete);
            
            // add the listener so it will tries to suggest while the user types
            myAutoComplete.addTextChangedListener(new CustomAutoCompleteTextChangedListener(this));
            
            // set our adapter
            myAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, item);
            myAutoComplete.setAdapter(myAdapter);
        
        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void insertSampleData(){
        
        // CREATE
        databaseH.create( new MyObject("January") );
        databaseH.create( new MyObject("February") ); 
        databaseH.create( new MyObject("March") );
        databaseH.create( new MyObject("April") );
        databaseH.create( new MyObject("May") );
        databaseH.create( new MyObject("June") );
        databaseH.create( new MyObject("July") );
        databaseH.create( new MyObject("August") );
        databaseH.create( new MyObject("September") );
        databaseH.create( new MyObject("October") );
        databaseH.create( new MyObject("November") );
        databaseH.create( new MyObject("December") );
        databaseH.create( new MyObject("New Caledonia") ); 
        databaseH.create( new MyObject("New Zealand") );
        databaseH.create( new MyObject("Papua New Guinea") );
        databaseH.create( new MyObject("COFFEE-1K") );
        databaseH.create( new MyObject("coffee raw") );
        databaseH.create( new MyObject("authentic COFFEE") );
        databaseH.create( new MyObject("k12-coffee") );
        databaseH.create( new MyObject("view coffee") );
        databaseH.create( new MyObject("Indian-coffee-two") );
        
    }
    
    // this function is used in CustomAutoCompleteTextChangedListener.java
    public String[] getItemsFromDb(String searchTerm){
        
        // add items on the array dynamically
        List<MyObject> products = databaseH.read(searchTerm);
        int rowCount = products.size();
        
        String[] item = new String[rowCount];
        int x = 0;
        
        for (MyObject record : products) {
            
            item[x] = record.objectName;
            x++;
        }
        
        return item;
    }

}

3.3 CustomAutoCompleteView.java – since we want the AutocompleteTextView to work with a database, we must customize by extending it.

package com.example.autocompletetextviewdb;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.AutoCompleteTextView;

public class CustomAutoCompleteView extends AutoCompleteTextView {

    public CustomAutoCompleteView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }
    
    public CustomAutoCompleteView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    public CustomAutoCompleteView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
    }

    // this is how to disable AutoCompleteTextView filter
    @Override
    protected void performFiltering(final CharSequence text, final int keyCode) {
        String filterText = "";
        super.performFiltering(filterText, keyCode);
    }

    /*
     * after a selection we have to capture the new value and append to the existing text
     */
    @Override
    protected void replaceText(final CharSequence text) {
        super.replaceText(text);
    }

}

3.4 DatabaseHandler.java – as the name suggests, this is where we can create the database, tables, manipulate and query the data.

package com.example.autocompletetextviewdb;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DatabaseHandler extends SQLiteOpenHelper {

    // for our logs
    public static final String TAG = "DatabaseHandler.java";

    // database version
    private static final int DATABASE_VERSION = 4;

    // database name
    protected static final String DATABASE_NAME = "NinjaDatabase2";

    // table details
    public String tableName = "locations";
    public String fieldObjectId = "id";
    public String fieldObjectName = "name";

    // constructor
    public DatabaseHandler(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // creating table
    @Override
    public void onCreate(SQLiteDatabase db) {

        String sql = "";

        sql += "CREATE TABLE " + tableName;
        sql += " ( ";
        sql += fieldObjectId + " INTEGER PRIMARY KEY AUTOINCREMENT, ";
        sql += fieldObjectName + " TEXT ";
        sql += " ) ";

        db.execSQL(sql);

    }

    // When upgrading the database, it will drop the current table and recreate.
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        String sql = "DROP TABLE IF EXISTS " + tableName;
        db.execSQL(sql);

        onCreate(db);
    }

    // create new record
    // @param myObj contains details to be added as single row.
    public boolean create(MyObject myObj) {

        boolean createSuccessful = false;

        if(!checkIfExists(myObj.objectName)){
                    
            SQLiteDatabase db = this.getWritableDatabase();
            
            ContentValues values = new ContentValues();
            values.put(fieldObjectName, myObj.objectName);
            createSuccessful = db.insert(tableName, null, values) > 0;
            
            db.close();
            
            if(createSuccessful){ 
                Log.e(TAG, myObj.objectName + " created.");
            }
        }
        
        return createSuccessful;
    }
    
    // check if a record exists so it won't insert the next time you run this code
    public boolean checkIfExists(String objectName){
        
        boolean recordExists = false;
                
        SQLiteDatabase db = this.getWritableDatabase();
        Cursor cursor = db.rawQuery("SELECT " + fieldObjectId + " FROM " + tableName + " WHERE " + fieldObjectName + " = '" + objectName + "'", null);
        
        if(cursor!=null) {
            
            if(cursor.getCount()>0) {
                recordExists = true;
            }
        }

        cursor.close();
        db.close();
        
        return recordExists;
    }

    // Read records related to the search term
    public List<MyObject> read(String searchTerm) {

        List<MyObject> recordsList = new ArrayList<MyObject>();

        // select query
        String sql = "";
        sql += "SELECT * FROM " + tableName;
        sql += " WHERE " + fieldObjectName + " LIKE '%" + searchTerm + "%'";
        sql += " ORDER BY " + fieldObjectId + " DESC";
        sql += " LIMIT 0,5";

        SQLiteDatabase db = this.getWritableDatabase();

        // execute the query
        Cursor cursor = db.rawQuery(sql, null);

        // looping through all rows and adding to list
        if (cursor.moveToFirst()) {
            do {

                // int productId = Integer.parseInt(cursor.getString(cursor.getColumnIndex(fieldProductId)));
                String objectName = cursor.getString(cursor.getColumnIndex(fieldObjectName));
                MyObject myObject = new MyObject(objectName);

                // add to list
                recordsList.add(myObject);

            } while (cursor.moveToNext());
        }

        cursor.close();
        db.close();

        // return the list of records
        return recordsList;
    }

}

3.5 CustomAutoCompleteTextChangedListener.java – this is where the program requeries the database each time a user types a character on the AutocompleteTextView.

package com.example.autocompletetextviewdb;

import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.ArrayAdapter;

public class CustomAutoCompleteTextChangedListener implements TextWatcher{

    public static final String TAG = "CustomAutoCompleteTextChangedListener.java";
    Context context;
    
    public CustomAutoCompleteTextChangedListener(Context context){
        this.context = context;
    }
    
    @Override
    public void afterTextChanged(Editable s) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void onTextChanged(CharSequence userInput, int start, int before, int count) {

        // if you want to see in the logcat what the user types
        Log.e(TAG, "User input: " + userInput);

        MainActivity mainActivity = ((MainActivity) context);
        
        // query the database based on the user input
        mainActivity.item = mainActivity.getItemsFromDb(userInput.toString());
        
        // update the adapater
        mainActivity.myAdapter.notifyDataSetChanged();
        mainActivity.myAdapter = new ArrayAdapter<String>(mainActivity, android.R.layout.simple_dropdown_item_1line, mainActivity.item);
        mainActivity.myAutoComplete.setAdapter(mainActivity.myAdapter);
        
    }

}

3.6 MyObject.java – this is used for inserting sample data and other operations related to the database.

package com.example.autocompletetextviewdb;

public class MyObject {

    public String objectName;

    // constructor for adding sample data
    public MyObject(String objectName){
        
        this.objectName = objectName;
    }

}

On the next post, we’ll have this code with a custom ArrayAdapter so you can style the drop down that appears, see you! Feel free to drop a comment on this Android AutocompleteTextView with database example.