The most powerful library in this case is ORMLite. It provides some simple, lightweight functionality for persisting Java objects to SQL databases while avoiding the complexity and overhead of more standard ORM packages. Moreover, it also solved well the problems concerning the relationship between the entities/tables.
Today, in this post, I would like to build a simple project which creating/managing a database with ORMLite, resolved entities relationships by ForeignCollection object. See this DEMO VIDEO for output:
This post is part of a series called Android TOP useful libraries
Before Start
- I have 2 POJOs: Cat and Kitten. ORMLite will persist theme to 2 corresponding tables.
- Relationship: Cat : Kitten = 1 : n.
Adding dependencies to app/build.gradle befor start coding:
compile 'com.j256.ormlite:ormlite-core:4.48' compile 'com.j256.ormlite:ormlite-android:4.48'- Project structure:
Creating Database/Tables
package info.devexchanges.ormlite.model; import com.j256.ormlite.dao.ForeignCollection; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.field.ForeignCollectionField; import java.util.ArrayList; import java.util.List; public class Cat { @DatabaseField(generatedId=true) private int id; @DatabaseField (columnName = "name") private String name; @ForeignCollectionField private ForeignCollection<Kitten> kittens; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setKittens(ForeignCollection<Kitten> items) { this.kittens = items; } public List<Kitten> getKittens() { ArrayList<Kitten> itemList = new ArrayList<>(); for (Kitten item : kittens) { itemList.add(item); } return itemList; } @Override public String toString() { return this.name; } }
package info.devexchanges.ormlite.model; import com.j256.ormlite.field.DatabaseField; public class Kitten { @DatabaseField(generatedId=true) private int id; @DatabaseField (columnName = "name") private String name; @DatabaseField(foreign=true, foreignAutoRefresh=true) private Cat cat; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } }
Building Database Helper and Controller classes
package info.devexchanges.ormlite.database; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.util.Log; import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.support.ConnectionSource; import com.j256.ormlite.table.TableUtils; import info.devexchanges.ormlite.model.Cat; import info.devexchanges.ormlite.model.Kitten; public class DatabaseHelper extends OrmLiteSqliteOpenHelper { // name of the database file for your application -- change to something appropriate for your app private static final String DATABASE_NAME = "cat.db"; // any time you make changes to your database objects, you may have to increase the database version private static final int DATABASE_VERSION = 1; // the DAO object we use to access the SimpleData table private Dao<Cat, Integer> catDAO = null; private Dao<Kitten, Integer> kittenDAO = null; public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) { try { TableUtils.createTable(connectionSource, Cat.class); TableUtils.createTable(connectionSource, Kitten.class); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (java.sql.SQLException e) { e.printStackTrace(); } } @Override public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) { try { List<String> allSql = new ArrayList<>(); for (String sql : allSql) { db.execSQL(sql); } } catch (SQLException e) { Log.e(DatabaseHelper.class.getName(), "exception during onUpgrade", e); throw new RuntimeException(e); } } public Dao<Cat, Integer> getCatsDAO() { if (catDAO == null) { try { catDAO = getDao(Cat.class); } catch (java.sql.SQLException e) { e.printStackTrace(); } } return catDAO; } public Dao<Kitten, Integer> getKittenDAO() { if (kittenDAO == null) { try { kittenDAO = getDao(Kitten.class); } catch (java.sql.SQLException e) { e.printStackTrace(); } } return kittenDAO; } }We need one more class that will encapsulate all the interactions with the DAO in the same package, let's create a basic code for it, and then we will add methods gradually as we need them in code. Through this class, define the way to display/update/delete table records/POJOs through DAO. In Activities/Adapter code, we will mainly work with this class:
package info.devexchanges.ormlite.database; import android.content.Context; import com.j256.ormlite.stmt.DeleteBuilder; import java.sql.SQLException; import java.util.ArrayList; import info.devexchanges.ormlite.model.Cat; import info.devexchanges.ormlite.model.Kitten; public class DatabaseManager { private static DatabaseManager instance; private DatabaseHelper helper; public static void init(Context ctx) { if (null == instance) { instance = new DatabaseManager(ctx); } } static public DatabaseManager getInstance() { return instance; } private DatabaseManager(Context ctx) { helper = new DatabaseHelper(ctx); } public DatabaseHelper getHelper() { return helper; } /** * Get all customer in db * * @return */ public ArrayList<Cat> getAllCats() { ArrayList<Cat> cats = null; try { cats = (ArrayList<Cat>) getHelper().getCatsDAO().queryForAll(); } catch (SQLException e) { e.printStackTrace(); } return cats; } public void addCat(Cat cat) { try { getHelper().getCatsDAO().create(cat); } catch (SQLException e) { e.printStackTrace(); } } public void refreshCat(Cat cat) { try { getHelper().getCatsDAO().refresh(cat); } catch (SQLException e) { e.printStackTrace(); } } public void updateCat(Cat wishList) { try { getHelper().getCatsDAO().update(wishList); } catch (SQLException e) { e.printStackTrace(); } } public void deleteCat (int catId) { try { DeleteBuilder<Cat, Integer> deleteBuilder = getHelper().getCatsDAO().deleteBuilder(); deleteBuilder.where().eq("id", catId); deleteBuilder.delete(); } catch (SQLException e) { e.printStackTrace(); } } public Kitten newKitten() { Kitten kitten = new Kitten(); try { getHelper().getKittenDAO().create(kitten); } catch (SQLException e) { e.printStackTrace(); } return kitten; } public Kitten newKittenAppend(Kitten kitten) { try { getHelper().getKittenDAO().create(kitten); } catch (SQLException e) { e.printStackTrace(); } return kitten; } public void updateKitten(Kitten item) { try { getHelper().getKittenDAO().update(item); } catch (SQLException e) { e.printStackTrace(); } } public ArrayList<Kitten> getAllKittens() { ArrayList<Kitten> kittenArrayList = null; try { kittenArrayList = (ArrayList<Kitten>) getHelper().getKittenDAO().queryForAll(); } catch (SQLException e) { e.printStackTrace(); } return kittenArrayList; } public void deleteKitten (int kittenId) { try { DeleteBuilder<Kitten, Integer> deleteBuilder = getHelper().getKittenDAO().deleteBuilder(); deleteBuilder.where().eq("id", kittenId); deleteBuilder.delete(); } catch (SQLException e) { e.printStackTrace(); } } }
Display data to Views
DatabaseManager.init(this);Get all tables records by DAO and save to ArrayList objects:
//get all data to Lists ArrayList<Cat> catArrayList = DatabaseManager.getInstance().getAllCats();No more specials, we have full code for main activity:
package info.devexchanges.ormlite.activity; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ExpandableListView; import android.widget.ProgressBar; import android.widget.TextView; import java.util.ArrayList; import java.util.List; import info.devexchanges.ormlite.R; import info.devexchanges.ormlite.adapter.ExpandableListAdapter; import info.devexchanges.ormlite.database.DatabaseManager; import info.devexchanges.ormlite.model.Cat; import info.devexchanges.ormlite.model.Kitten; public class MainActivity extends AppCompatActivity { private ProgressBar progressBar; private ExpandableListView listView; private TextView notice; private List<Cat> cats; private ExpandableListAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DatabaseManager.init(this); notice = (TextView)findViewById(R.id.notice); listView = (ExpandableListView)findViewById(R.id.list_item); progressBar = (ProgressBar)findViewById(R.id.progress); cats = new ArrayList<>(); //set data to views adapter = new ExpandableListAdapter(this, cats); listView.setAdapter(adapter); } @Override protected void onResume() { super.onResume(); Log.d("Main", "resume"); getDataFromDB(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.add) { Intent i = new Intent(this, AddingActivity.class); startActivity(i); return true; } return super.onOptionsItemSelected(item); } public void getDataFromDB() { if (cats != null) cats.clear(); //get all data to Lists ArrayList<Cat> catArrayList = DatabaseManager.getInstance().getAllCats(); for (int i = 0; i < catArrayList.size(); i++) { cats.add(catArrayList.get(i)); } if (cats.size() == 0) { //no data in database listView.setVisibility(View.GONE); notice.setText("Database is Empty"); notice.setVisibility(View.VISIBLE); } else { adapter.notifyDataSetChanged(); } } }Layout for this activity:
Adding new items
//save new object to db DatabaseManager.getInstance().addCat(cat);And add a Kitten is more complicate:
//save to database DatabaseManager.getInstance().newKittenAppend(kitten); DatabaseManager.getInstance().updateKitten(kitten);This "adding items" process has been written in an activity, and this is full code for it:
package info.devexchanges.ormlite.activity; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.Spinner; import android.widget.Toast; import java.util.ArrayList; import java.util.List; import info.devexchanges.ormlite.R; import info.devexchanges.ormlite.database.DatabaseManager; import info.devexchanges.ormlite.model.Cat; import info.devexchanges.ormlite.model.Kitten; public class AddingActivity extends AppCompatActivity { private View btnAddCat; private View btnAddKitten; private View btnOK; private Spinner spinner; private ViewGroup layoutAddCat; private ViewGroup layoutAddKitten; private ViewGroup layoutButtons; private EditText editCat; private EditText editKitten; private View btnCancel; private List<Cat> cats; private boolean havingCat = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_adding); btnAddCat = findViewById(R.id.btn_add_cat); btnOK = findViewById(R.id.btn_ok); layoutAddKitten = (ViewGroup) findViewById(R.id.ll_kit); layoutAddCat = (ViewGroup) findViewById(R.id.ll_cat); layoutButtons = (ViewGroup) findViewById(R.id.ll_buttons); btnCancel = findViewById(R.id.btn_cancel); spinner = (Spinner) findViewById(R.id.spinner); btnAddKitten = findViewById(R.id.btn_add_kitten); editCat = (EditText) findViewById(R.id.txt_name); editKitten = (EditText) findViewById(R.id.txt_kitten_name); btnAddKitten.setOnClickListener(onAddKittenListener()); btnAddCat.setOnClickListener(onAddCatListner()); btnCancel.setOnClickListener(onCancelListener()); btnOK.setOnClickListener(onConfirmListener()); } private View.OnClickListener onConfirmListener() { return new View.OnClickListener() { @Override public void onClick(View v) { if (layoutAddCat.getVisibility() == View.VISIBLE) { if (editCat.getText().toString().trim().equals("")) { Toast.makeText(getBaseContext(), "Please input cat name", Toast.LENGTH_SHORT).show(); } else { Cat cat = new Cat(); cat.setName(editCat.getText().toString().trim()); //save new object to db DatabaseManager.getInstance().addCat(cat); } } else if (layoutAddKitten.getVisibility() == View.VISIBLE) { if (editKitten.getText().toString().trim().equals("")) { Toast.makeText(getBaseContext(), "Please input kitten name", Toast.LENGTH_SHORT).show(); } else { Kitten kitten = new Kitten(); Cat cat = (Cat) spinner.getSelectedItem(); kitten.setName(editKitten.getText().toString().trim()); kitten.setCat(cat); //save to database DatabaseManager.getInstance().newKittenAppend(kitten); DatabaseManager.getInstance().updateKitten(kitten); } } goneLayouts(); } }; } private View.OnClickListener onCancelListener() { return new View.OnClickListener() { @Override public void onClick(View v) { goneLayouts(); } }; } private View.OnClickListener onAddCatListner() { return new View.OnClickListener() { @Override public void onClick(View v) { goneLayouts(); layoutAddCat.setVisibility(View.VISIBLE); layoutButtons.setVisibility(View.VISIBLE); } }; } private View.OnClickListener onAddKittenListener() { return new View.OnClickListener() { @Override public void onClick(View v) { if (!havingCat) { Toast.makeText(getBaseContext(), "None Cat in DB, please add cat first", Toast.LENGTH_SHORT).show(); } else { goneLayouts(); cats = DatabaseManager.getInstance().getAllCats(); if (cats.size() == 0) { havingCat = false; } else { //set spinner adapter ArrayAdapter<Cat> adapter = new ArrayAdapter<>(AddingActivity.this, android.R.layout.simple_dropdown_item_1line, cats); spinner.setAdapter(adapter); } layoutAddKitten.setVisibility(View.VISIBLE); layoutButtons.setVisibility(View.VISIBLE); } } }; } private void goneLayouts() { layoutAddCat.setVisibility(View.GONE); layoutAddKitten.setVisibility(View.GONE); layoutButtons.setVisibility(View.GONE); editCat.setText(""); editKitten.setText(""); } }And it's layout:
Output for this screen:
Editing/Updating an item
private View.OnClickListener onEditCatListener(final Cat cat, final int position) { return new View.OnClickListener() { @Override public void onClick(View view) { AlertDialog.Builder alertDialog = new AlertDialog.Builder(activity); alertDialog.setTitle("EDIT Cat"); alertDialog.setMessage("Please type a new cat name"); final EditText input = new EditText(activity); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); input.setLayoutParams(lp); input.setText(cat.getName()); alertDialog.setView(input); alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //update database with new cat name cat.setName(input.getText().toString().trim()); DatabaseManager.getInstance().updateCat(cat); //update views cats.get(position).setName(input.getText().toString().trim()); notifyDataSetChanged(); } }); alertDialog.setNegativeButton("Cancel", null); alertDialog.show(); } }; } private View.OnClickListener onEditKittenListener(final Kitten kitten, final int groupPos, final int childPos) { return new View.OnClickListener() { @Override public void onClick(View view) { AlertDialog.Builder alertDialog = new AlertDialog.Builder(activity); alertDialog.setTitle("EDIT Kitten"); alertDialog.setMessage("Please type a new kitten name"); final EditText input = new EditText(activity); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); input.setLayoutParams(lp); input.setText(kitten.getName()); alertDialog.setView(input); alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //update database kitten.setName(input.getText().toString().trim()); DatabaseManager.getInstance().updateKitten(kitten); //update views cats.get(groupPos).getKittens().get(childPos).setName(input.getText().toString()); notifyDataSetChanged(); } }); alertDialog.setNegativeButton("Cancel", null); alertDialog.show(); } }; }
Output of this action:
Deleting an item
private View.OnClickListener onDeleteKittenListener(final int groupPosition, final int childPosition) { return new View.OnClickListener() { @Override public void onClick(View view) { AlertDialog.Builder alertDialog = new AlertDialog.Builder(activity); alertDialog.setTitle("Delete?"); alertDialog.setMessage("Are you sure to delete?"); alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //delete in database DatabaseManager.getInstance().deleteKitten(getChild(groupPosition, childPosition).getId()); //update views activity.getDataFromDB(); } }); alertDialog.setNegativeButton("Cancel", null); alertDialog.show(); } }; } private View.OnClickListener onDeleteCatListener(final int groupPosition) { return new View.OnClickListener() { @Override public void onClick(View view) { AlertDialog.Builder alertDialog = new AlertDialog.Builder(activity); alertDialog.setTitle("Delete?"); alertDialog.setMessage("Are you sure to delete?"); alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //delete in database DatabaseManager.getInstance().deleteCat(getGroup(groupPosition).getId()); //update views cats.remove(groupPosition); notifyDataSetChanged(); } }); alertDialog.setNegativeButton("Cancel", null); alertDialog.show(); } }; }Output:
Final code
package info.devexchanges.ormlite.adapter; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import java.util.List; import info.devexchanges.ormlite.R; import info.devexchanges.ormlite.activity.MainActivity; import info.devexchanges.ormlite.database.DatabaseManager; import info.devexchanges.ormlite.model.Cat; import info.devexchanges.ormlite.model.Kitten; public class ExpandableListAdapter extends BaseExpandableListAdapter { private MainActivity activity; private List<Cat> cats; public ExpandableListAdapter(MainActivity context, List<Cat> rows) { this.activity = context; this.cats = rows; } @Override public Kitten getChild(int groupPosition, int childPosititon) { return this.cats.get(groupPosition).getKittens().get(childPosititon); } @Override public long getChildId(int groupPosition, int childPosition) { return childPosition; } @Override public int getChildrenCount(int groupPosition) { return getGroup(groupPosition).getKittens().size(); } @Override public Cat getGroup(int groupPosition) { return this.cats.get(groupPosition); } @Override public int getGroupCount() { return cats.size(); } @Override public long getGroupId(int groupPosition) { return groupPosition; } @Override public View getChildView(int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { ChildViewHolder holder; LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); if (convertView == null) { convertView = inflater.inflate(R.layout.layout_child_list, parent, false); holder = new ChildViewHolder(convertView); convertView.setTag(holder); } else { holder = (ChildViewHolder) convertView.getTag(); } holder.textView.setText(getChild(groupPosition, childPosition).getName()); holder.btnEdit.setOnClickListener(onEditKittenListener(getChild(groupPosition, childPosition), groupPosition, childPosition)); holder.btnDelete.setOnClickListener(onDeleteKittenListener(groupPosition, childPosition)); return convertView; } @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { HeaderViewHolder holder; LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); if (convertView == null) { convertView = inflater.inflate(R.layout.layout_header_list, parent, false); holder = new HeaderViewHolder(convertView); convertView.setTag(holder); } else { holder = (HeaderViewHolder) convertView.getTag(); } holder.textView.setText(getGroup(groupPosition).getName()); holder.btnEdit.setOnClickListener(onEditCatListener(getGroup(groupPosition), groupPosition)); holder.btnDelete.setOnClickListener(onDeleteCatListener(groupPosition)); return convertView; } private View.OnClickListener onDeleteKittenListener(final int groupPosition, final int childPosition) { return new View.OnClickListener() { @Override public void onClick(View view) { AlertDialog.Builder alertDialog = new AlertDialog.Builder(activity); alertDialog.setTitle("Delete?"); alertDialog.setMessage("Are you sure to delete?"); alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //delete in database DatabaseManager.getInstance().deleteKitten(getChild(groupPosition, childPosition).getId()); //update views activity.getDataFromDB(); } }); alertDialog.setNegativeButton("Cancel", null); alertDialog.show(); } }; } private View.OnClickListener onDeleteCatListener(final int groupPosition) { return new View.OnClickListener() { @Override public void onClick(View view) { AlertDialog.Builder alertDialog = new AlertDialog.Builder(activity); alertDialog.setTitle("Delete?"); alertDialog.setMessage("Are you sure to delete?"); alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //delete in database DatabaseManager.getInstance().deleteCat(getGroup(groupPosition).getId()); //update views cats.remove(groupPosition); notifyDataSetChanged(); } }); alertDialog.setNegativeButton("Cancel", null); alertDialog.show(); } }; } private View.OnClickListener onEditCatListener(final Cat cat, final int position) { return new View.OnClickListener() { @Override public void onClick(View view) { AlertDialog.Builder alertDialog = new AlertDialog.Builder(activity); alertDialog.setTitle("EDIT Cat"); alertDialog.setMessage("Please type a new cat name"); final EditText input = new EditText(activity); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); input.setLayoutParams(lp); input.setText(cat.getName()); alertDialog.setView(input); alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //update database with new cat name cat.setName(input.getText().toString().trim()); DatabaseManager.getInstance().updateCat(cat); //update views cats.get(position).setName(input.getText().toString().trim()); notifyDataSetChanged(); } }); alertDialog.setNegativeButton("Cancel", null); alertDialog.show(); } }; } private View.OnClickListener onEditKittenListener(final Kitten kitten, final int groupPos, final int childPos) { return new View.OnClickListener() { @Override public void onClick(View view) { AlertDialog.Builder alertDialog = new AlertDialog.Builder(activity); alertDialog.setTitle("EDIT Kitten"); alertDialog.setMessage("Please type a new kitten name"); final EditText input = new EditText(activity); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); input.setLayoutParams(lp); input.setText(kitten.getName()); alertDialog.setView(input); alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //update database kitten.setName(input.getText().toString().trim()); DatabaseManager.getInstance().updateKitten(kitten); //update views cats.get(groupPos).getKittens().get(childPos).setName(input.getText().toString()); notifyDataSetChanged(); } }); alertDialog.setNegativeButton("Cancel", null); alertDialog.show(); } }; } @Override public boolean hasStableIds() { return false; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } private static class ChildViewHolder { private TextView textView; private View btnEdit; private View btnDelete; public ChildViewHolder(View v) { textView = (TextView) v.findViewById(R.id.kitten_name); btnEdit = v.findViewById(R.id.edit); btnDelete = v.findViewById(R.id.delete); } } private static class HeaderViewHolder { private TextView textView; private View btnEdit; private View btnDelete; public HeaderViewHolder(View v) { btnDelete = v.findViewById(R.id.delete); textView = (TextView) v.findViewById(R.id.cat_name); btnEdit = v.findViewById(R.id.edit); } } }Now, adding some necessary files to comple our project:
- ExpandableListView group layout, showing Cat name:
- ExpandableListView child layout, displaying Kitten name:
Main screen after app launching:
- Strings resource: