I have a RecyclerView in which a selected number of items show a different background color.
It gave me with many views with different color.
I used the position of adapter to change color and later I found that position changes when scrolling.
So I gave a unique code to every object of the ArrayList. And I gave the program to change background color if the unique code matches the given code.
Like,
uniqueCode = 5;
public void onBindViewHolder(//..){
Object object = objectArrayList.get(i);
if(object.uniqueCode() == uniqueCode ){
holder.layout.setBackgroundColor(//....);
}
}
But still I get some views changes the background color on scrolling which does not match that unique Id.
What is the solution to this problem ?
If i understand correctly there are multiple cells with the "unique" color but it shouldn't. This happens because you never reset the background of the layout to the default color.
if(object.uniqueCode() == uniqueCode){
holder.layout.setBackgroundColor(/*unique color*/);
}
else{
holder.layout.setBackgroundColor(/*default color*/);
}
I'm learning android programming, following a book's examples, but I'm still a beginner in this field.
The aim of the book is to build a progressively more sophisticated "note taking" app, starting from a very simple one then each time adding features.
I got a working app that allows adding notes, deleting notes: each note added becomes an item in a listview, and has a title, a text, and some boolean flag like "idea", "todo" and "important". Adding notes having the "important" boolean flag set, makes an "important" icon visible.
Internally, notes are stored in a List (see code later).
Each note, tapping its listitem, can be also opened in a modal showing its properties, which has a button to delete the shown note.
Pressing that delete button, the modal is dismissed, the note is deleted, and the item in the listview is removed.
This works.
Now I'm following the next step: adding a "flashing" animation for items (notes) with the "Important" flag set.
Regular notes' items are not animated, but "important" ones are constantly "flashing" when they appear in the listview.
This works.
Removing the notes, one by one, still works, and each item is removed.
But, if I remove all notes, and the last one is flashing
- this last note is effectively "removed" from the list
- but the listview still shows (only) its animation!?!
But the flashing animation, showing the last note (deleted) title and text in the listitem, cannot be clicked... inspecting the code it is effectively gone fro the List, but not its flashing animation.
Btw, the book at this point doesn't cover deleting notes, and that's a feature I added on my own.
the modal showing the note has, as said a btnDelete, that works like this:
btnDelete.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View view) {
new AlertDialog.Builder(getContext())
.setMessage("Really delete this note?")
.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
//nothing
}
}
)
.setPositiveButton("Ok",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
MainActivity mainActivity = (MainActivity) getActivity();
mainActivity.deleteNote(getNoteId());
String msg = "Note " + getNoteId() + " deleted (" + mNote.getTitle() + ")";
Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
dismiss();
}
})
.show();
}
}
);
I tried adding and deleting many notes, flashing and not, but the only way to get this "last" flashing ghost is to remove all notes but remain with one last flashing note.
Then, after removing that last flashing note, the flashing ghost remains.
I can have any number of flashing notes, and maybe one normal note but if I delete all notes leaving a normal one, that is deleted just fine: leave a last flashing note and, even if it is deleted, the flashing ghost remains.
When this ghost flashing animation lasts forever, if I add a new note, I can see the new listitem note text and icons over the flashing ghost animation, since it is added at first place... there are two animation overimposed, one is the old "ghost" and the other is the "regular" new note...
Now, some more code:
The listview is
listNote = (ListView) findViewById(R.id.listView);
and is "ruled" by a mNoteAdapter object
listNote.setAdapter(mNoteAdapter);
which extends BaseAdapter, overriding needed methods and also has two methods to add and remove notes
public void addNote(Note n) {
noteList.add(n);
notifyDataSetChanged();
saveNotes();
}
public void deleteNote(int i) {
//(I tried getting the view and canceling animation, setting it to null.. nothing works)
//View listItem = listNote.getAdapter().getView(i, null, listNote);
noteList.remove(i);
notifyDataSetChanged();
saveNotes();
}
the added notes are stored in a list:
List<Note> noteList;
the main overridden method is getView, which is "magically" called by the adapter when a node is added, and creates the listitem view, including the flashing animation if necessary.
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater)
getSystemService(getApplicationContext().LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.listitem, viewGroup, false);
}
TextView txtTitle = (TextView) view.findViewById(R.id.txtSettingsTitle);
TextView txtDescription = (TextView) view.findViewById(R.id.txtDescription);
ImageView ivImportant = (ImageView) view.findViewById(R.id.imageViewImportant);
ImageView ivTodo = (ImageView) view.findViewById(R.id.imageViewTodo);
ImageView ivIdea = (ImageView) view.findViewById(R.id.imageViewIdea);
Note tmpNote = noteList.get(i);
if (mAnimationSpeed != SettingsActivity.NONE && tmpNote.isImportant()) {
view.setAnimation(mAnimFlash);
} else {
view.setAnimation(mAnimFadeIn);
}
if (!tmpNote.isIdea()) ivIdea.setVisibility(View.GONE);
if (!tmpNote.isImportant()) ivImportant.setVisibility(View.GONE);
if (!tmpNote.isTodo()) ivTodo.setVisibility(View.GONE);
txtTitle.setText(tmpNote.getTitle());
txtDescription.setText(tmpNote.getDescription());
return view;
}
I don't need the flashing feature, of course, but I wish to understand what's happening and possibly solve it.
the "flashing" animation (mAnimFlash) xml is:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha android:fromAlpha="0.0"
android:toAlpha="1.0"
android:interpolator="#android:anim/accelerate_interpolator"
android:repeatMode="reverse"
android:repeatCount="infinite"/>
</set>
while the "fading in" (mAnimFadeIn, which is not causing any issue) animation xml is:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="500"
android:interpolator="#android:anim/accelerate_interpolator">
</alpha>
</set>
Can anyone help me understand why that ghost animation remains, just in that case, and how can I better implement this kind of thing?
This is a bit speculation on my part, I'm not exactly sure but my guess is that this behavior is the result of the interaction between:
The way ListView recycles and caches views for items that are no longer visible
That the animation lasts 'forever'
The property being animated is one of the properties used to 'hide' the cached views that ListView is saving for later.
A ListView is a container that arranges child views so that it 'seems' that all the items are visible. The items that would fall outside the ListView are not really there. This is done for performance reasons (speed and to save memory). When the user scrolls and a view falls out of view it is 'recycled' and put in stand-by until another item comes into view to be reused for that new item. It gets passed as the 2nd parameter of the getView() function.
This means that usually there are more views than visible items. There can be space for 10 items, and there will be 10 visible items and 1-3 hidden extra ones that will be used for items that come into view. (see How ListView's recycling mechanism works).
One drawback of ListView is that there is no easy way to detect when a view is being recycled (it becomes hidden). Usually we have to wait until it's reused for another item. RecyclerView does have events to handle when a view gets recycled.
In your case we can see this happening when you're setting the animation. If you set the mAnimFlash on an item, the only time it gets stopped is when the view is reused for another item that not 'important' and it gets the mAnimFadeIn animation (replacing the flashing animation).
If you commented out the call to view.setAnimation(mAnimFadeIn) you would notice that as you scroll down some items are blinking when they shouldn't. That's because there's no place where the view is told to stop blinking, even after being assigned to another item in the list.
Now to item 3 of the list above, the 'alpha' property. You are animating the 'alpha' property of the view. So if the ListView is making the view transparent to hide it from view, then after the ListView hides it by making it transparent, the animation is making it visible again by changing the values of the alpha transparency.
More thoughts on this:
If the animation worked on a property of a child item of the view, instead of the 'main' view, probably the view would remain hidden. Or the animation changed something else like the text color.
Additional info/topics:
Further investigation can be done with the Layout Inspector, which you can use to check where are the views and their properties. So you could for example check what children views are inside the ListView and what are their properties.
You can use different layouts for different items. See the getItemViewType() and related functions of the Adapter class. You can specify for example that there are 2 types of views: 'important' and 'not important' and the views passed as convertView will match the type for that item, and you won't have to make the choices in getView(), but isolate the decision of type in getItemViewType().
Also take a look at RecyclerView, since it has better handling for a few situations where ListView falls short. For example, the adapter for a RecyclerView has a method onViewRecycled() that lets you know that a view is no longer used, and you can stop animations there.
Currently I have a GridView that holds some ImageViews. The GridView implements setOnItemClickListener. When the onItemClick occurs, the user gets a dialog and on back pressed or on dismiss, he returns to the current GridView.
Till now everything works fine.
I would like to know if there is a way to change the selected imageView (with another image, or even just the alpha).
I have tried inside the setOnItemClickListener to change the image, but when I try to change it back to it's normal image after the dialog.dismiss() call, a white screen pops up for some milliseconds. Although, the image is set correctly, I would like to avoid this white image.
I have tried to implement the GridView.setOnTouchListener, but I would get only a white screen (for some milliseconds) and no other operation would be executed (no pop up dialog).
I even tried to implement OnTouchListener inside the ViewHolder class and although it seemed to work with some problems (changing the alpha on ACTION_DOWN was permanent, so I had to reselect the same item to change the alpha back to normal), the setOnItemClickListener wouldn't work at all.
Can someone help me with this?
Thanks in advance!
Just for anyone having the same problem, I used TransitionDrawable. Inside my imageAdapter, I loaded the transition xml files:
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/soft"/>
<item android:drawable="#drawable/soft_pressed"/>
</transition>
and on gridview.setOnItemClickListener, I used this sample of code:
ViewHolder holder = (ViewHolder) v.getTag();
final ImageView imageview = holder.image;
((TransitionDrawable)imageview.getDrawable()).startTransition(2000);
And to execute the next command (some delay is needed):
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
YourMethod(...);
//reverse the transition
((TransitionDrawable)imageview.getDrawable()).reverseTransition(2000);
}
}, 2000);
I have to show list view as like below image
Here I have the view in the xml as like this
<View
android:id="#id/margin"
android:background="#6DAAE9"
android:layout_width="20.0dip"
android:layout_height="fill_parent" />
I Have to change this background (#6DAAE9) dynamically when list is loaded and I have to fix these colors to corresponding item even the list items are increased.
Can any body help me. Thanks in advance
Basiclly you need to edit your getView method.
you can use
convertView.setBackgroundColor(Color.parseColor("#6DAAE9"));
With your appropriate logic, meaning if you want it to be random set random color or hold an array with the order, etc.
See this listview I added list item as per odd or even row and you can do it checking position in getView() method.And you should put imageview for that and set color rather that set color to row.
if(position==0){
//here set color for imageview which position is 0
}else if(position==1){
//here set color for imageview which position is 1
}
....
I'm trying to restore the background Color of a View.
I have several selectable Views. When the user clicks one of those Views, the following code is executed and the View becomes Yellow:
View newSelection, previousSelection;
...
if(previousSelection != null) {
previousSelection.setBackgroundColor(Color.BLACK); // problem here
}
newSelection.setBackgroundColor(Color.YELLOW);
However, I want to reset the color of the previously selected View. However, I do not know which color it was (I'm setting it to Color.BLACK in the above code). I was not able to find a getBackgroundColor or similar method in the View class. If I had it, I could save the previous color and just put it back when the new View is selected.
use View.getBackground(), it returns the current 'Drawable' background of the view which can then be used in View.setBackgroundDrawable()
View theView;
Drawable originalBackground;
...
originalBackground = theView.getBackground();
theView.setBackgroundColor(Color.YELLOW);
...
theView.setBackgroundDrawable(originalBackground);
I'm not sure exactly what you are trying to accomplish but perhaps a ColorStateList would come in handy here.
You can try setting the previous color as a tag of the view.
For example
View newSelection, previousSelection;
newSelection.setTag(Color.Green);
previousSelection.setTag(Color.Black);
if(previousSelection != null) {
previousSelection.setBackgroundColor((int)previousSelection.getTag());
}
newSelection.setBackgroundColor(Color.YELLOW);
I haven't tried the code if there is an error but the flow on how to implement is there.