In part 2 we created a RecyclerView and, of course, the main Activity. That’s not too bad, but still not very useful, because we can not really use the app (yet, I promise). So this time we are going to implement another activity that shows all details of a lease and lets us create new and delete existing entities – it is basically close to be CRUD.
Originally this series was intended to have 3 parts, but during this part we realized the 3rd post is getting too long. Well, we’ll make it 4 parts…
- Moving session routines to Application
- New Activity
- Add onClick Event in RecyclerView
- Loading detail data
Moving session routines to Application
In part 2 we inserted some hacks to get things up and running quickly, but some of them would create more work in the future. One of them is we had our session in the Activity – we are now going to move it to the Application instance of our app. You might ask “where is my Application? I can’t find it..” – and that is perfectly right, we don’t have one, yet! So the first step is to create a class that extends android.app.Application. Right-click the package “com.devteam83.tutorials.leasegreendao” (or how you’ve called it), select “new” -> “Java class” and make it extend the a.m. class:
package com.devteam83.tutorials.leasegreendao; import android.app.Application; public class LeaseApplication extends Application { }
To enable this class to hold a reference to our session class, it needs a member, and that member has to become initialized somewhere. Android Studio helps us with this – please right-click somewhere in that class and select “Generate…” -> “Override Methods…” and select “onCreate” method from Application:
Neat! We just have to add the code from MainActivity here, and create a getter for daoSession (right-click -> generate.. etc… you know it! 😉 )so the full class looks as follows:
package com.devteam83.tutorials.leasegreendao; import android.app.Application; import android.database.sqlite.SQLiteDatabase; import com.devteam83.tutorials.leasegreendao.model.DaoMaster; import com.devteam83.tutorials.leasegreendao.model.DaoSession; public class LeaseApplication extends Application { public DaoSession daoSession; @Override public void onCreate() { super.onCreate(); DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "lease-db", null); SQLiteDatabase db = helper.getWritableDatabase(); DaoMaster daoMaster = new DaoMaster(db); daoSession = daoMaster.newSession(); } public DaoSession getDaoSession() { return daoSession; } }
Please pay attention to two things:
Please don’t remove the automatically generated call to super.onCreate();
Note in the last line “daoSession = daoMaster.newSession();”, in contrast to the code in MainActivity, we removed the class DaoSession in front of the attributes name daoSession. If we wouldn’t do so, we’d create a local attribute with scope limited to the onCreate-method. Not very useful here, so we use the member-attribute from the class.
From MainActivity we can remove the full block and insert one line (strikethrough = remove):
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "lease-db", null); SQLiteDatabase db = helper.getWritableDatabase(); DaoMaster daoMaster = new DaoMaster(db); DaoSession daoSession = daoMaster.newSession(); DaoSession daoSession = ((LeaseApplication) getApplicationContext()).getDaoSession();
Much better, isn’t it? Let’s try, if our app is still working. Click the “play”-button in the toolbar (but check, if left to it the run/debug configuration is set to “app”).
WTF? (By the way, this means “What a Terrible Failure” – if you don’t believe me look here 😉 )
We get:
Caused by: java.lang.ClassCastException: android.app.Application cannot be cast to com.devteam83.tutorials.leasegreendao.LeaseApplication at com.devteam83.tutorials.leasegreendao.MainActivity.onCreate(MainActivity.java:26) at android.app.Activity.performCreate(Activity.java:5937) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2251) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360) at android.app.ActivityThread.access$800(ActivityThread.java:144) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5221) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Exceptions? No!!! Yes – with a simple reason: we changed our application class and the app doesn’t know about it. You have to add android:name=”.LeaseApplication” to your AndroidManifest.xml. Mine now looks as follows:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.devteam83.tutorials.leasegreendao" > <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" android:name=".LeaseApplication"> <activity android:name=".MainActivity" android:label="@string/title_activity_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
After that everything should run smoothly.
New Activity
We show data of our leases in a new Activity we open from the RecyclerView, so I think the next logical step is creating this Activity. Again, right-click the package “com.devteam83.tutorials.leasegreendao” and select “New” -> “Activity” -> “Blank Activity” and set data in the wizard as follows:
Please switch to “Text” (Design? no!) and remove the TextView that was part of the generated layout. Instead please insert a LinearLayout (with android:orientation=”vertical”) and, in the LinearLayout, two EditTexts, a Spinner and two Buttons, so the file looks as follows:
<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="com.devteam83.tutorials.leasegreendao.LeaseActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:id="@+id/activity_lease_item" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/activity_lease_comment" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Spinner android:id="@+id/activity_lease_person" android:layout_width="match_parent" android:layout_height="wrap_content"></Spinner> <Button android:id="@+id/activity_lease_save" android:text="save" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/activity_lease_delete" android:text="delete" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> </RelativeLayout>
This is basically a repetition of former tasks, they are just fields to hold our data later. Please note the added IDs. If you switch to design-view you can already see the just added fields. Now we have to get references to these fields in LeaseActivity – I’ll do that quickly (if that is too quick please leave a comment – then I’ll explain it in more detail) and the result is the following (in LeaseActivity.java):
public class LeaseActivity extends Activity { public EditText editTextItem; public EditText editTextComment; public Spinner spinnerPerson; public Button buttonSave; public Button buttonDelete; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_lease); editTextItem = (EditText) findViewById(R.id.activity_lease_item); editTextComment = (EditText) findViewById(R.id.activity_lease_comment); spinnerPerson = (Spinner) findViewById(R.id.activity_lease_person); buttonSave = (Button) findViewById(R.id.activity_lease_save); buttonDelete = (Button) findViewById(R.id.activity_lease_delete); } ...
Add onClick Event in RecyclerView
At the moment we have two separate Activities that don’t know nothing about each other and we have no chance to get to see LeaseActivity on our emulator (or device, depends how you are developing). If you have worked with ListViews before, things have changed a bit since RecyclerView was there. The ClickListener now feels at home in the ViewHolder…
That means we are now going to work on MainActivityListAdapter.java – open the file and scroll to the bottom. We have to make our ViewHolder implement OnClickListener by changing the first line of it to:
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
Android Studio will immediately complain we have to implement methods, so we are doing that by, you guessed it, right-click -> “Generate…” -> “Override Methods…”. Select method “onClick(View v)” from class OnClickListener.
In that method we can now specify what to do, but there is a small problem:
The way everything works together does not make it easy for us to get references to all things we need. The best way would be to create an interface for handling click events, let MainActivity implement that interface and register MainActivity (more precise: something implementing the interface, in this case MainActivity) as listener in the onClick method.
In this tutorial, because interfacing is not the central topic of this series of posts, I’ll skip this and hard-wire the Activities, handlers and so on. Don’t do this at home – bad way of programming! 🙁 (THAT is boilerplate-code)
I poked references to MainActivity through to ViewHolder via MainActivityListAdapter, added an ID to the parent element of the items in RecyclerView (in activity_main_item.xml, to be able to attach the ClickListener to it) and implemented the event method in ViewHolder(onClick). Our Files look as follows now:
MainActivityListAdapter.java: package com.devteam83.tutorials.leasegreendao; import android.content.Intent; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.devteam83.tutorials.leasegreendao.model.Lease; import java.util.List; public class MainActivityListAdapter extends RecyclerView.Adapter<MainActivityListAdapter.ViewHolder>{ private List<Lease> mData; private MainActivity mainActivity; public MainActivityListAdapter(List<Lease> data, MainActivity mainActivity) { this.mData = data; this.mainActivity = mainActivity; } @Override public MainActivityListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // create a new view View itemLayoutView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.activity_main_item, null); // create ViewHolder ViewHolder viewHolder = new MainActivityListAdapter.ViewHolder(itemLayoutView, mainActivity); return viewHolder; } @Override public void onBindViewHolder(ViewHolder viewHolder, int position) { viewHolder.textViewItem.setText(mData .get(position) .getItem()); viewHolder.textViewPersonName.setText(mData .get(position) .getPerson() .getName()); viewHolder.leaseId = mData.get(position).getId(); } @Override public int getItemCount() { return mData.size(); } public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public long leaseId; public MainActivity mainActivity; public TextView textViewItem; public TextView textViewPersonName; public View viewParent; @Override public void onClick(View v) { Intent intent = new Intent(mainActivity, LeaseActivity.class); intent.putExtra("lease_id", leaseId); mainActivity.startActivityForResult(intent, 1); } public ViewHolder(View itemLayoutView, MainActivity mainActivity) { super(itemLayoutView); this.mainActivity = mainActivity; textViewItem = (TextView) itemLayoutView.findViewById(R.id.activity_main_item_item); textViewPersonName = (TextView) itemLayoutView.findViewById(R.id.activity_main_item_person_name); viewParent = (View) itemLayoutView.findViewById(R.id.activity_main_item_parent); viewParent.setOnClickListener(this); } public void setLeaseId(long id) { this.leaseId = id; } } }
activity_main_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main_item_parent" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- item --> <TextView android:id="@+id/activity_main_item_item" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="16dp" /> <!-- person name --> <TextView android:id="@+id/activity_main_item_person_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="16dp" /> </LinearLayout>
in MainActivity.java [part]:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaoSession daoSession = ((LeaseApplication) getApplicationContext()).getDaoSession(); LeaseDao leaseDao = daoSession.getLeaseDao(); List leaseList = leaseDao.loadAll(); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.main_activity_recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); MainActivityListAdapter adapter = new MainActivityListAdapter(leaseList, this); recyclerView.setAdapter(adapter); }
Once again, I skipped over all these steps very fast – in case it is too fast please leave a comment.
If you start the app now, you should be able to start LeaseActivity by clicking items of the RecyclerView, but all fields are still empty.
Loading detail data
The last section was pretty much, I know, but there isn’t too much left to do and now we are also going away from simple basic Android back-and-forth. I already put the IDs of the leases in the ViewHolders, and from there right into the Intent that opens LeaseActivity, so we have everything we need to load our data. For that, we are opening LeaseActivity.java and create a member to hold our Lease:
public Lease lease;
At the end of onCreate, we are then trying to get our Intent and read the lease id from it:
Intent intent = getIntent(); Long leaseId = intent.getLongExtra("lease_id", 0);
After that, you might know what follows, we can simply check, if there was a leaseId supplied. If that’s the case we load the Lease with that id from sqlite:
if(leaseId != 0 ) { DaoSession daoSession = ((LeaseApplication) getApplicationContext()).getDaoSession(); LeaseDao leaseDao = daoSession.getLeaseDao(); lease = leaseDao.load(leaseId); }
(Perhaps) we have a full entity in our attribute, so there is one last step to make it show everything… bind the values to the fields in our UI:
if(lease != null) { editTextItem.setText(lease.getItem()); editTextComment.setText(lease.getComment()); }
I think you can already see what it’s all about. We are hardly anywhere dealing with our database directly anymore! No SQL, no nothing. If you have worked with plain-old SQLite you know what I mean, and now you can also see (especially if you do a lot of reading from / writing to DB), all the effort we invested in part 1 pays out now.
I hope passing around the ID wasn’t too much and your app is up and runnning – part 4 with the remaining operations will follow shortly. As usual, if you have questions please leave a comment.
Waooo! So perfect and specific! I’m studied it for one day. Thanks a lot!
Is it possible to use green dao with sqlchiper ?
Hi Mithun,
from the discussion here I can see there is some activity in using greenDAO with SQLCipher (even though we have not been working with it).
To sum it up: there is a branch from the creators of greenDAO themselves that shall be working, but it is 10% slower (+ the usual speed decrease by SQLCipher). You can find it here:
https://github.com/greenrobot/greenDAO/tree/DbAbstraction
Best regards,
Mercatorius
thanks really great tutorial
I have just finished these 3rd part also.
everything was in brief.
Looking for 4th one.
Thanks again.