I have been trying this for a while now and my inexperience has been getting the best of me. Any pointers or suggestions about how I should be approaching the issue will be greatly appreciated.
I have a custom ListView with each item containing two strings. One particular item in the ListView is changing a mode of operation. What I am trying to achieve is this:
- On clicking this ListView item, open a Dialog containing a list attached to radio buttons.
- On selecting one of the Dialog items, close the Dialog and change one of the two ListView strings in that item to reflect that mode selection.
My questions is this: How can I update my ListView items when a Dialog button is clicked?
I can create the ListView and Dialog fine, but updating the ListView is proving difficult. Initial values can be set but are not being updated. I know that I need to call .notifyDataSetChanged, but as I would like this to happen inside onItemClick for the Dialog, I get an error along the lines of "Cannot refer to a non-final variable inside an inner class defined in a different method", and my variables cannot be final.
What I have tried:
- Notifying the data change immediately before the dialog closes. Got "non-final variable" error.
- Implementing a model-view-controller setup and trying to create a method that acts like an "onVariableChangeListener", but I left this idea because it seemed a bit off-topic and I wasn't quite understanding it.
- Create one method to bring up the Dialog and another to update the ListView data. The issue was that the Update method was not waiting for the Dialog item to be clicked. Even after the dialog selection is changed and re-opened, the ListView is not updated.
- Creating a method, operating similarly to onCreateView, that is called whenever the item is clicked. It should create and inflate a new ListView. Since it is called within onItemClick it does not have the "non-final variable" error, though it required me to create final instances of onCreateView's parameters. I am unsure of the implications this has. Is this a valid approach?
The following code is a mix of the latter two ideas. There are methods for the Dialog and update separately, though updating does not wait for the dialog and the parameters going into the update method are final.
I am hoping the solution will be straightforward, but I believe there are multiple errors at work. I know SO likes to have questions that can apply to others who may read this later on, so I apologise if this is a bit specific to my case. Any help is appreciated!
CalibrationFragment.java
public class CalibrationFragment extends Fragment{
private Context mContext;
private ArrayList<DataItem> data = new ArrayList<DataItem>(); // DataItem is a class that sets two strings
private ArrayList<DataItem> currentData = new ArrayList<DataItem>(); // Two records kept to identify changes
ListView myList;
int checkedItem1 = 0; // For the dialog boxes.
@Override
public void onCreate (Bundle savedInstanceState){
super.onCreate(savedInstanceState);
mContext = getActivity(); // Get the context of the Activity and therefore the fragment
myList = new ListView (mContext); // Create a ListView
// Set initial values
calibrationData readData = new calibrationData(); // readData will contain all ListView values
// ... other items...
readData.setMode(checkedItem1, mContext); // Set to 0, 1 or 2 indicating different modes
// ... other items...
// Create elements to be added to the list
// ...
DataItem calibration4 = new DataItem(getResources().getString(R.string.mode_calibration), readData.getMode());
// readData.getMode() returns different strings depending on int input
// ...
// Add elements to the list
// ...
data.add(calibration4);
// ...
currentData = data; // Initialise both as the same
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Inflate the view
View rootView = inflater.inflate(R.layout.fragment_list, container, false);
myList=(ListView) rootView.findViewById(android.R.id.list);
// Create and set the customised adapter to take data (ArrayList<DataItem>) and format
// each element as defined in list_row.xml
CustomAdapter adapter = new CustomAdapter(mContext, R.layout.list_row, data);
myList.setAdapter(adapter);
// The above adapter is the one I need to notify
// For the sake of letting my code compile in onItemClick. Unsure if this causes further issues.
final LayoutInflater finalInflater = inflater;
final ViewGroup finalContainer = container;
final Bundle finalSavedInstanceState = savedInstanceState;
// CustomAdapter adapter cannot be used in setOnItemClickListener because it is non-final
myList.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
public void onItemClick(AdapterView<?> parentAdapter, View view, int position, long id)
{
switch((int)id){
// [Different cases here]
case 3: // Clicks of Mode item
// Creates Dialog
showDialog((int)id, checkedItem1);
// updateData executes as Dialog is generated, not clicked or closed. Do not want this.
updateData(finalInflater, finalContainer, finalSavedInstanceState);
break;
// [Rest of the cases and default statement here]
}
}
});
return rootView; // onCreateView must return a View variable if displaying a UI.
}
public void showDialog(int id, int checkedItem)
{
// In case I want to have other dialogs later, ensure this is for Mode selection.
if(id == 3){
final CharSequence[] modeItems = {
"Mode 1", "Mode 2", "Mode 3"
}; // For Dialog
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle(getResources().getString(R.string.mode_calibration));
builder.setSingleChoiceItems(modeItems, checkedItem, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
checkedItem1 = item; // Change checkedItem1 to the index selected in the dialog
calibrationData readData = new calibrationData();
readData.setMode(checkedItem1, mContext); // Set Mode depending on checkedItem
// Create DataItem representing new mode
DataItem newMode = new DataItem(getResources().getString(R.string.mode_calibration), readData.getMode());
currentData.set(3, newMode); // Set new mode into currentData.
// Eventual comparison with var 'data' will indicate a change
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
alert.show();
}
public View updateData(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
// Inflate the new view
View rootView = inflater.inflate(R.layout.fragment_list, container, false);
myList=(ListView) rootView.findViewById(android.R.id.list);
// Create and set the customised adapter to take data (ArrayList<DataItem>) and format
// each element as defined in list_row.xml
CustomAdapter adapter = new CustomAdapter(mContext, R.layout.list_row, data);
myList.setAdapter(adapter);
// As this is only called in onClick, I can work with data assuming something has changed and been clicked.
int i = 0;
// Compare each element inside data and currentData.
for(i = 0; i < data.size(); i++){
if(data.get(i) != currentData.get(i)) data = currentData;
if(i==5) Toast.makeText(mContext, "Iterating to end", Toast.LENGTH_SHORT).show(); // debug purposes
}
adapter.notifyDataSetChanged(); // Update list
return rootView;
}