When I use this, it removes one element with animation
{
notificationItems.remove(0);
adapterForNotification.notifyItemRemoved(0);
adapterForNotification.notifyItemRangeRemoved(0,count-1);
}
But, when I use this, it removes all element without animation
count = adapter.getItemCount();
for(int i = 0 ; i < count; ++i){
notificationItems.remove(0);
adapterForNotification.notifyItemRemoved(0);
adapterForNotification.notifyItemRangeRemoved(0,count-1)
}
As I can understand, you are able to remove items, but you need to add sort of animation while removing.
It can be done by deleting a single item at a time with a single animation for each item.
For instance by simulating a swipe animation on an item at a time, and post a delay before deleting the next item, and so on to the way down to the last item of the RecyclerView
Steps:
Step No.1:
In your activity that holds the clear all button and the RecyclerView instance: Create a method of single item delete
private void deleteItem(View rowView, final int position) {
Animation anim = AnimationUtils.loadAnimation(requireContext(),
android.R.anim.slide_out_right);
anim.setDuration(300);
rowView.startAnimation(anim);
new Handler().postDelayed(new Runnable() {
public void run() {
if (myDataSource.size() == 0) {
addEmptyView(); // adding empty view instead of the RecyclerView
return;
}
myDataSource.remove(position); //Remove the current content from the array
myRVAdapter.notifyDataSetChanged(); //Refresh list
}
}, anim.getDuration());
}
Step No.2:
Create the method that will delete all RecyclerView list items >> call it in your button click callback.
boolean mStopHandler = false;
private void deleteAllItems() {
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
if (myDataSource.size() == 0) {
mStopHandler = true;
}
if (!mStopHandler) {
View v = myRecyclerView.findViewHolderForAdapterPosition(0).itemView;
deleteItem(v, 0);
} else {
handler.removeCallbacksAndMessages(null);
}
handler.postDelayed(this, 250);
}
};
requireActivity().runOnUiThread(runnable);
}
Also it's important to handle configuration change in manifest, activity section, as if the configuration changes while clearing your recycler view list, an exception will be raised
<activity
android:name=".activities.MainActivity"
android:configChanges="orientation|screenSize|keyboard"
android:label="#string/app_name"
...
</activity>
You shouldn't be using both notifyItemRemoved() and notifyItemRangeRemoved(). Only use one at a time.
If you want to remove one item:
notificationItems.remove(index);
adapterForNotification.notifyItemRemoved(index);
If you want to remove all items:
int origCount = notificationItems.size();
notificationItems.clear();
adapterForNotification.notifyItemRangeRemoved(0, origCount - 1);
If you want to remove a range of items:
notificationItems.subList(startIndex, endIndex).clear();
adapterForNotification.notifyItemRangeRemoved(startIndex, endIndex);
EDIT:
If you want to remove each item one by one and show the removal animation for each, try this:
for (int i = 0; i < notificationItems.size(); i++) {
notificationItems.remove(i);
adapterForNotification.notifyItemRemoved(i);
}
count = adapter.getItemCount();
for (int i = 0 ; i < count; ++i){
notificationItems.remove(0);
}
adapterForNotification.notifyItemRangeRemoved(0, count-1)
I did this by accessing the views of the list items directly from the adapater of the recyclerview, animating them, and notifying the adapter after the last animation has played.
fun deleteMultipleAnimated(){
val oldSize = myList.size
myList.removeAt(3)
myList.removeAt(4)
val newSize = myList.size
for(i in newSize until oldSize){
val listItemView = myRecycler.findViewHolderForAdapterPosition(i) as MyAdapater.MyViewHolder
if(i == oldSize-1){
//only the last animation notifies the adapter
listItemView.myView.animate().scaleX(0f).setDuration(250).withEndAction{
myAdapter.notifyItemRangeRemoved(newSize, oldSize)}
}else{
//every other view is animated without an end action
listItemView.myView.animate().scaleX(0f).duration = 250
}
}
}
Related
My RecyclerView is having some elements in it. Every second element should be painted in a different color. This is my function.
val BackgroundElemnt: LinearLayout = itemView.findViewById(R.id.container1)
fun bind(currencyPost: CurrencyPost){
if (BackgroundElemnt.get(position)%2 == 0){
BackgroundElemnt.setBackgroundColor(0xB8B8B8)
}
else
{
BackgroundElemnt.setBackgroundColor(0xFFFFFF)
}
CName.text = currencyPost.Name
CValue.text = currencyPost.Value
}
Solution 1:
you should try this
val backgroundElement: LinearLayout = itemView.findViewById(R.id.container1)
fun bind(currencyPost: CurrencyPost){
if (position%2 == 0){
backgroundElement.setBackgroundColor(0xB8B8B8)
}
else
{
backgroundElement.setBackgroundColor(0xFFFFFF)
}
CName.text = currencyPost.Name
CValue.text = currencyPost.Value
}
I replaced if (BackgroundElemnt.get(position)%2 == 0) with if (position%2 == 0) because you're checking for the position of the item in the adapter not the position of the root view.
Solution 2:
You should create a flag in your model that decides what the colour should be, then you should modify your list item set the colour for every second item.
Example:
data class CurrencyPost(val id: Int, val name: String, val hasDifferentColour: Boolean = false)
assuming that is the model of the item, you should then convert your list item to indicate what colour it should be. So before you call adapter.submitList(listOfCurrencyPost)
you should map the items to show the colour
so
val modifiedListOfCountryPost =
listOfCurrencyPost.mapIndexed { countryPost, index ->
if (index % 2 == 0) countryPost.copy(hasDifferentColour = true)
}
then adapter.submit(modifiedListOfCountryPost)
in the onBind method of your adapter you should then do this
fun bind(currencyPost: CurrencyPost){
if (countryPost.hasDifferentColur){
BackgroundElemnt.setBackgroundColor(0xB8B8B8)
}else
{
BackgroundElemnt.setBackgroundColor(0xFFFFFF)
}
CName.text = currencyPost.Name
CValue.text = currencyPost.Value
}
Integer timeOut = 1000;
public void AlterColor(){
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// put your condition statement here
finish();
}
},timeOut);
try this out...placae it in your class and call it within the main method.
I`m new here. Here is my question. I have an array list and adapter (RecyclerView). I want to make pause after inserting one item after another. If I add one item, animation goes fine. And even when I send multiple items to adapter it is not look terrible, but to the usability concerns, I want to add them one by one with pause. What I did. If I do this code in my fragment, no pause at all. Items appear in one moment.
for( int i = 0; i<filteredList.size();i++){
try {
mAdapter.add(filteredList.get(i));
Thread.sleep(350);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
If I add similar code to adapter, it stacks delay and then all items appear same way. May be I missed something?
It seems that handlder is the solution.
int dlay;
Handler handler = new Handler();
for( int i = 0; i<filteredList.size();i++){
final int finalI = i;
handler.postDelayed(new Runnable() {
#Override
public void run() {
mAdapter.add(filteredList.get(finalI));
}
}, dlay);
dlay = dlay + 350;
I've a ListView with some items that users can select.
I want that the first element appears selected, and after 1 or 2 seconds, the selected item will be the second automatically.
How can I do this?
When a item is selected, it can has a bold text for example.
I have a custom adapter for the ListView.
Update:
listview.setSelection(1);
System.out.println(listview.getSelectedItemPosition());
I've tested the code with this println, but it returns "-1". Not selects any row.
For a great tutorial on ListView see this tutorial: http://www.vogella.com/tutorials/AndroidListView/article.html
Back to the original question to select an item use the following:
ListView.setSelection(int);
Calling will simply change the UI without direct user input.
For the delay, you can use the following snippet:
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
ListView.setSelection(int);
}
}, 100);
For information on how change the Android ListView background color of selected item see this post.
If you are asking to iterate through the list and change it's selector you can use a handler like this:
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
#Override public void run() {
int currentSelectedIndex = mListView.getSelectedItemPosition();
if ((currentSelectedIndex + 1) < listCount) {
mListView.setSelection(currentSelectedIndex + 1);
} else {
mListView.setSelection(0);
}
handler.postDelayed(runnable, 1000);
}
};
And then in your code, in onCreate() for example call:
handler.postDelayed(runnable, 1000);
This will change the list selection every second as you would like.
If you do want to stop the automatic list selector, call this:
handler.removeCallbacks(runnable);
It's considered bad UX to automatically change the UI without direct user input, but if you want to select an item in a ListView programatically, you can call
ListView.setSelection(int);
Sets the currently selected item. If in touch mode, the item will not
be selected but it will still be positioned appropriately. If the
specified selection position is less than 0, then the item at position
0 will be selected.
For the delay, you will want to place this inside a Handler.
public class ListLooper {
private LoopHandler mHandler;
private ListView mListView;
public ListLooper(Activity activity) {
mListView = new ListView(activity);
mHandler = new LoopHandler(mListView);
}
private void start() {
mHandler.sendEmptyMessageDelayed(LoopHandler.MSG_LOOP, LoopHandler.DELAY);
}
private void stop() {
mHandler.removeMessages(LoopHandler.MSG_LOOP);
}
private static class LoopHandler extends Handler {
private static final int MSG_LOOP = 1;
private static final int DELAY = 2000;
/**
* Use a WeakReference so we don't keep an implicit reference to the Activity
*/
private WeakReference<ListView> mListRef;
private int mPosition;
private LoopHandler(ListView list) {
mListRef = new WeakReference<>(list);
}
#Override public void handleMessage(Message msg) {
super.handleMessage(msg);
// Check if we still have a reference to the ListView, and the Activity/Fragment hasn't been destroyed
if (mListRef.get() == null) {
return;
}
// If we're looping, run this code
if (msg.what == MSG_LOOP) {
int count = mListRef.get().getAdapter().getCount();
mListRef.get().setSelection(mPosition);
// If the position is less than the count, increment it, otherwise set it to 0
if (mPosition < count - 1) {
mPosition++;
} else {
mPosition = 0;
}
// Send the same message again, so we repeat this process
sendEmptyMessageDelayed(MSG_LOOP, DELAY);
}
}
}
Thanks so much!
This instruction:
listview.setSelection(int)
does not work in my code.
When I select some item, the background colour turns blue. That's correct.
But when the activity is just loaded, there is not item selected automatically.
I use performItemClick and the item is selected, highlighted and the getSelectedItem method will return the correct value:
ListView1.performItemClick(ListView1.getChildAt(1),1,ListView1.getItemIdAtPosition(1));
where 1 = position in the list
How can I programmatically scroll to a specific position in a ListView?
For example, I have a String[] {A,B,C,D....}, and I need to set the top visible item of the ListView to the index 21 of my String[].
For a direct scroll:
getListView().setSelection(21);
For a smooth scroll:
getListView().smoothScrollToPosition(21);
For a SmoothScroll with Scroll duration:
getListView().smoothScrollToPositionFromTop(position,offset,duration);
Parameters
position -> Position to scroll to
offset ---->Desired distance in pixels of position from the top of the view when scrolling is finished
duration-> Number of milliseconds to use for the scroll
Note: From API 11.
HandlerExploit's answer was what I was looking for, but My listview is quite lengthy and also with alphabet scroller. Then I found that the same function can take other parameters as well :)
Edit:(From AFDs suggestion)
To position the current selection:
int h1 = mListView.getHeight();
int h2 = listViewRow.getHeight();
mListView.smoothScrollToPositionFromTop(position, h1/2 - h2/2, duration);
Put your code in handler as follows,
public void timerDelayRunForScroll(long time) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
try {
lstView.smoothScrollToPosition(YOUR_POSITION);
} catch (Exception e) {}
}
}, time);
}
and then call this method like,
timerDelayRunForScroll(100);
CHEERS!!!
The Listview scroll will be positioned to top by default, but want to scroll if not visible then use this:
if (listView1.getFirstVisiblePosition() > position || listView1.getLastVisiblePosition() < position)
listView1.setSelection(position);
I have set OnGroupExpandListener and override onGroupExpand() as:
and use setSelectionFromTop() method which
Sets the selected item and positions the selection y pixels from the top edge of the ListView. (If in touch mode, the item will not be selected but it will still be positioned appropriately.) (android docs)
yourlist.setOnGroupExpandListener (new ExpandableListView.OnGroupExpandListener()
{
#Override
public void onGroupExpand(int groupPosition) {
expList.setSelectionFromTop(groupPosition, 0);
//your other code
}
});
If someone looking for a similar functionality like Gmail app,
The Listview scroll will be positioned to top by default. Thanks for the hint.
amalBit.
Just subtract it. That's it.
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
int h1 = mDrawerList.getHeight();
int h2 = header.getHeight();
mDrawerList.smoothScrollToPosition(h2-h1);
}
}, 1000);
If you want to jump directly to the desired position in a listView just use
listView.setSelection(int position);
and if you want to jump smoothly to the desired position in listView just use
listView.smoothScrollToPosition(int position);
Handling listView scrolling using UP/ Down using.button
If someone is interested in handling listView one row up/down using button. then.
public View.OnClickListener onChk = new View.OnClickListener() {
public void onClick(View v) {
int index = list.getFirstVisiblePosition();
getListView().smoothScrollToPosition(index+1); // For increment.
}
});
This is what worked for me. Combination of answers by amalBit & Melbourne Lopes
public void timerDelayRunForScroll(long time) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
try {
int h1 = mListView.getHeight();
int h2 = v.getHeight();
mListView.smoothScrollToPositionFromTop(YOUR_POSITION, h1/2 - h2/2, 500);
} catch (Exception e) {}
}
}, time);
}
and then call this method like:
timerDelayRunForScroll(400);
-If you just want the list to scroll up\dawn to a specific position:
myListView.smoothScrollToPosition(i);
-if you want to get the position of a specific item in myListView:
myListView.getItemAtPosition(i);
-also this myListView.getVerticalScrollbarPosition(i);can helps you.
Good Luck :)
You need two things to precisely define the scroll position of a listView:
To get the current listView Scroll position:
int firstVisiblePosition = listView.getFirstVisiblePosition();
int topEdge=listView.getChildAt(0).getTop(); //This gives how much the top view has been scrolled.
To set the listView Scroll position:
listView.setSelectionFromTop(firstVisiblePosition,0);
// Note the '-' sign for scrollTo..
listView.scrollTo(0,-topEdge);
it is easy
list-view.set selection(you pos);
or you can save your position with SharedPreference and when you start activity
it get preferences and setSeletion to that int
I found this solution to allow the scroll up and down using two different buttons.
As suggested by #Nepster I implement the scroll programmatically using the getFirstVisiblePosition() and getLastVisiblePosition() to get the current position.
final ListView lwresult = (ListView) findViewById(R.id.rds_rdi_mat_list);
.....
if (list.size() > 0) {
ImageButton bnt = (ImageButton) findViewById(R.id.down_action);
bnt.setVisibility(View.VISIBLE);
bnt.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(lwresult.getLastVisiblePosition()<lwresult.getAdapter().getCount()){
lwresult.smoothScrollToPosition(lwresult.getLastVisiblePosition()+5);
}else{
lwresult.smoothScrollToPosition(lwresult.getAdapter().getCount());
}
}
});
bnt = (ImageButton) findViewById(R.id.up_action);
bnt.setVisibility(View.VISIBLE);
bnt.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(lwresult.getFirstVisiblePosition()>0){
lwresult.smoothScrollToPosition(lwresult.getFirstVisiblePosition()-5);
}else{
lwresult.smoothScrollToPosition(0);
}
}
});
}
I'm use StaggeredGridView in my project,and I add data into the staggered grid view when scroll to bottom,but the grid view had go back top.I do not want it to go back top.
The following is the onChanged method in StaggeredGridView, how to modify it?
Please help me to resolve this problem,thanks very much!
private class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
#Override
public void onChanged() {
mDataChanged = true;
mItemCount = mAdapter.getCount();
// TODO: Consider matching these back up if we have stable IDs.
mRecycler.clearTransientViews();
if (mHasStableIds) {
// Clear all layout records and recycle the views
mLayoutRecords.clear();
recycleAllViews();
// Reset item bottoms to be equal to item tops
final int colCount = mColCount;
for (int i = 0; i < colCount; i++) {
mItemBottoms[i] = mItemTops[i];
}
}
// reset list if position does not exist or id for position has changed
if(mFirstPosition > mItemCount-1 || mAdapter.getItemId(mFirstPosition) != mFirstAdapterId){
mFirstPosition = 0;
Arrays.fill(mItemTops, 0);
Arrays.fill(mItemBottoms, 0);
if(mRestoreOffsets!=null)
Arrays.fill(mRestoreOffsets, 0);
}
// TODO: consider repopulating in a deferred runnable instead
// (so that successive changes may still be batched)
requestLayout();
}
#Override
public void onInvalidated() {
}
}
I have found a method to resolve this problem by remove the following code:
// reset list if position does not exist or id for position has changed
if(mFirstPosition > mItemCount-1 || mAdapter.getItemId(mFirstPosition) != mFirstAdapterId){
mFirstPosition = 0;
Arrays.fill(mItemTops, 0);
Arrays.fill(mItemBottoms, 0);
if(mRestoreOffsets!=null)
Arrays.fill(mRestoreOffsets, 0);
}