Android Material List - SQLite - ADD RETRIEVE UPDATE DELETE - Master Detail

October 23, 2017 Oclemy Android Material List, Android SQLite, Android Activity 15 minutes, 17 seconds

Android Material List Master Detail SQlite tutorial right here. How to ADD UPDATE DELETE RETRIEVE data to and from SQLite database.

Introduction

In the previous tutorials, we've been looking at using material list to construct nice cards easily. We had previously seen how to bind a simple array with images and text to our material list. We then saw how to construct master detail example with master view showing our material list and the detail view for performing our CRUD operations.

So in this example we'll see how to use a database. We insert data to sqlite database from our crud activity.When the onResume of the master activity is called we refresh data. We also see how to edit existing data by opeing crud activity and also how to delete data.

We will buiding our input forms programmatically using Form-Master library, alibrary that enables you build bigger forms with headers easily programmatically. We'll furthermore use masterial list to display nice cardviews and rushorm to manipulate our database easily.

Common Questions this example explores

  • Android Material List CRUD master detail example with SQLite database.
  • How to INSERT SELECT UPDATE DELETE data to and from sqite data to a list.
  • How to build froms programmatically using FormMaster and input sqlite data.
  • Android SQlite example with multiple activities.
  • How to insert datetime from datetimepicker to sqlite database in android

Tools Used

This example was written with the following tools:

  • Windows 8
  • AndroidStudio IDE
  • Genymotion Emulator
  • Language : Java
  • Topic : Material List CRUD, Material List Master Detail, Activity master detail

Libaries Used

Lets jump directly to the source code.

1. AndroidManifest.xml

  • We are using SQLite database with RushORM.
  • Hence we use AndroidManifest.xml to define the meta data for our sqlite database.
  • These include database name, database class package(where our database model belongs), database version etc.
  • Take note that we have registered our MainApplication.java class in the name attribute of element, as shown below. So don't forget that since its at the MainApplication where we will initialize our RushORM configurations.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tutorials.hp.materialsqlite">

    <application
        android:name=".MainApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".CrudActivity"></activity>
        <!--
     - We are using SQLite database with RushORM.
     - Hence we use AndroidManifest.xml to define the meta data for our sqlite database.
     - These include database name, database class package(where our database model belongs), database version etc.
     -->
        <!-- Updating this will cause a database upgrade -->
        <meta-data android:name="Rush_db_version" android:value="3" />

        <!-- Database name -->
        <meta-data android:name="Rush_db_name" android:value="GalaxiesDB.db" />

        <!-- Setting this to true will cause a migration to happen every launch,
        this is very handy during development although could cause data loss -->
        <meta-data android:name="Rush_debug" android:value="false" />

        <!-- Setting this to true mean that tables will only be created of classes that
        extend RushObject and are annotated with @RushTableAnnotation -->
        <meta-data android:name="Rush_requires_table_annotation" android:value="false" />

        <!-- Turning on logging can be done by settings this value to true -->
        <meta-data android:name="Rush_log" android:value="false" />

</application>

</manifest>

 

2. Build.Gradle App Level

  • Our app level build.gradle file.
  • We specify compilesdk,minimum sdk,target sdk and dependencies.
  • Note that the minimum sdk for this project isn't that strict,it is much lower than that specified below.
  • We also add dependencies using 'compile' statement.
  • Our activity shall derive from the appCompatActivity to make it target earlier android versions.
  • We specify material list which will allows us easily build awesome cards.
  • We also add FormMaster that will allow us build forms easily via java instead of xml.Note that FormMaster requires a minimum of API 16 as the minimum sdk version so specify it as below.
  • Finally we add the RushORM dependency, which is a third party library. We'll use it for sqlite database as it abstracts away sql statements.
apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.0"
    defaultConfig {
        applicationId "com.tutorials.hp.materialsqlite"
        minSdkVersion 16
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:26.+'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha7'
    compile 'com.android.support:design:26.+'
    compile 'com.github.dexafree:materiallist:3.2.1'
    compile 'me.riddhimanadib.form-master:form-master:1.0.2'
    compile 'co.uk.rushorm:rushandroid:1.3.0'

}

 

3. MainApplication.java

  • MainApplication class.
  • Extends android.app.Application.
  • This is a global class available to all classes within your project.
  • We initialize our RushORM configuration here using the initialize() method.
package com.tutorials.hp.materialsqlite;

import android.app.Application;
import com.tutorials.hp.materialsqlite.mData.Galaxy;
import java.util.ArrayList;
import java.util.List;
import co.uk.rushorm.android.AndroidInitializeConfig;
import co.uk.rushorm.core.Rush;
import co.uk.rushorm.core.RushCore;

public class MainApplication extends Application {
    /*
    when our app is created.
     */
    @Override
    public void onCreate() {
        super.onCreate();
        // Rush is initialized asynchronously to recieve a callback after it initialized
        // set an InitializeListener on the config object
        // All classes that extend RushObject or implement Rush must be added to the config
        List<Class<? extends Rush>> rushClasses = new ArrayList<>();
        rushClasses.add(Galaxy.class);

        //CONFIGURE AND INITIALIZE RUSHORM
        AndroidInitializeConfig config=new AndroidInitializeConfig(getApplicationContext(),rushClasses);
        RushCore.initialize(config);
    }
}

4. Galaxy.java

  • Our Galaxy class.
  • Is our data object.
  • Derives from RushObject. This makes it special in that our ORM will translate it to a sqlite database table.
  • It will inherit methods like save,delete,update,select etc and fields like id.
package com.tutorials.hp.materialsqlite.mData;

import java.util.Date;

import co.uk.rushorm.core.RushObject;

public class Galaxy extends RushObject {

    private String name,description;
    private String date;

    public Galaxy(){}

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public String getDate() {
        return date;
    }
    public void setDate(String date) {
        this.date = date;
    }

}

 

5. MyDataManager.java

  • Our MyDataManager class.
  • We perfom all our SQLite CRUD operations here.
  • We INSERT,SELECT,UPDATE,DELETE and FIND.
  • We are using RushORM as our Object Relational Mapper.
package com.tutorials.hp.materialsqlite.mData;
import java.util.ArrayList;
import java.util.List;

import co.uk.rushorm.core.RushSearch;

public class MyDataManager {

    /*
  - Retrieve SQLite data using the RushSearch().find() method.
  - This returns a list of Galaxies.
   */
    public static ArrayList<Galaxy> getGalaxies() {
        List<Galaxy> galaxies = new RushSearch().find(Galaxy.class);
        return (ArrayList<Galaxy>) galaxies;
    }
    /*
    - Save Galaxy to sqlite database by calling save() method.
     */
    public static boolean add(Galaxy g) {
        try {
            g.save();
            return true;
        }catch (Exception e)
        {
            e.printStackTrace();
            return false;
        }
    }

    /*
    - UPDATE SQLITE DATA GIVEN OLD GALAXY ID AND NEW GALAXY
     */
    public static boolean update(String id,Galaxy newGalaxy)
    {
        try
        {
            Galaxy g = new RushSearch().whereId(id).findSingle(Galaxy.class);
            g.setName(newGalaxy.getName());
            g.setDescription(newGalaxy.getDescription());
            g.save();

            return true;
        }catch (Exception e)
        {
            e.printStackTrace();
            return false;
        }

    }
    /*
    - FIND SINGLE GALAXY FROM SQLITE GIVEN ID
     */
    public static Galaxy find(String id)
    {
        try
        {
            Galaxy g = new RushSearch().whereId(id).findSingle(Galaxy.class);
            return g;
        }catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }

    /*
    - DELETE FROM SQLITE GIVEN THE GALAXY
     */
    public static boolean delete(Galaxy galaxy)
    {
        try
        {
            Galaxy g = new RushSearch().whereId(galaxy.getId()).findSingle(Galaxy.class);
            g.delete();
            return true;
        }catch (Exception e)
        {
            e.printStackTrace();
            return false;
        }
    }
}

6. MainActivity.java

  • Our Mainactivity class.
  • Derives from appcompatactivity.
  • Will be our master view. It will contain a material listview to display our list of cards.
  • Each card will comprise image,texts and two action buttons.
  • New will be for opening crud activity for adding new data while edit will be for opening it for editing existing data.
  • If it's for editing, we pass the id of the selected object to the crud activity.
package com.tutorials.hp.materialsqlite;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.view.Gravity;
import android.view.View;
import com.dexafree.materialList.card.Card;
import com.dexafree.materialList.card.CardProvider;
import com.dexafree.materialList.card.OnActionClickListener;
import com.dexafree.materialList.card.action.TextViewAction;
import com.dexafree.materialList.view.MaterialListView;
import com.squareup.picasso.RequestCreator;
import com.tutorials.hp.materialsqlite.mData.Galaxy;
import com.tutorials.hp.materialsqlite.mData.MyDataManager;
import java.util.Random;

public class MainActivity extends AppCompatActivity {
    MaterialListView materialListView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        this.initializeViews();
    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent i = new Intent(MainActivity.this, CrudActivity.class);
                Context c = MainActivity.this;
                c.startActivity(i);
            }
        });
    }

    /*
    - Initialize Material ListView
     */
    private void initializeViews() {
        materialListView = (MaterialListView) findViewById(R.id.material_listview);
    }

    /*
    - Bind data from database to Material ListView
     */
    private void bindData() {
        materialListView.getAdapter().clearAll();
        if(MyDataManager.getGalaxies().size()>0)
        {
            for (Galaxy g : MyDataManager.getGalaxies()) {
                this.createCard(g);
            }
        }

    }

    /*
    - Get random image to show in list from drawables
     */
    private int getRandomImage()
    {
        int[] images={R.drawable.andromeda,R.drawable.cartwheel,R.drawable.canismajoroverdensity,R.drawable.centaurusa};
        int image=new Random().nextInt(images.length);
        return images[image];
    }

    /*
    - Create a Card and add to Material List.
    - Pass the galaxy to display
     */
    private void createCard(final Galaxy g) {
        Card card = new Card.Builder(this)
                .withProvider(new CardProvider())
                .setLayout(R.layout.material_basic_image_buttons_card_layout)
                //.setLayout(R.layout.material_image_with_buttons_card)
                //.setLayout(R.layout.material_basic_buttons_card)
                //.setLayout(R.layout.material_welcome_card_layout)
                //.setLayout(R.layout.material_small_image_card)
                //.setLayout(R.layout.material_big_image_card_layout)
                .setTitle(g.getName())
                .setTitleGravity(Gravity.CENTER_HORIZONTAL)
                .setSubtitle(g.getDate())
                .setDescription(g.getDescription())
                .setDescriptionGravity(Gravity.CENTER_HORIZONTAL)
                .setDrawable(getRandomImage())
                .setDrawableConfiguration(new CardProvider.OnImageConfigListener() {
                    @Override
                    public void onImageConfigure(@NonNull RequestCreator requestCreator) {
                        //requestCreator.fit();
                        requestCreator.resize(121,121);
                    }
                })
                .addAction(R.id.left_text_button, new TextViewAction(this)
                        .setText("New")
                        .setTextResourceColor(R.color.colorPrimary)
                        .setListener(new OnActionClickListener() {
                            @Override
                            public void onActionClicked(View view, Card card) {
                                Intent i = new Intent(MainActivity.this, CrudActivity.class);
                                Context c = MainActivity.this;
                                c.startActivity(i);
                            }
                        }))
                .addAction(R.id.right_text_button, new TextViewAction(this)
                        .setText("Edit")
                        .setTextResourceColor(R.color.orange_button)
                        .setListener(new OnActionClickListener() {
                            @Override
                            public void onActionClicked(View view, Card card) {
                                Intent i = new Intent(MainActivity.this, CrudActivity.class);
                                i.putExtra("KEY_GALAXY", g.getId());
                                Context c = MainActivity.this;
                                c.startActivity(i);
                            }
                        }))
                .endConfig()
                .build();

        materialListView.getAdapter().add(card);
    }

    /*
    - When activity is resumed
     */
    @Override
    protected void onResume() {
        super.onResume();
        this.bindData();
    }
}

 

7. CrudActivity.java

  • Our CrudActivity class.
  • We perform our UI crud operation here. That is we get data from our forms and talk to MyDataManager to interact with db.
  • This activity is special in that we use it for both adding new data and editing existing data. Also we use it for deleting.
  • This is easy in that we only need to what the user clicked in the master activity and render our forms appropriately.
  • Hence a boolean is passed to us from mainactivity showing whether it is for adding or editing.
package com.tutorials.hp.materialsqlite;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.tutorials.hp.materialsqlite.mData.Galaxy;
import com.tutorials.hp.materialsqlite.mData.MyDataManager;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import me.riddhimanadib.formmaster.helper.FormBuildHelper;
import me.riddhimanadib.formmaster.model.FormElement;
import me.riddhimanadib.formmaster.model.FormHeader;
import me.riddhimanadib.formmaster.model.FormObject;

public class CrudActivity extends AppCompatActivity {

    RecyclerView mRecyclerView;
    FormBuildHelper mFormBuilder;
    Button saveBtn,deleteBtn;
    Galaxy galaxy;
    String id=null;
    Boolean isForUpdate = true;

    /*
    - When activity is created
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_crud);

        this.receiveData();
        this.initializeViews();
    }

    private void initializeViews() {
        // initialize variables
        mRecyclerView = (RecyclerView) findViewById(R.id.newRecyclerView);
        saveBtn = (Button) findViewById(R.id.newBtn);
        deleteBtn = (Button) findViewById(R.id.deleteBtn);

        deleteBtn.setEnabled(isForUpdate);

        mFormBuilder = new FormBuildHelper(this, mRecyclerView);

        // declare form elements
        FormHeader header = FormHeader.createInstance().setTitle("Galaxy Info");
        final FormElement nameEditText = FormElement.createInstance().setType(FormElement.TYPE_EDITTEXT_TEXT_SINGLELINE).setTitle("Galaxy").setHint("Galaxy Name....");
        final FormElement descEditText = FormElement.createInstance().setType(FormElement.TYPE_EDITTEXT_TEXT_MULTILINE).setTitle("Description").setHint("Galaxy Description....");
        final FormElement publishDate = FormElement.createInstance().setType(FormElement.TYPE_PICKER_DATE).setTitle("Date");

        if (isForUpdate) {
            nameEditText.setValue(galaxy.getName());
            descEditText.setValue(galaxy.getDescription());
            publishDate.setValue(galaxy.getDate());
        }
        // add them in a list
        List<FormObject> formItems = new ArrayList<>();
        formItems.add(header);
        formItems.add(nameEditText);
        formItems.add(descEditText);
    formItems.add(publishDate);

        // build and display the form
        mFormBuilder.addFormElements(formItems);
        mFormBuilder.refreshView();

        //INSERT DATA OR UPDATE EXISTING DATA
        saveBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (!isForUpdate) {
                    //INSERT NEW DATA
                    Galaxy galaxy = new Galaxy();
                    galaxy.setName(nameEditText.getValue());
                    galaxy.setDescription(descEditText.getValue());
                    galaxy.setDate(publishDate.getValue());
                    if(MyDataManager.add(galaxy))
                    {
                        nameEditText.setValue("");
                        descEditText.setValue("");
            publishDate.setValue("");
                        mFormBuilder.refreshView();
                        Toast.makeText(getApplicationContext(), "Successfully Saved Data", Toast.LENGTH_SHORT).show();

                    }else {
                        Toast.makeText(getApplicationContext(), "Unable To Insert data", Toast.LENGTH_SHORT).show();
                    }

                } else {

                    if (galaxy != null) {
                        //UPDATE EXISTING DATA
                        Galaxy newGalaxy=new Galaxy();
                        newGalaxy.setName(nameEditText.getValue());
                        newGalaxy.setDescription(descEditText.getValue());
                        newGalaxy.setDate(publishDate.getValue());
                        if(MyDataManager.update(galaxy.getId(),newGalaxy))
                        {
                            Toast.makeText(getApplicationContext(), "Updated Successfully", Toast.LENGTH_SHORT).show();

                        }else {
                            Toast.makeText(getApplicationContext(), "Unable To Update", Toast.LENGTH_SHORT).show();

                        }

                    } else {
                        Toast.makeText(getApplicationContext(), "Galaxy is null.", Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });
        //DELETE DATA FROMSQLITE DATABASE
        deleteBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(isForUpdate)
                {
                    if (galaxy != null)
                    {
                        if(MyDataManager.delete(galaxy))
                        {
                            nameEditText.setValue("");
                            descEditText.setValue("");
              publishDate.setValue("");
                            mFormBuilder.refreshView();

                            deleteBtn.setEnabled(isForUpdate=false);
                            saveBtn.setEnabled(false);
                            Toast.makeText(getApplicationContext(), "Deleted Successfully.", Toast.LENGTH_SHORT).show();
                        }else {
                            Toast.makeText(getApplicationContext(), "Unable To Delete", Toast.LENGTH_SHORT).show();

                        }

                    }
                }
            }
        });
    }

    /*
    - Receive data from MainActivity
     */
    private void receiveData() {
        try {
            Intent i = this.getIntent();
            id=i.getStringExtra("KEY_GALAXY");
            if(id == null)
            {
                isForUpdate=false;
            }else
            {
                galaxy=MyDataManager.find(id);
            }

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

8. ActivityMain.xml

  • ActivityMain.xml.
  • This is a template layout for our MainActivity.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.tutorials.hp.materialsqlite.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        app:srcCompat="@drawable/add_green" />

</android.support.design.widget.CoordinatorLayout>

9. ContentMain.xml

  • ContenMain.xml.
  • Contains our Material ListView which shall display our list of cards.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.tutorials.hp.materialsqlite.MainActivity"
    tools:showIn="@layout/activity_main">

        <com.dexafree.materialList.view.MaterialListView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:id="@+id/material_listview"/>

</android.support.constraint.ConstraintLayout>

 

10. activity_crud.xml

  • Our activity_crud.xml layout.
  • Root tag is constraintlayout.
  • Contains a RecyclerView and two buttons.
  • We will use the RecyclerView to build our input forms using the Form-Master library.
  • Delete button will delete data. Save button will both add new data and update existing data.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.tutorials.hp.materialsqlite.CrudActivity">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:id="@+id/newRecyclerView"
            android:descendantFocusability="beforeDescendants" />
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <Button
                android:id="@+id/newBtn"
                android:text="Save"
                android:background="@color/colorPrimary"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            <Button
                android:id="@+id/deleteBtn"
                android:text="Delete"
                android:background="@color/colorAccent"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </LinearLayout>
</android.support.constraint.ConstraintLayout>

How To Run

  1. Download the project.
  2. You'll get a zipped file,extract it.
  3. Open the Android Studio.
  4. Now close, already open project.
  5. From the Menu bar click on File >New> Import Project.
  6. Now Choose a Destination Folder, from where you want to import project.
  7. Choose an Android Project.
  8. Now Click on “OK“.
  9. Done, your done importing the project,now edit it.

More Resources

Resource Link
GitHub Browse Browse
GitHub Download Link Download

Best Regards, Oclemy.

Comments