Skip to content

Instantly share code, notes, and snippets.

@PawelSzymanski89
Last active March 5, 2022 16:01
Show Gist options
  • Save PawelSzymanski89/91e4faa97b88fe5352574ec0753d7a85 to your computer and use it in GitHub Desktop.
Save PawelSzymanski89/91e4faa97b88fe5352574ec0753d7a85 to your computer and use it in GitHub Desktop.
How to use two diffrent types (or more) of ListView rows in Android ListView - Simple Tutorial
////////////////////////////////////////////////////
// STEP 1
////////////////////////////////////////////////////
// CREATE TWO DIFFRENT TYPES OF LAYOUTS FOR YOUR ROW IN res/layout/
// FOR EXAMPLE line1.xml and line2.xml
//LINE 1 XML CODE EXAMPLE (CONTAINS TextView and Spinner)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:padding="10dp">
<TextView
android:id="@+id/name" //<-- THE SAME ID OD THE SAME DATA IN BOTH LAYOUTS
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="name"
android:textSize="20sp" />
<Spinner
android:id="@+id/spinnerOut"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2" />
</LinearLayout>
//LINE 2 XML CODE EXAMPLE (CONTAINS 3x textView)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:padding="10dp">
<TextView
android:id="@+id/name" //<-- THE SAME ID OD THE SAME DATA IN BOTH LAYOUTS
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="name"
android:textSize="20sp" />
<TextView
android:id="@+id/value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="0"
android:textSize="20sp" />
<TextView
android:id="@+id/unit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:textSize="20sp" />
</LinearLayout>
////////////////////////////////////////////////////
// STEP 2
////////////////////////////////////////////////////
// CREATE CLASS FOR SINGLE DATA ROW (JUST ONE CLASS FOR BOTH TYPES OF DATA)
// IT'S SOME KIND OF TEMPLATE OF DATA IN ALL POSITIONS FROM OUR LIST VIEW
import android.widget.ArrayAdapter;
public class AfrData {
// HERE CREATE ALL CLASS FIELDS USING IN YOUR LIST VIEW
private String nameOfData;
private String valueOfData;
private String unitOfData;
private int dataID;
private int dataType;
private ArrayAdapter<String> arrayAdapter;
// NOW GENERATE CONSTRUCTOR WITH ALL ABOVE FIELDS (intelij press alt+insert and select generate constructor)
public AfrData(String nameOfData, String valueOfData, String unitOfData, int dataID, int dataType, ArrayAdapter<String> arrayAdapter) {
this.nameOfData = nameOfData;
this.valueOfData = valueOfData;
this.unitOfData = unitOfData;
this.dataID = dataID;
this.dataType = dataType;
this.arrayAdapter = arrayAdapter;
}
// WE NEED GENERATE GETTERS AND SETTERS FOR ALL BELOW DATA (intelij again alt+insert and select Getters and Setters)
public String getNameOfData() {
return nameOfData;
}
public void setNameOfData(String nameOfData) {
this.nameOfData = nameOfData;
}
public String getValueOfData() {
return valueOfData;
}
public void setValueOfData(String valueOfData) {
this.valueOfData = valueOfData;
}
public String getUnitOfData() {
return unitOfData;
}
public void setUnitOfData(String unitOfData) {
this.unitOfData = unitOfData;
}
public int getDataID() {
return dataID;
}
public void setDataID(int dataID) {
this.dataID = dataID;
}
public int getDataType() {
return dataType;
}
public void setDataType(int dataType) {
this.dataType = dataType;
}
public ArrayAdapter<String> getArrayAdapter() {
return arrayAdapter;
}
public void setArrayAdapter(ArrayAdapter<String> arrayAdapter) {
this.arrayAdapter = arrayAdapter;
}
}
////////////////////////////////////////////////////
// STEP 3
////////////////////////////////////////////////////
// NOW WE NEED ADAPTER FOR OUR DATA SO CREATE NEW CLASS EXTENDS WITH BASE ADAPTER
public class AdapterOfData extends BaseAdapter {
//WHEN YOU ARE USING BASE ADAPTER YOU HAVE TO IMPLEMENT OVERRIDE METHODS, PLEASE DO THAT
//THAN PREPARE FIELDS OF ADAPTER
private Context context; // Context is needed for layout inflater service
private List<AfrData> afrData; // Preparing field for list of our data based on above AfrData template.
private LayoutInflater layoutInflater; // It's needed for build our position
public static int ROW_WITH_DATA = 0; //We need define of id's for switching between our two types of layouts
public static int ROW_WITH_SPINNER = 1; //So 0 is for 3x textViev layout and 1 is for layout with spinner
// NOW PREPARE CONSTRUCTOR OF ADAPTER WITH CONTEXT AND OUR LIST OF DATA
public AdapterOfData(Context context, List<AfrData> afrData){
this.afrData = afrData;
this.context = context;
}
//BASIC OVERRIDE METHODS OF BASE ADAPTER DONT CONTAINS OF getViewTypeCount() and public int getItemViewType()
//SO WE HAVE TO IMPLEMENT THESE METHODS MANUALLY
@Override
public int getViewTypeCount() {
// HERE WE HAVE TO SET HOW MANY DIFFRENT TYPES OF ROW WE NEED US, IN OUR CASE 2
return 2;
}
@Override
public int public int getItemViewType(int position) {
// THIS METHOD IS USED BY OUR ATAPTER TO KNOW WHICH TYPE OF ROW WE NEED DISPLAY
return afrData.get(position).getDataType();
//getDataType() IS OUR FUNCTION FROM AfrData CLASS AND RETURN ONE OF OUR INT ROW_WITH_SPINNER = 1; or ROW_WITH_DATA = 0;
}
@Override
public int getCount() {
// HERE WE HAVE TO INFORM ADAPTER HOW MANY POAITIONS WE ADDED TO OUR AFR DATA LIST SO WE GET SIZE BY .size() METHOD
return afrData.size();
}
@Override
public Object getItem(int position) {
// WE DONT NEED IT FOR NOW
return null;
}
@Override
public long getItemId(int position) {
// WE DONT NEED IT FOR NOW
return 0;
}
//NOTICE: IF YOU NEED BETTER PERFORMANCE FOR BIG NUMBER OF DATA PLEASE READ ABOUT LAYOUT HOLDER
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
int type = getItemViewType(position);
if (v == null){
// GET SERVICE OF INFLATER FOR BUILD OF ROW
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (type == ROW_WITH_DATA){
//IF IN TYPE I FOUND 0 CREATE JUST 3x TEXT VIEW ROW
v = layoutInflater.inflate(R.layout.data_row,parent,false);
}else if (type == ROW_WITH_SPINNER) {
//ELSE IN TYPE I FOUND 1 CREATE ROW WITH SPINNER
v = layoutInflater.inflate(R.layout.analog_row,parent,false);
}
}
// NAME TEXT VIEW WE HAVE IN BOTH LAYOUTS SO WE SET TEXT ALWAYS
TextView name = (TextView) v.findViewById(R.id.name);
name.setText(afrData.get(position).getNameOfData());
//NOW TO AVOID NULL POINTER EXCEPTION OR ANOTHER ERROR WE HAVE TO SWITCH BETWEN LAYOUTS AND SET VALUES
if (type == ROW_WITH_DATA){
TextView value = (TextView) v.findViewById(R.id.value);
TextView unit = (TextView) v.findViewById(R.id.unit);
value.setText(String.valueOf(afrData.get(position).getValueOfData()));
unit.setText(afrData.get(position).getUnitOfData());
}
if (type == ROW_WITH_SPINNER){
Spinner spinner = (Spinner) v.findViewById(R.id.spinnerOut);
spinner.setAdapter(afrData.get(position).getArrayAdapter());
}
//NOW WE RETURN COMPLETE VIEW
return v;
}
}
////////////////////////////////////////////////////
// STEP 4
////////////////////////////////////////////////////
//NOTICE: FOR BIND VIEWS I'M USING OF BUTTER KINFE LIBRAY
// WE ARE CREATING COMPLETE LIST VIEW IN ACTIVITY
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.os.Bundle;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import static pl.ccy.afrmobile.AdapterOfData.ROW_WITH_DATA;
import static pl.ccy.afrmobile.AdapterOfData.ROW_WITH_SPINNER;
public class ActivityMain extends Activity {
//ITS IMPORTANT TO DECLARE FIELDS OF ADAPTER AND LIST OUTSIDE OF ON CREATE METHOD
//TO AVOID PROBLEMS WITH UPDATE OF DATA
List<AfrData> afrDataList;
AdapterOfData adapterOfData;
//HANDLER IS NEEDED FOR UPDATE OF DATA
public static Handler activityMainHandler;
Definitions df;
// BIND OF OUR MAIN LIST VIEW BY BUTTER KNIFE
@BindView(R.id.listOfData)
ListView listOfData;
@SuppressLint("HandlerLeak")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
df = new Definitions();
//CREATE ARRAY LIST FOR OUR DATA ROWS
afrDataList = new ArrayList<>();
//CREATE ADAPTER BASED ON OUR AdapterOfData
adapterOfData = new AdapterOfData(this, afrDataList);
//NOW ADD ALL POSITIONS WHAT WE NEED
afrDataList.add(new AfrData("AFR", "0", "", df.AFR,ROW_WITH_DATA,null));
afrDataList.add(new AfrData("Analog 1 in", "0", "", df.ANALOG_1_IN_A,ROW_WITH_DATA,null));
// EXPONATION
// ACCORDING TO CONSTRUCTOR OF AfrData WE HAVE TO FILL ALL DATA AS:
// public AfrData(String nameOfData, String valueOfData, String unitOfData, int dataID, int dataType, ArrayAdapter<String> arrayAdapter)
// IF WE USE ROW_WITH_DATA array adapter is no needed so is null
// CREATE NEW LIST FOR SPINNER
ArrayList<String> optionsList = new ArrayList<>();
optionsList.add("SPINNER POS 0");
optionsList.add("SPINNER POS 1);
optionsList.add("SPINNER POS 2");
//CREATE ADAPTER FOR SPINNER
ArrayAdapter<String> adp1 = new ArrayAdapter<String>(this,R.layout.spinner_position,optionsList);
adp1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
//ADD POSITION WITH ROW_WITH_SPINNER and adapter adp1
afrDataList.add(new AfrData("Analog 1 out", "", "", df.ANALOG_1_OUT,ROW_WITH_SPINNER,adp1));
// HERE SET ADAPTER OF OUR VIEW
listOfData.setAdapter(adapterOfData);
// IM USING HANDLER TO DINAMICALY UPDATE OF VALUES IF YOU NEED DESCRIPTION PLEASE COMMENT
activityMainHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (isDataToUpdate(msg.arg1)) {
afrDataList.get(findDataRowId(msg.arg1)).setValueOfData(String.valueOf(msg.obj));
Log.i(String.valueOf(msg.arg1),String.valueOf(msg.obj));
adapterOfData.notifyDataSetChanged();
}
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment