ListView - CHOICE_MODE_MULTIPLE_MODAL - Random items are being selected - java

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.

Related

Only one item may be clicked on recyclerView android

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.

Java: How to make RecyclerView item number change to the same number in Activity? (real time)

As the title says, I need to make RecyclerView item number, change to that same number, which is in that same Activity (in real-time). So when the number in Activity changes, also that number which is in the RecyclerView item, should change to that same number.
When the game starts, par number and those numbers in RecyclerView are the same as they should but ...
Now the 2nd hole par number changes to 3, but those RecyclerView item numbers are still 4, but instead, they should be also 3. How Can I make this change happen?
Here is my adapter:
#Override
public void onBindViewHolder(#NonNull GameViewHolder holder, int position) {
GameItem currentItem = mGameList.get(position);
holder.mTextPlayer.setText(currentItem.getText1());
holder.mTextPar.setText(currentItem.getText2());
/** If persons par number is smaller than course par number, then change persons par number background to blue **/
if (Integer.parseInt(holder.mTextPar.getText().toString()) == 1) {
holder.mTextPar.setBackgroundResource(R.drawable.border_box_yellow);
} else if (Integer.parseInt(holder.mTextPar.getText().toString()) < Integer.parseInt(ActivityGame.mParNm.getText().toString())) {
holder.mTextPar.setBackgroundResource(R.drawable.border_box_blue);
} else if (Integer.parseInt(holder.mTextPar.getText().toString()) > Integer.parseInt(ActivityGame.mParNm.getText().toString())) {
holder.mTextPar.setBackgroundResource(R.drawable.border_box_red);
} else if (Integer.parseInt(holder.mTextPar.getText().toString()) == Integer.parseInt(ActivityGame.mParNm.getText().toString())) {
holder.mTextPar.setBackgroundResource(R.drawable.border_box_green);
}
Here in my GameAdapter, where I set those items background colors (according also to that par number) I tried to do:
holder.mTextPar.setText(ActivityGame.mParNm.getText().toString());
Meaning that I tried to set the number according to Par number, but it didn't change at all, it just changed the background of the items, but number stayed the same.
Do two simple things.
Create a method in the adapter-
public void updateAdapterData() {
notifyDataSetChanged();
}
Next, add the below method in the activity and call it when there is a change in the par value in your activity.
private void updateGameArrayList(int parVal) {
for(GameItem model : GameList) {
model.setText2(parVal);
adapterVariableInActivity.updateAdapterData();
}
}
These line of code worked for me.
UPDATE THE DATA: The only things I had to do is
myAdapter.notifyDataSetChanged();
1- You have to implement this in your adapter.
2- You had to do all of this in the Actvity/Fragment code not in the RecyclerView Adapter code.
I hope it helps you!
You have to add a integer field in GameList Model that you fix at the time of adapter as same as activity value.
class GameList{
String testPar;
}
then if the text is changed in Activity you have to change the GameList objects of all List to current text of activity and call
// index in which you want to change text in list
gameList.get(index).setTestPar(currentTextOfActivityTextView);
adapter.notifyDataSetChanged();

How to Highlight Single Row or Item of Recycler View and Scroll the Highlighted Row to Top of screen

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

Highlight selected ListView item

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"));

Focus ScrollView to selected position programmatically - Android

I have a ScrollView with a lot buttons. Each button is enabled when the user unlocks that button/level. I would like to focus the ScrollView on the latest unlocked button/level. See the screenshot below.
I found some functions like scrollTo() that could focus the ScrollView either at top, button or something like that but I would like to focus the ScrollView at certain places like the button ID that says Level 8 in the screenshot below. How would I go about doing this?
I think this should be good enough :
yourButtonView.getParent().requestChildFocus(yourButtonView,yourButtonView);
public void RequestChildFocus (View child, View focused)
child - The child of this ViewParent that wants focus. This view will contain the focused view. It is not necessarily the view that actually has focus.
focused - The view that is a descendant of child that actually has focus
First you need to know the position of item in scroll that has to get focus once you know that you can use following code to make that item focus
final int x;
final int y;
x = rowview[pos].getLeft();
y = rowView[pos].getTop();
yourScrollView.scrollTo(x, y);
refer this question
I agree with the benefits of previous answers. However, by experience, I have found this to be more complex than this. The setSelectionFromTop is very sensitive and often breaks if it is executed too early. This may depend on different reasons but the two primary reasons are that
If executed from within Activity lifecycle methods the views have not been loaded/configured yet.
View modifications triggered after the list move action seems to break the move. Probably overwriting some value before the move has been finalized due to a recalculation of the views.
The reasoning seems to apply for both setSelectionFromTop and setSelection() methods although
I tested mostly with setSelectionFromTop. smoothScrollToPosition() seems to be more robust, probably because it by definition changes the list position delayed whn doing the smooth scrolling.
Look at this example source code:
// Wee need to pospone the list move until all other view setup activities are finished
list.post(new Runnable(){
#override
public void run() {
list.setSelectionFromTop(selectedPosition, Math.max(0, Math.round(list.getHeight() / 3))); // Make sure selection is in middle of page, if possible.
// Make sure you do not modify (this or other) parts of the view afterwards - it may break the list move
// activityButtons.setVisibility(View.GONE);
}});
see: http://developer.android.com/reference/android/widget/ListView.html#setSelectionFromTop(int, int)
'int selectedLevel' holds the currently selected level, information is held in a Level class.
'values' is a list with all your levels, displayed by the list.
Example:
ListView lv = (ListView)findViewById(android.R.id.list);
int i = 0;
for (Level l : values) {
i++;
if (l.level == selectedLevel) {
lv.setSelectionFromTop(i, 200);
break;
}
}
try this
sc.post(new Runnable() {
public void run() {
sc.smoothScrollTo(x, y);
}
});
x the position where to scroll on the X axis
y the position where to scroll on the Y axis
Use this code, taken from this answer
private final void focusOnView(){
new Handler().post(new Runnable() {
#Override
public void run() {
your_scrollview.scrollTo(0, your_EditBox.getBottom());
}
});
}
Use this code, taken from my another answer
scrollViewSignup.post(new Runnable() {
#Override
public void run() {
int scrollY = scrollViewSignup.getScrollY();
scrollViewSignup.scrollTo(0, 0);
final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, true);
int new_scrollY = scrollViewSignup.getScrollY();
scrollViewSignup.scrollTo(0, scrollY);
scrollViewSignup.smoothScrollTo(0, new_scrollY);
}
});
This code tries to smooth scroll and uses the standard behaviour of system to position to an item. You can simply change smoothScrollTo with scrollTo if you don't want the system to smooth scroll to the item. Or, you can use the code below only.
scrollViewSignup.post(new Runnable() {
#Override
public void run() {
scrollViewSignup.scrollTo(0, 0);
final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, true);
}
});
Use the desired code block after trying, or, according to the need.

Categories

Resources