Last active
March 5, 2022 16:01
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//////////////////////////////////////////////////// | |
// 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