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();
Related
I am new to android and my code has got a bit messy. I have successfully created a list view extended from item_layout.xml. When I click on the list view It works exactly how I want it. However in each item of the list view I have a button that when clicked I want the item of the list to delete.
When researching I have come across that you need to create a customer adapter to do this however I have come so far in the project that I wouldn't even know where to start.
This code it used successfully to when the list items are clicked it works. This is just put in the Main Activity class
mylist.setOnItemClickListener(
new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
}
}
);
I populate the list using this function just outside the main activity class. It is needed to be written like this as It gets the items from a database and has to be called depending on different circumstances
private void populatelistView() {
Cursor res = userDb.getAllRows();
String[] fromFeildnames = new String[]{ DatabaseUser.KEY_1, DatabaseUser.KEY_2};
int[] toViewIds = new int[]{R.id.textViewNum, R.id.textViewItem};
SimpleCursorAdapter myCursorAdaptor;
myCursorAdaptor = new SimpleCursorAdapter(getBaseContext(), R.layout.item_layout, res, fromFeildnames, toViewIds, 0);
//ListView mylist = (ListView) findViewById(R.id.listViewID);
mylist.setAdapter(myCursorAdaptor);
}
I would like to be able to get the button on each items to work by not changing much of what I have already written. I have tried just using the following code. But because it is in a different xml layout it display an error of null reference towards the item button
delete.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
View parentRow = (View) v.getParent();
ListView listView = (ListView) parentRow.getParent();
final int position = listView.getPositionForView(parentRow);
Toast.makeText(getApplicationContext(), "Button " + position, Toast.LENGTH_SHORT).show();
}
}
);
Please could someone help me make the button work without changing much code or give me a step by step tutorial on how to add an adapter but make my populateListView function do the same thing.
Ps. I have looked at so many tutorials about list adapters but can't find ones that are for my specific need
I have tried to click on the first item of my arraylist for few hours, I have read a lot of questions on Stack Overflow, tutorials and I have tried to implement them in many ways, but it doesn't work.
clickView(getSearchActivity().hintListView);
onData(anything())
.onChildView(withId(R.id.hintLayout))
.atPosition(0)
.perform(click());
onData(allOf(is(instanceOf(String.class))))
.atPosition(0)
.perform(click());
onData(instanceOf(ArrayList.class))
.atPosition(0)
.perform(click());
onData(instanceOf(String.class))
.atPosition(0)
.perform(click());
onData(anything())
.inAdapterView(withId(R.id.hintLayout))
.atPosition(0)
.perform(click());
onData(anything())
.inAdapterView(allOf(withId(R.id.hintLayout), isCompletelyDisplayed()))
.atPosition(0).perform(click());
I have to click on the first item of the ListView, that take ArrayList
id of this ListView is hintLayout.
Does anybody know what is the problem ? and where can it be ?
A ListView must be filled with an adapter. After your adapter is finished just add it to your ListView like this:
private void setListAdapter(ListDataAdapter adapter){
lvYourListView.setAdapter(adapter);
}
In order to get the data back out on the OnClick event you need to something like this in you onCreate() method:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lvYourListView = (ListView) findViewById(R.id.lvListview);
lvYourListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ListData data = new ListData();
data = (ListData)parent.getItemAtPosition(position);
doSomethingWithData(data);
}
});
If you need help with the adapter - just let me know. I will need to see more of your code in order to do that.
This is my problem:
https://youtu.be/k-N5uthYhYw
and this is my onBindViewHolder() method.
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
holder.specName.setText(specList.get(position).getSpecName());
// Assign a tag number to later identify what radio-button
holder.specRadioBtn.setTag(new Integer(position));
/* Event listenr for longClick - we prob. won't use it, but it's here just in case */
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Toast.makeText(context, "Long press", Toast.LENGTH_SHORT).show();
return false;
}
});
/* Little hack to select its Radio Button when a specific row is tapped */
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Turn rowSelectedFlag to true since the user selected this row
rowSelectedFlag = true;
// When the user taps on a row select that row's radio button
holder.specRadioBtn.setChecked(true);
// I'm not sure why, but locally the interface needs to be started by pointing it
// to where it should drive the data (to send the params)
tempInterface = new AdminUserSpecialty();
// Call the interface to send the data (row spec-name and id) back to AdminUserSpecialty
tempInterface.activateSpecSelect(specList.get(position).getSpecName().toString(),
specList.get(position).getSpecId().toString(), rowSelectedFlag);
int clickedPos = ((Integer) holder.specRadioBtn.getTag());
// Check if the radio button is already selected
if (holder.specRadioBtn.isChecked()) {
if (lastCheckedBtn != null) {
// Don't deselect if user taps on the same row several times
if (lastCheckedBtn == holder.specRadioBtn) {
// do nothing
}
// Otherwise do deselect the previously selected radio button
else {
lastCheckedBtn.setChecked(false);
}
}
lastCheckedBtn = holder.specRadioBtn;
lastCheckedPos = clickedPos;
}
// If radio is not checked set the lastCheckedBtn to null (reset counter)
else {
lastCheckedBtn = null;
}
}
});
/* ----------------------------------------------------------------------*/
}
I can't seem to preserve my radio-button selection on RecyclerView scroll. On scroll the selection becomes erratic and random. I understand that one of RecyclerView's features is to recycle rows as they leave the screen, but what do I need to do to keep my selection? Thanks much.
I know that this was answered already but if some of you are still looking for an easier answer and your application does not rely on the RecyclerView view recycling feature much (for example if you have a fixed size list of items...) you can always set your recycler view cache view size. That way it would not recycler your views hence it would not recycler the views and you will avoid copy selected values to another views...
yourRecyclerView..setItemViewCacheSize(yourItemList.size());
Save the checked / unchecked status of the radio button (you should use checkbox instead if you want to allow the user to select multiple items) to your model (i.e. your items in the list should have a field for this) when the onClick event happens. When you bind the ViewHolder, make sure you set checkbox's value to whatever you saved in your model.
It's happen because of the Recycling mechanism
(PS: its the same for the ListView or RecyclerView).
To fix that:
1) Add a booelan variable to your model to save the state of the RadioButton
2) Update your RadioButton state in onBindViewHolder() method from this boolean in the model.
3) Add setOnCheckedChangeListener() to your RadioButton to listen to his state (checked/unchecked) and to update the boolean in your model when the state changes.
I targeted the items in an action bar using ShowcaseView, but I can't target elements of a ListView! I tried that and it didn't work:
ShowcaseView sv = new ShowcaseView.Builder(this,true)
.setTarget(new ViewTarget(lv.getChildAt(1).findViewById(R.id.heart)))
// Here is where you supply the id of the action bar item you
// want to display
.setContentTitle("Heart")
.setContentText("Check the venues of your city")
.hideOnTouchOutside()
.build();
Very simple. Take the element in your ViewHolder then place the code for showing the ShowCaseView inside your bindView.
Don't forget to put different a different ID for each element in the list.
So you may try something like this.
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView button;
public ViewHolder(final View itemView) {
super(itemView);
button = (Button) itemView.findViewById(R.id.list_button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new MaterialShowcaseView.Builder(getActivity())
.setTarget(verifyButton)
.setDismissText("GOT IT")
.setContentText("Your text goes here")
.setDelay(100) // optional but starting animations immediately in onCreate can make them choppy
.singleUse(id + " define an unique id for each item of the list") // provide a unique ID used to ensure it is only shown once
.show();
}
});
}
}
}
To get more information you can check the library here.
I'm trying to set a header and footer in my list view that are clickable buttons. The problem is that the OnClickListener isn't responding to anything and I can't figure out what I'm doing wrong.
$ View header = getLayoutInflater().inflate(R.layout.header_layout, null, true);
getListView().addHeaderView(header);
myAdapter = new myAdapter(this);
header.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do what I want when i click it
}
});
Update
The best solution I ultimately came up with was adding a separate button to the header layout, and then doing it like this:
View header = getLayoutInflater().inflate(R.layout.header_layout, null);
Button headerButton = (Button)header.findViewById(R.id.header_button);
getListView().addHeaderView(header);
headerButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// My Click Stuff
}
});
You need to handle the click in the ListView's onItemClick. Simply check if that's the first or last item in the adapter and handle the clicks that way. You need to treat it as an item in the ListView.
I see a few issues:
when inflating the header, use getListView() as the second parameter (root, where you have null now)l
should the header be a View or a ViewGroup? I've ended up using ViewGroup in these situations.
finally -- perhaps you should be setting the click listener on the button in the header instead of the header itself?
There is a way more easier solution:
Just set a "OnClickListener" to the applied View:
View view = inflater.inflate(R.layout.xxx, null);
view.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
//do something
}
});
Very easy thing which solved it!
Example with a footer:
mYourListView.addFooterView(footer, null, true);
Then in the OnItemClickListener you can check:
#Override
public void onItemClick(AdapterView<?> parent,
View view, final int position, final long id) {
if (id != -1) {
// do whatever you do with list items
} else {
// do what you need after the footer been clicked
}
(If you need to handle and the header and the footer click, check position - 0 for the header and [adapter.getCount() - 1] for the footer)
This approach will provide the same visual effect while footer click as if the list item been clicked. (But if you do not need that effect just add OnClickListener to the footer and it will intercept all footer clicks)
i think the listview and buttons are fighting for focus and your listview is winning.
you'll have to use a onTouchListener for the buttons.
There is one more alternative and its to set the following on the footer. There is another method overload created but it did not come up in the docs, i had to check online documentation:
mylistView.addFooterView(footerView, null, false);
where false tells the footer its not selectable. I tested this myself and the buttons inside the footer respond to touches now. I hope this an acceptable answer.
What worked for me:
When inflating the header view, before adding it to the listview, I then used that view to get the subview and add a click listener on it
myHeaderView.findViewById(R.id.myButton).setOnClickListener(new OnClickListener() { ... } );
also, when I added it to the listView, I used the overloaded constructor with the third variable set to true
mListView.addHeaderView(myHeaderView, null, true);