In Android, given a ListView of items with TextView and RadioGroup, how to add items dynamically?
I need to manage a ListView, so that each item has a textView and a RadioGroup. Following is the xml file for one item: controls_layout_row.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:padding="20dp" android:background="@drawable/fragment_template" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/control_name" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#ffffff" android:textSize="20sp" android:textStyle="bold" android:layout_marginBottom="5dp"/> <RadioGroup android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/control_name" android:id="@+id/control_options_list" android:layout_marginBottom="20dp" tools:layout_height="150dip"> <!-- items added within code --> </RadioGroup> </RelativeLayout>
The items are part of a ListView – following is the xml file for the ListView:
controls_dialog.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:padding="20dp" android:background="@drawable/fragment_template" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/controls_list_title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#ffffff" android:text="Controls" android:textSize="20dp" android:textStyle="bold" android:layout_marginBottom="5dp"/> <ListView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/controls_list_title" android:id="@+id/controls_list" tools:layout_height="150dip"> <!-- items added within code --> </ListView> //Close button <Button android:id="@+id/controls_close_button" android:layout_below="@id/controls_list" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="14dp" android:text="Close" android:layout_alignParentEnd="true" android:textAllCaps="false" android:textStyle="bold"/> </RelativeLayout>
Now, the items are not known at the beginning. The application uses JNI interface to get info from connected hardware, and then some getters are used in the activity in order to know which items must appear in the dialog. I have tried 2 methods, and I got stuck in both of them.
First method: simply create some item each time I need and add its view to the list:
// retrieve item view final View emittersRowView = inflater.inflate(R.layout.controls_layout_row, null); // set TextView final TextView emitterTitle = emittersRowView.findViewById(R.id.control_name); emitterTitle.setText("Projector"); //set RadioGroup final String emitterDescriptions[] = getOptionDescriptions(Option.EMITTER_ENABLED); final RadioGroup emittersRadioGroup = emittersRowView.findViewById(R.id.control_options_list); // adding buttons to the group for(int i = 0; i < emitterDescriptions.length; ++i) { RadioButton button = new RadioButton(activity); button.setId(i); button.setText(emitterDescriptions[i]); button.setTextColor(getResources().getColor(R.color.white)); button.setChecked(i == indexOfCurrentEmitter); emittersRadioGroup.addView(button); } emittersRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener(){ @Override public void onCheckedChanged(RadioGroup group, int checkedId) { List<Sensor> sensors = mDevice.querySensors(); for (Sensor s : sensors) { if (s.supports(Option.EMITTER_ENABLED)) { s.setValue(Option.EMITTER_ENABLED, checkedId); } } } }); //add control to list ListView controlsList = fragmentView.findViewById(R.id.controls_list); controlsList.addView(emittersRowView);
This method did not work. I have read in other forums that this cannot work without using an adpater. This seems weird to me, because using the addView method on the RadioGroup object, in order to add RadioButton objects works great.
Second Method: Using a Custom Adapter: I have defined an adpater and my plan was to first build:
- a String array for the TextView objects
- a list of RadioGroup objects for the RadioGroup objects and then use them for creating the adapter. But when I got to the code, I got stuck, no knowing how to set the RadioGroup in the adapter:
Code for the adapter:
class CustomAdapter extends BaseAdapter { @Override public int getCount() { return numOfControls; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { final Activity activity = getActivity(); convertView = activity.getLayoutInflater().inflate(R.layout.controls_layout_row, null); TextView control_name = convertView.findViewById(R.id.control_name); RadioGroup control_options = convertView.findViewById(R.id.control_options_list); control_name.setText(mControlNames[i]); control_options.set....??? return convertView; } }
Please help me to make it work.
Start a new project and try below complete sample code,
MainActivity.java:
public class MainActivity extends AppCompatActivity { class Control { String name; String[] options; int checkedPosition = -1; public Control(String name, String[] options) { this.name = name; this.options = options; } public String getName() { return name; } public String[] getOptions() { return options; } public int getCheckedPosition() { return checkedPosition; } public void setCheckedPosition(int checkedPosition) { this.checkedPosition = checkedPosition; } } Control[] controls = new Control[10]; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView controlsList = findViewById(R.id.controls_list); createSampleData(); CustomAdapter adapter = new CustomAdapter(this, 0, controls); controlsList.setAdapter(adapter); Button button = findViewById(R.id.controls_close_button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String msg = ""; for (int i = 0; i < controls.length; i++) { msg += controls[i].getName() + " , " + controls[i].getCheckedPosition() + "\n"; } Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show(); } }); } private void createSampleData() { Random random = new Random(); for (int i = 0; i < controls.length; i++) { int j = random.nextInt(4) + 2; String[] options = new String[j]; for (int k = 0; k < j; k++) { options[k] = (i + 1) + "-" + k; } controls[i] = new Control("Control " + (i + 1), options); } } }
CustomAdapter.java:
public class CustomAdapter extends ArrayAdapter { Context context; LayoutInflater inflater; public CustomAdapter(@NonNull Context context, int resource, @NonNull MainActivity.Control[] objects) { super(context, resource, objects); this.context = context; inflater = LayoutInflater.from(context); } @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { convertView = inflater.inflate(R.layout.controls_layout_row, null); TextView control_name = convertView.findViewById(R.id.control_name); RadioGroup control_options = convertView.findViewById(R.id.control_options_list); MainActivity.Control control = (MainActivity.Control)getItem(position); control_name.setText(control.getName()); String[] options = control.getOptions(); for(int i = 0; i < options.length; i++) { RadioButton button = new RadioButton(context); button.setId(i); button.setText(options[i]); button.setTextColor(Color.WHITE); button.setChecked(i == control.getCheckedPosition()); control_options.addView(button); } control_options.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup radioGroup, int i) { int pos = (int)radioGroup.getTag(); MainActivity.Control changedControl = (MainActivity.Control)getItem(pos); changedControl.setCheckedPosition(i); } }); control_options.setTag(position); return convertView; } }
use your posted layouts.