I've a recycler view on my app. I'd like to prevent multi selections on my recyclerView, because I'm getting unexpected output while trying to delete items.
holder.mView.setOnClickListener(v -> {
if (null != mListener) {
mListener.onPreviewItemClicked(position);
if (holder.isSelected) {
holder.mView.setBackgroundColor(Color.TRANSPARENT);
Log.d("positionClicked adapter",position+"");
holder.isSelected = false;
isSelected = holder.isSelected;
} else {
holder.mView.setBackgroundColor(ContextCompat.getColor(mContext, (R.color.accent_light)));
holder.isSelected = true;
positionClicked = position;
isSelected = holder.isSelected;
}
}
});
On my code, I use is Selected boolean to catch if something is clicked. When I click on an item, background of its changes. The thing is :
If I click on item1, then on item2, both items have backgrounds changed, and I'd like to have only one. And when I click on item, code below is done. But then, If i click again on item2, it goes transparent, which is good ! But the position of my item selected is wrong ( should be 1, but it's 2 because last item selected was 2). So when I delete, it will delete 2nd item and not first one...
I delete using positionClicked variable.
Any idea on how to fix this ?
You should keep if item is selected in your model, and use it in the onBindViewHolder to setup background for the ViewHolder. In OnClickListener just set true for the specific data item, false for all others and then call notifyDataSetChanged() to rebinf all items.
Related
I want to Highlight a single row of recyclerview with sound one after another and scroll the highlight row to top of screen This is what i have done:
Here its code:
fun AnimateToSurahAlFeel(recyclerView: RecyclerView, layoutManager: LinearLayoutManager, currentPosition: Int) {
var position: Int = currentPosition / 1000
when (position) {
0 -> {
recyclerView.smoothScrollToPosition(0)
recyclerView.getChildAt(0).isSelected = true
}
4 -> {
recyclerView.smoothScrollToPosition(1)
recyclerView.getChildAt(0).isSelected = false
recyclerView.getChildAt(1).isSelected = true
}
11 -> {
recyclerView.smoothScrollToPosition(2)
recyclerView.getChildAt(1).isSelected = false
recyclerView.getChildAt(2).isSelected = true
}
17 -> {
recyclerView.smoothScrollToPosition(3)
recyclerView.getChildAt(2).isSelected = false
recyclerView.getChildAt(3).isSelected = true
}
21 -> {
recyclerView.smoothScrollToPosition(4)
recyclerView.getChildAt(3).isSelected = false
recyclerView.getChildAt(4).isSelected = true
}
28 -> {
recyclerView.smoothScrollToPosition(5)
recyclerView.getChildAt(4).isSelected = false
if (recyclerView.getChildAt(5) != null)
recyclerView.getChildAt(5).isSelected = true
}
}
}
In the Function currentPosition is Media player current position
Problem in this code is:
In the Screen shot Row 4 and 5 are currently not visible,when highlighting Row 4 and 5 the App crash and giving Null Pointer Exception, according to my knowledge these two row are not yet created that's why
recyclerview.getChildAt(4) or recyclerview.getChildAt(5) return null and that cause the App crash.
Now
How to fix the App crash that recyclerview.getchildAt(4) or recyclerview.getchildAt(5) return null and also getChildAt(position) return n-1 row, so the App crash at recyclerview.getchildAt(5) will occur anyhow but i want n Row because i want to highlight all rows
How to scroll the highlighted row to position 0 (at top)
i.e. Row 0 go up from screen and Row 1 take it position and so on...
I want to achieve like this the highlighted one is at top and that will go off from screen when another row is highlighted
You need time for View to Bind. Just for ex. you can post action.
....
17 -> {
recyclerView.smoothScrollToPosition(3)
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
recyclerView.getChildAt(2).isSelected = false
recyclerView.getChildAt(3).isSelected = true
}, 500);
}
....
But I strongly recommend you use some state Collection, which will save and handle states of your running and showing Views.
This is because recycler views don't have all the views inflated, but only the visible ones. This is by design and should not be tinkered with. Personally, I think you should use the recycling functionality.
You need to make the selected state part of your model in the adapter - the items in the adapter. Let's say this is called RowItem, so in your adapter you'd have a list of RowItems for example. Aside from the text in both languages, you need to add the selected state too. Then it's just a matter of getting the list's adapter, setting the correct position to selected and deselecting the ones you want.
For example, in your adapter:
fun select(position: Int) {
data[position].selected = true
notifyItemChanged(position)
// deselect all other positions that you don't want selected
}
When you bind the view holder you could do then:
public void onBindViewHolder(ViewHolder viewHolder, int position) {
val item = data[position]
viewHolder.itemView.selected = item.selected
// take care of the rest of the views
}
data would be a list where you store your RowItems
Now you can scroll with no problem and set the item to selected. Once the view is visible in the recycler view, the adapter will set the correct state.
It's fair to say I'm guessing a bit since there's no adapter code in your question, but the idea I think it's easy to understand. You need to change the state in the adapter and let the implementation of the recycler view handle it for you. After all the purpose is to get the recycler view to recycle your views based on the models adapted by the adapter.
Remember you can always get your adapter from the recycler view itself. In the end you can do something like this:
...
0 -> {
recyclerView.smoothScrollToPosition(0)
(recyclerView.adapter as MyAdapter).select(0)
}
Here MyAdapter would be the class name of your adapter
For the scrolling part you can take a look at this
Okay so I've been able to hightlight an item in a ListView, but it ends up highlighting every fourth item. I'm pretty sure that's because of recycling. Then I had the issue where the highlighted item would go back to normal after I scrolled, and that's also because of recycling. Is there any way to keep it highlighted, or to maybe stop the ListView from recycling?
This is what the code looks like right now...
runTimes.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> list, View v, int pos, long id) {
v.setSelected(true);
}
});
This is the code where the highlighted item goes back to normal after you scroll.
to hilight a row you should not touch the view at all. you should use listviews setItemChecked with a selector as the background of your view.
runTimes.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> list, View v, int pos, long id) {
runTimes.setItemChecked(pos,true);
}
});
you also need to make sure you keep track of the last position you selected so that you can deselect it when you select a new one
If you want to stop ListView from recycling you should think again if you really need a ListView.
To properly accomplish this with a ListView, though, you need to save the highlighted item states inside of your adapter. Then in getView highlight the items based on their position.
There have been A LOT of questions about saving the state of the ListView items, I'm sure you can find some.
I ended up finding another question that helped me figure out how to do it. This is what I did. In the OnClickListener I check to see if something has been pressed before. If it hasn't been pressed before, then I set the views background color, and prevRunView to the view. If something has been pressed before, then I change the previous view background color to white. After that I do the same thing as before, but for the different view.
if(runIndex == -1){
runIndex = pos;
v.setBackgroundColor(Color.parseColor("#A6A6A8"));
prevRunView = v;
}else{
prevRunView.setBackgroundColor(Color.parseColor("#FFFFFF"));
runIndex = pos;
v.setBackgroundColor(Color.parseColor("#A6A6A8"));
prevRunView = v;
}
In my adapter I wrote this code so it won't seem like it's recycling.
if(ScoreActivity.runIndex == position)
v.setBackgroundColor(Color.parseColor("#A6A6A8"));
else
v.setBackgroundColor(Color.parseColor("#FFFFFF"));
Apparently the notifyDataSetChanged() only updates the visible items in the listview, I have a system that changes the background color of an item when its clicked (to dkgray), and I set everything else to transparent(the default), however other items that aren't visible remain selected(dkgray) (I only want the currently selected item to be dkgray). Is there a way to force notifyDataSetChanged() to update all items.
Here's example:
//makes all item backgrounds transparent
public void resetListViewBackground(){
for (int i = 0; i < listView.getChildCount(); i++){ //parent.getChildCount()
listView.getChildAt(i).setBackgroundColor(Color.TRANSPARENT);
}
}
//reloads the listview
private void reloadListView() {
listItems.clear();
adapter.notifyDataSetChanged();
listView.invalidateViews();
ArrayList<HashMap<String, String>> notesArrayList = dbTools.getAllNotes();
for (int i = 0; i < notesArrayList.size(); i++){
String temp = "";
if (notesArrayList.get(i).get("note").length() > 51){
temp = notesArrayList.get(i).get("note").substring(0,50).toString() + "...";
} else {
temp = notesArrayList.get(i).get("note").toString();
}
listItems.add(temp);
adapter.notifyDataSetChanged();
}
}
Everywhere I call resetListViewBackground(), I call reloadListView() after.
And this is what I use to highlight the selected item.
view.setBackgroundColor(Color.DKGRAY);
Also, the most common occurrence of this problem is that every 6th item is highlighted. The listview only shows about 4 items at a time.
The getChildCount() method won't work for all list items as view recycling is done.
Astral is right as he writes in the comments.
1) Create a custom adapter.(See http://windrealm.org/tutorials/android/listview-with-checkboxes-without-listactivity.php)
2)Inside the onListItemClick listener's onItemClick() method, call your adapter's notifyDataSetChanged() method(whenever the user clicks a list item).
I modified the project that I mentioned and posted it on my Dropbox.(Just import it in Eclipse and run).
Check it out at https://www.dropbox.com/s/gchccjzpkxk8n2z/Planets_modified.zip
Basically, I have a bug where some of my list view's children will appear selected, when in fact they are not. For example, If I enter ActionMode and select the first child only, several other children will appear as though I've selected them, but aren't actually selected. Why is this happening?
public void onItemCheckedStateChanged(ActionMode mode, int position,
long id, boolean checked) {
final int checkedCount = lv.getCheckedItemCount();
mSelectedConvCount.setText(Integer.toString(checkedCount));
if (checked) {
lv.getChildAt(position).setBackgroundColor(
Color.BLUE);
} else {
lv.getChildAt(position).setBackgroundColor(
Color.TRANSPARENT);
}
}
}
Remember: In a ListView, anything you set or change in any child views must be set every time they are displayed; all you are doing in the code provided is setting it initially. This means your adapter must also setBackgroundColor as appropriate, e.g. in bindView(). Otherwise, when the view is recycled it will retain the previous background color and appear selected when it is not.
In my application i have put listfield on the screen. Now my problem is that when i am clicking the list item (while list item has the focus on it) it does activities asopen the menu.
I want to disable the menu while clicking list. I want that if i click the list item it should perform only the necessary task defined in the item click event, Not shown the menu with it.
Any body has some idea about about that?
Override the navigationClick() for your ListField to consume the click event (should return true) without calling super.navigationClick():
protected boolean navigationClick(int status, int time) {
Status.show("Clicked on item: " + myList.getSelectedIndex());
return true;
}