I have a listview in my android app which does two functions with the listview itself.
Fist, I can delete an item from the listview.
Second, I can rename something in the listview.
Anytime any of the above happens,
How do I display a different XML layout if the last item on the listview is deleted?
adapter = new SetRowsCustomAdapter(getActivity(), R.layout.customlist, rowsArray);
dataList = (ListView) mFrame3.findViewById(R.id.lvFiles); //lvfiles from xml layout
dataList.setAdapter(adapter);
dataList.setEmptyView(noFilesDisplayed); //not working
Second, once I rename anything within the listview, how do I update the listview to reflect the changes?
adapter.notifyDataSetChanged(); //not working
I have two choices in my ContextMenu, Delete and Rename.
If Delete is chosen, the following code executes:
if (menuItemIndex == 0) {
if (folder.exists()) {
//File flEachFile = new File(folder.toString() + "/" + currentFileName + ".tp");
flEachFile = new File(folder.toString() + "/" + txt + ".tp");
flEachFile2 = new File(folder.toString() + "/." + txt + ".tp");
if (flEachFile.exists()) {
flEachFile.delete();
}
if (flEachFile2.exists()) {
flEachFile2.delete();
}
adapter.remove(adapter.getItem(info.position)); //updated the list
//adapter.notifyDataSetChanged();
dataList.invalidate();
dataList.setEmptyView(noFilesDisplayed);//should display the noFilesDisplayed layout but it's not.
//getActivity().getActionBar().setSelectedNavigationItem(1);
}
}
This works fine as in the list updates itself as I delete a list. So if I have two lists in the view and I delete one the list updates to show one:
adapter.remove(adapter.getItem(info.position)); //updated the list
But if I delete the last item on the list and I want to display another xml layout as in this line:
dataList.setEmptyView(noFilesDisplayed);//should display the noFilesDisplayed layout but it's not.
It doesn't work.
======================================================
If Rename is chosen the following code executes:
if (menuItemIndex == 1) {
// custom dialog
final Dialog dialog = new Dialog(getActivity());
dialog.setContentView(R.layout.renamefile);
dialog.setTitle("Rename Existing Trip");
currTrip = (TextView) dialog.findViewById(R.id.tvCurrentFileName);
currTrip.setText(txt);
etRT = (EditText) dialog.findViewById(R.id.etRenameTo);
Button btnCancel = (Button) dialog.findViewById(R.id.btnCancel);
Button btnApply = (Button) dialog.findViewById(R.id.btnApply);
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
btnApply.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(folder.exists()) {
flEachFile = new File(folder.toString() + "/" + currTrip.getText().toString() + ".tp");
flEachFile2 = new File(folder.toString() + "/." + currTrip.getText().toString() + ".tp");
if (flEachFile.exists()) {
if (etRT.getText().toString().matches("")) {
rTo = (TextView) dialog.findViewById(R.id.tvRenameTo);
rTo.setTextColor(Color.parseColor("#A60000"));
}
else {
File toFile = new File(folder.toString() + "/" + etRT.getText().toString() + ".tp");
flEachFile.renameTo(toFile);
if (flEachFile2.exists()) {
File toFile2 = new File(folder.toString() + "/." + etRT.getText().toString() + ".tp");
flEachFile2.renameTo(toFile2);
}
//adapter.notifyDataSetChanged();
dataList.invalidate();
dialog.dismiss();
//getActivity().getActionBar().setSelectedNavigationItem(1);
}
}
else {
Toast.makeText(getActivity(), "File does not exist", Toast.LENGTH_SHORT).show();
}
}
}
});
// if cancel is clicked, close the custom dialog
btnCancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dialog.dismiss();
}
});
dialog.show();
}
I can only see the changes if I switch tab and come back to this tab.
It's best to interact with a ListView through its associated adapter, in this case your SetRowCustomAdapter. To remove items you should do adapter.remove(position), which will automatically handle the notifyDataSetChanged() function and internally remove it from whatever data structure it's in. As for renaming something, it depends on how your modeled the data behind the scenes in your adapter. Could you provide some more code for your customer adapter? Are you using an array internally?
UPDATE
Regarding setEmptyView(noFilesDisplayed) not working, is noFilesDisplayed in the same the layout hierarchy where the ListView is? In my experience, which has also been verified by other people, the empty view (in this case noFilesDisplayed) must be in the same layout XML file and, more specifically, must be in the same hierarchy. I personally don't know why it has to be this way, but it seems like that's the only way it works. Here's the example of what I mean...
<ListView
android:id="#+id/list_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<View
android:id="#+id/empty_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:visibility="gone" />
Then in code, you can point noFilesDisplayed to the resource ID empty_view. I'm not entirely sure if setting the visibility to gone is necessary, try playing around with that. I hope that answers your question about the empty view.
call adpater.notifyDataSetChanged(); after you have made changes to your ListView, if notifyDataChanged() is still not working, you can set the adapter and pass the new updated data to the adapter constructor in onResume method. So whenever you delete/rename data in your listView, it will be updated automatically. (This second method will work only if you are starting a new activity for deleting or renaming listView items.)
#Override
protected void onResume() {
super.onResume();
SetRowsCustomAdapter adapter = new SetRowsCustomAdapter(getActivity(), R.layout.customlist, rowsArray);
dataList.setAdapter(adapter);
}
Related
I have a ListView with rows with different layouts. So I'm using the pattern of ViewHolder.
If the user clicks on a row, one sub-layout of the same row must be shown/hidden.
viewHolder.btn1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = (int) v.getTag();
Log.d(TAG, "Line in position " + position + " clicked");
if (!checkBoxSendChoice[position]) {
checkBoxSendChoice[position] = true;
viewHolder.layout_choice.setVisibility(View.VISIBLE);
} else {
checkBoxSendChoice[position] = false;
viewHolder.layout_choice.setVisibility(View.GONE);
}
}
});
However I noticed that the entire ListView is refreshed (getView is called multiple times for all rows), because of setVisibility(). If I comment out the two setVisibility() instructions, the ListView isn't refreshed anymore.
Is it possible to optimize and avoid refreshing all the views in the ListView?
I think there is a better way of doing this. Instead of editing the view directly, you should have a Boolean isVisible inside the list item and change that, then notify the adapter that an item has changed. This will make the holder re-bind to the item. And inside the holder's bind function you can set the view's visibility depends on the boolean. Here is a rough example (half pseudo code):
List<MyItem> items;
viewHolder.btn1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = (int) v.getTag();
Log.d(TAG, "Line in position " + position + " clicked");
checkBoxSendChoice[position] != checkBoxSendChoice[position];
items.get(position).isVisible = heckBoxSendChoice[position];
adapter.notifyItemRangeChanged(position, 1);
}
});
class MyItem {
boolean isVisible = true;
}
class holder {
View layout_choice;
private void onBind(MyItem item) {
if (item.isVisible) {
layout_choice.setVisibility(View.VISIBLE);
} else {
layout_choice.setVisibility(View. GONE);
}
}
}
By notifying the adapter with notifyItemRangeChanged, the adapter will know what items have been update and therefore will only refresh them.
If you want i'll be happy to edit my answer with a working tested example. Hope this helps!
I have been trying to solve this issue for a while but seems not to work. I am getting a nullPointer Exception on this Line school.setClasses(classesName.getText().toString());in the code block below.
Which is the code that actually posts to the SQLite Database
public void postSchoolSetuptoSQLite() {
school.setSchoolName(nameOfSchool.getText().toString());
school.setSchoolLocation(schoolLocation.getText().toString());
school.setClasses(classesName.getText().toString());
academeaSQL.addSchool(school);
if (demeaSQL != null) {
StringBuilder sb = new StringBuilder();
for (School s : demeaSQL.getAllSchools()) {
sb.append(" SchoolName= " + s.getSchoolName() + " SchoolLocation= " + s.getSchoolLocation()
+ " ClassName= " + s.getClasses());
sb.append("\n");
}
Log.i("Database content", sb.toString());
Toast.makeText(getApplicationContext(), "Added Successfully", Toast.LENGTH_LONG).show();
} else {
Log.i("Database Err", "Database Error");
}
}
I am creating the field for the classesName dynamically by clicking an "Add New Class" Button through this onAddField Method
public void onAddField(View v) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflater.inflate(R.layout.field, null);
rowView.setId(ViewIdGenerator.generateViewId());
// Add the new row before the add field button.
parentLinearLayout.addView(rowView, parentLinearLayout.getChildCount() - 1);
Log.i("ids", String.valueOf(rowView.getId()));
}
The field Id is dynamically generated through the ViewIdGenerator Class.
The Error is at this point classesName = rowView.findViewById(rowView.getId()); when getting the Ids from the dynamically created fields in this code block
public void findByIds() {
rowView = new View(this);
parentLinearLayout = findViewById(R.id.parent_linear_layout);
nameOfSchool = findViewById(R.id.nameOfSchool);
schoolLocation = findViewById(R.id.schoolLocation);
addSchoolAndMoveNext = findViewById(R.id.addSchoolAndMoveNext);
classesName = rowView.findViewById(rowView.getId());
Log.i("Classname", String.valueOf(classesName));
Log.i("SchoolLoca", String.valueOf(schoolLocation));
}
Here is where I am calling the findByIds();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_school_search_setup);
findByIds();
intitializeListeners();
initializeObjects();
}
For the Nullpointer exception. the error is pointing at these areas
school.setClasses(classesName.getText().toString());
and
classesName = rowView.findViewById(rowView.getId());
Please why am I getting the nullpointer exception and How can I resolve it. Thank you. I will really appreciate your responses.
Your problem is that if you are calling findByIds() inside of onCreate() then rowView.getId() will return null, because you are setting the id in the default id generator AFTER the button gets pressed. onCreate() gets called when the activity first starts up.
It also looks like you are trying to add a list of classes when you hit the button. There are built in ways to do this in Android that have a bit more overhead than what you are doing (in terms of time it takes to implement) but that are going to be much more reliable for scrolling, adding new classes, deleting classes, etc.
Look into the recyclerview in order to do this
Inside of the Display Cursor contents in a RecyclerView section here will show you how to add data from the database to the RecyclerView
I recognize that there are plenty of questions and answers about the notifyDataSetChanged() method already on stackoverflow, but I've reviewed most of them and can't figure out what could be wrong here. I'm trying to get my listview to dynamically add more lines as the user clicks on the "Add Ingredient Button". It will add the first ingredient after the first click, but any subsequent clicks do not result in any change to the list view. Any help is appreciated.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new_recipe);
ButterKnife.bind(this);
mAddInstructionsButton.setOnClickListener(this);
mAddIngredientButton.setOnClickListener(this);
adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, ingredientList);
mListView.setAdapter(adapter);
}
public void onClick(View v) {
if(v == mAddIngredientButton) {
if(mIngredientName.getText().toString().trim().equalsIgnoreCase("") || mIngredientMeasurement.getSelectedItem().toString().trim().equalsIgnoreCase("") || mIngredientCount.getText().toString().trim().equalsIgnoreCase("")) {
Toast answerStatus = Toast.makeText(NewRecipeActivity.this, "Fill out all fields", Toast.LENGTH_LONG);
answerStatus.show();
} else {
String ingredient = createIngredientString();
ingredientList.add(ingredient);
adapter.notifyDataSetChanged();
clearIngredientInputs();
Log.i("NewRecipeActivity", "List includes: " + ingredientList);
}
}
You need to add the new Ingredient to the Adapter's list, using the method
adapter.add(ingredient)
like this
String ingredient = createIngredientString();
adapter.add(ingredient);
adapter.notifyDataSetChanged();
Oh boy, I feel silly about this one. The code was working all along just as it was posted in my question. The listview element on my activity was set with a height of 52 pixels, so that any rows that were added to the listview did not appear.
Spent about 4 hours reading posts and changing up all kinds of stuff in the java file, and it was just an issue with the height of the display element. Cue sad trombone sound
Trudging my way through my introduction to Java and Android on a simple app and have run into an issue with ListView item selection. For one of my activities, I have a layout with two buttons, one of which is a "Delete" button, and a ListView of "passages" which are essentially timestamps for when a device has passed a sensor.
I have implemented the ability to click on an item to select it, which then enables the "Delete" button. A click of the "Delete" button removes the "passage" but I still end up with a selected item, which I don't want.
To implement selection, I added the following property to the ListView:
android:id="#+id/passagesListView"
android:choiceMode="singleChoice"
android:listSelector="#666666"
Selection is supported in OnCreate via a an OnItemClickListener:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_passages);
passagesViewAdapter = new PassagesViewAdapter(this, R.layout.passages_row_layout, passages);
final ListView passagesListView = (ListView) findViewById(R.id.passagesListView);
assert passagesListView != null;
final Button deleteButton = (Button) findViewById(R.id.deleteButton);
deleteButton.setEnabled(false);
buildPassageList();
passagesListView.setAdapter(passagesViewAdapter);
passagesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View view, int position,long arg3) {
Toast.makeText(ViewPassagesActivity.this, "position is " + position,
Toast.LENGTH_LONG).show();
view.setSelected(true);
passagesViewAdapter.notifyDataSetChanged();
selectedItemPos = position;
deleteButton.setEnabled(true);
}
});
}
This part works. However, there is some issue with deletion. As you can see in the comments, I have tried several methods that I found on StackOverflow that seemed to apply but, although I am able to delete the correct item from the list, I am still ending up with a selected item after the call to delete().
public void delete (View view)
{
final Button deleteButton = (Button) findViewById(R.id.deleteButton);
ListView passagesListView = (ListView) findViewById(R.id.passagesListView);
if(selectedItemPos != -1)
{
Toast.makeText(ViewPassagesActivity.this, "remove " + selectedItemPos,
Toast.LENGTH_LONG).show();
// This did not work, which is strange since it worked similarly for selection when clicked
// View itemView = passagesListView.getChildAt(selectedItemPos);
View itemView = passagesViewAdapter.getView(selectedItemPos, null, passagesListView);
itemView.setSelected(false);
// This was also recommended in various posts on StackOverflow.
// Not clear whether clearChoices applies only to checkBoxes?
// passagesListView.clearChoices();
// passagesListView.requestLayout();
passages.remove(selectedItemPos);
deleteButton.setEnabled(false);
selectedItemPos = -1;
passagesViewAdapter.notifyDataSetChanged();
}}
}
I also ran into some issues trying to track which item is selected via setSelected() and getSelectedItemPosition() and punted by just tracking the index myself. So, as I am new to this, I'm sure there is something I am not understanding about Views or maybe something else such as a misunderstanding of how selection works?
How can I clear the selection?
Thanks!
I don't know what your PassagesViewAdapter class looks like. Maybe you can try
passagesViewAdapter.remove(passages.get(selectedItemPos));
passages.remove(selectedItemPos);
deleteButton.setEnabled(false);
selectedItemPos = -1;
passagesViewAdapter.notifyDataSetChanged();
I have an EditText field that represents an ID number. That field can either be filled programmatically, using IDField.setText(String) based on the results of a card swipe, or it can be filled manually using the keyboard.
Once the text is filled both methods (auto login--based on swipe, or manual--based on button click) both run the same sign in script. However when I go to grab the contents of the EditText field, if I edited the text manually I get an empty string returned. If the text was set programmatically then it works perfectly.
This doesn't make any sense to me. Is there a reason that editText.getText().toString() would not return the content that is visibly shown in the textbox?
XML:
<Button
android:id="#+id/btn_swipeCard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/signInID"
android:layout_toLeftOf="#+id/textView1"
android:onClick="SignInStudent"
android:text="Swipe ID" />
Button Initialization:
IDField = (EditText) layout.findViewById (R.id.signInID);
LoginButton = (Button) findViewById(R.id.button1);
LoginButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { SignInStudent(); } } );
Card Swipe:
// displays data from card swiping
private Runnable doUpdateTVS = new Runnable()
{
public void run()
{
try
{
//Grab ID Number
String[] splitMSG = strMsrData.split("=");
//txtIDNumber.setText(splitMSG[2]);
IDField.setText(splitMSG[2]);
StringBuffer hexString = new StringBuffer();
hexString.append("<");
String fix = null;
for (int i = 0; i < msrData.length; i++) {
fix = Integer.toHexString(0xFF & msrData[i]);
if(fix.length()==1)
fix = "0"+fix;
hexString.append(fix);
if((i+1)%4==0&&i!=(msrData.length-1))
hexString.append(' ');
}
hexString.append(">");
myUniMagReader.WriteLogIntoFile(hexString.toString());
SignInStudent();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
};
Sign In Logic:
public void SignInStudent()
{
String temp = "http://wwww.tempUrl.com/signIn?ID="+ IDField.getText().toString() + "&DeviceId="+KEY;
webView.loadUrl(temp);
}
The layout is only updated during the onCreate phase of the loop. This is fired when an onResume event is called as well which explains why the fields update after you lock and unlock the device. There are a few workarounds for this such as doing more background processing and then creating a new view with correct values, or using a surfaceView that allows drawing to occur while the program is in its normal execute cycle.
For my application I either do background processing and then move to a new view, or have a view that just keeps calling itself to get the onCreate events to fire again. The solution depends on the application, but that's why the problem occurs.