I have a recycler view. On a button click I want to remove all the items from the recyclerview but the items must be removed with animation.
I am able to remove all the items at once but I don't know how to remove them with animation. Thanks
It's old, but wish this helps someone else as it's already not answered yet; I have done it by deleting a single item at a time by simulating a swipe animation on this item, and post a delay before deleting the next item, and so on to the way down to the last item of the RecyclerView
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>
This is a pretty good library and what's better is the documentation for it. You can even insert durations for transitions and animations.
Also, remember that if you are using default animation, after calling myDataSet.remove(pos) using adapter.notifyDataSetChanged() while there is an animation ongoing will cause the animation to stop.
Extend BaseItemAnimator class of recyclerview-animators library:
MyAdapter adapter = new MyAdapter(null);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new MyScaleInLeftAnimator());
findViewById(R.id.button).setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View view) {
int count = adapter.getItemCount();
adapter.clear();
adapter.notifyItemRangeRemoved(0, count);
}
}
);
...
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder{
private ArrayList<String> mItems;
...
public void clear() {
if (mItems != null) {
mItems.clear();
}
}
}
...
public class MyScaleInLeftAnimator extends BaseItemAnimator {
private long lastRemoval;
private int removeCount;
public MyScaleInLeftAnimator() {
lastRemoval = 0;
removeCount = 0;
}
public MyScaleInLeftAnimator(Interpolator interpolator) {
mInterpolator = interpolator;
lastRemoval = 0;
removeCount = 0;
}
#Override protected void preAnimateRemoveImpl(RecyclerView.ViewHolder holder) {
ViewCompat.setPivotX(holder.itemView, 0);
}
#Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
long time = System.currentTimeMillis();
long d = time - lastRemoval;
if (d < 100) {
removeCount++;
} else {
removeCount = 0;
}
lastRemoval = time;
ViewCompat.animate(holder.itemView)
.scaleX(0)
.scaleY(0)
.setDuration(getRemoveDuration())
.setInterpolator(mInterpolator)
.setListener(new DefaultRemoveVpaListener(holder))
.setStartDelay(removeCount * 100)
.start();
}
#Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
ViewCompat.setPivotX(holder.itemView, 0);
ViewCompat.setScaleX(holder.itemView, 0);
ViewCompat.setScaleY(holder.itemView, 0);
}
#Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) {
ViewCompat.animate(holder.itemView)
.scaleX(1)
.scaleY(1)
.setDuration(getAddDuration())
.setInterpolator(mInterpolator)
.setListener(new DefaultAddVpaListener(holder))
.setStartDelay(getAddDelay(holder))
.start();
}
}
This is how I have done without using any libraries - by inserting delays in the loop to remove items & restore (if needed)
clearItemsView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final List<LineItem> lineItemsCopy = new ArrayList<>(lineItems);
new Thread(new Runnable() {
#Override
public void run() {
for (int i=0; i<lineItemsCopy.size(); i++) {
runOnUiThread(new Runnable() {
#Override
public void run() {
salesOrderItemListAdapter.removeItem(0);
}
});
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
Snackbar snackbar = Snackbar.make(coordinatorLayout, getString(R.string.items_cleared_message), Snackbar.LENGTH_LONG)
.setAction(getString(R.string.label_undo), new View.OnClickListener() {
#Override
public void onClick(View v) {
new Thread(new Runnable() {
#Override
public void run() {
for (int i=0; i<lineItemsCopy.size(); i++) {
final int finalI = i;
runOnUiThread(new Runnable() {
#Override
public void run() {
salesOrderItemListAdapter.restoreItem(lineItemsCopy.get(finalI), 0);
}
});
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}).setActionTextColor(Color.YELLOW);
snackbar.show();
}
});
Related
In a chatting window I have a feature like self destructing message after a duration. For which I have a class which shows burning animation after the duration. While sending text the handler and timer I used in the below class is working fine for 2-3 times then some animation discrepancy is found like the fire isn't starting in exact location.
Apart from that while sending a file from file chooser fragment when it returns to the recyclerview of the chatwindow nothing happens until I touch the screen and scroll it a bit. While debugging I noticed that the first time when bindview is called after sending a file the handler and timer is not executing properly. Instead if I navigate back and enter the chatwindow again it's working perfectly. It burns the filemessage perfectly then.
The burner class:
public class Burner {
private static final TaggedLogger logger = new TaggedLogger("Burner");
public static void burnMessage(final TextMessageViewHolder holder, final BurnerListener listener) {
final int DURATION = 3000;
if (!holder.isBurning) {
final Handler handler = new Handler(Looper.getMainLooper());
final Animator.AnimatorListener animatorListener = new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {
logger.log("onAnimationStart");
}
#Override
public void onAnimationEnd(Animator animator) {
listener.onBurnFinished();
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
};
holder.isBurning = true;
holder.fireAnimationHolder.setVisibility(View.VISIBLE);
holder.fireAnimationHolder.measure(0, 0);
Activity activity = (Activity) ChatProperties.getChatWindowContext();
if (activity != null) {
final ParticleSystem particleSystem = new ParticleSystem(activity, 48, R.drawable.fire_2, DURATION)
.setSpeedByComponentsRange(-0.025f, 0.0f, -0.06f, -0.08f)
.setAcceleration(0.00003f, 0)
.setInitialRotationRange(30, 45)
.addModifier(new AlphaModifier(255, 0, 200, 1000))
.addModifier(new ScaleModifier(0.5f, 2f, 0, 1000));
holder.fireAnimationHolder.post(new Runnable() {
#Override
public void run() {
holder.fireAnimationHolder.measure(0,0);
int fireAnimationHolderWidth = holder.fireAnimationHolder.getWidth();
// logger.log("fireAnimationHolderWidth = " + fireAnimationHolderWidth);
particleSystem.emit(holder.fireSource, 12);
holder.fireSource.animate().translationXBy(fireAnimationHolderWidth).setStartDelay(500).setDuration(DURATION).setListener(animatorListener).start();
holder.fireSourceController.setPivotX(0);
holder.fireSourceController.animate().alpha(1f).scaleX(1f).setDuration(DURATION).start();
}
});
new Timer().scheduleAtFixedRate(new TimerTask() {
int elapsedTime = 0;
#Override
public void run() {
elapsedTime += 300;
if (elapsedTime >= DURATION) {
handler.post(new Runnable() {
#Override
public void run() {
particleSystem.cancel();
}
});
this.cancel();
} else {
handler.post(new Runnable() {
#Override
public void run() {
particleSystem.updateEmitPoint(holder.fireSource, Gravity.CENTER);
}
});
}
}
}, 100, 300);
}
}
}
This burnMessage method is called from the bindview of the TextMessageViewholder's bindview method and FilemessageViewHolder extends TextMessageViewHoler class.
In TextMessageViewHolder's bindview method this function is called if it's a self destructing message:
private void handleMessageBurning(final Message message) {
if (message.imBurnTime > 0 && BurnMessageHelper.animatedBurnMessageEntryMap.containsKey(message.callerId)) {
Log.e("BurnThread","message to be burned");
Burner.burnMessage(this, new BurnerListener() {
#Override
public void onBurnFinished() {
if (ChatProperties.isGroupChat) {
DataHelper.getInstance().deleteGroupMessage(Target.target, message.callerId);
} else {
DataHelper.getInstance().deleteMessage(Target.target, message.callerId);
}
BurnMessageHelper.animatedBurnMessageEntryMap.remove(message.callerId);
isBurning = false;
}
});
} else {
fireAnimationHolder.setVisibility(View.GONE);
}
}
What is happening here? Does the thread of the timer can't run in main thread here due to some reason? Please help. Thanks.
N.B. I tried this in Marshmallow, Nougat and Oreo.
I am now implementing the view pager and i want to continuously change the image one by one automatically for a particular periods of time may be once in 5 ms .
I also have to manually allow the user to swipe the images (it is working properly)
But the image is not automatically changing and when it reaches the last image it should come to the first image automatically
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
if (currentPage == strImages.length-1) {
currentPage = 0;
}
intro_images.setCurrentItem(currentPage++, true);
}
};
timer = new Timer(); // This will create a new Thread
timer .schedule(new TimerTask() { // task to be scheduled
#Override
public void run() {
handler.post(Update);
}
}, 500, 3000);
I used above code but it does not work properly
try this add addOnPageChangeListener to your viewPager like this
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == strImages.length-1) {
currentPage = 0;
}
intro_images.setCurrentItem(currentPage++, true);
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
or for auto change page try this create a time task like this
Timer timer;
timer = new Timer();
timer.scheduleAtFixedRate(new RemindTask(), 0, 3000); // delay*/
private class RemindTask extends TimerTask {
int current = viewPager.getCurrentItem();
#Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
if (current < strImages.size()) {
viewPager.setCurrentItem(current);
current += 1;
} else {
current = 0;
viewPager.setCurrentItem(current);
}
}
});
}
}
The below code is to get the last position and to scroll to the 1st position:
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
{
#Override
public void onPageScrolled(int position, float positionOffset,int positionOffsetPixels) {
if (position == Yourlist.size()-1) {
pager.setCurrentItem(0);
}
pager.setCurrentItem(position++,true);
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
The below code is For automatic change picture in every 10s using CountDown Timer:
- First you have to declare one array,which will contain your
images,then after you have to use below code.
int i=0;
new CountDownTimer(1000,1000)
{
#Override
public void onTick(long millisUntilFinished) {}
#Override
public void onFinish() {
iV.setImageDrawable(sdk.getContext().getResources().getDrawable(array[i]));
i++;
if(i== array.length-1) {i=0;}
start();
}
}.start();
You don't need the combination of Handler and TimerTask for this, You can use simple Handler like this using Handler.postDelayed
private Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
int totalCount = intro_images.getAdapter().getCount();
int currentItem = intro_images.getCurrentItem();
if (currentItem == (totalCount - 1)) {
currentItem = 0;
} else {
currentItem = currentItem + 1;
}
intro_images.setCurrentItem(currentItem, true);
handler.postDelayed(this, 3000);
}
};
#Override
protected void onResume() {
super.onResume();
handler.postDelayed(Update, 500);
}
#Override
protected void onPause() {
super.onPause();
if(handler != null) {
handler.removeCallbacks(Update);
}
}
just add listener to your viewpager to get the last position and to scroll to the 1st position.
vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if(position==yourlist.length){
vp.setCurrentItem(0);
}
}
});
AnimationDrawable animation = new AnimationDrawable();
I have implemented one counter and onFinish() of first counter,I started second counter but the first counter not able to finish.Text "Bye Guyz" remain for some time so how to finish the text.
Please help me.
Thanks in advance.!!!
Code :-
counter= new CountDownTimer(10000, 1000) {
public void onTick(long millisUntilFinished) {
if (count == 0) {
tv.setText("First counter");
tv2.setVisibility(View.VISIBLE);
tv2.setText("Hello Guyz");
}
}
public void onFinish() {
if(!flag) {
tv2.setText("Bye Guyz");
count = 0;
try {
counter.cancel();
}catch (Exception e){}
}
else if(flag) {
counter1 = new CountDownTimer(9000, 1000) {
public void onTick(long millisUntilFinished) {
flag = false;
tv.setText("Second counter");
tv2.setVisibility(View.VISIBLE);
tv2.setText("Hello Girls");
count = 0;
}
public void onFinish() {
tv2.setVisibility(View.VISIBLE);
tv2.setText("Bye Girls");
count = 0;
}
}.start();
Did you "debug" the code to be sure the code is arriving to counter1 = new CountDownTimer(9000, 1000)?
Are you sure when the first counter arrives to onFinish() the flag variable is true?
Why do you call counter.cancel() in onFinish() when obviously the counter is already over?
public void onFinish() {
if(!flag) {
tv2.setText("Bye Guyz");
count = 0;
try {
counter.cancel();
}catch (Exception e){}
}
If you say your tv2 displays "Bye Guyz" it means that your flag is set to false, so the "else if" part is not being executed. onFinish() is only executed once, so you need to make sure the flag is set for true to start the second counter.
Also you shouldn't cancel your counter in onFinish() because it's already finished.
Here is my alternative is as follows
Create the custom Counterextending Thread
class Counter extends Thread {
private long timeOne, timeTwo;
private OnCounterFinishedListener mCounterFinishedListener;
private Thread t;
Activity activity = null;
Counter(Context context){
t = new Thread(this);
activity = (Activity)context;
}
#Override
public void run() {
try {
sleep(timeOne);
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
mCounterFinishedListener.firstCounterFinished();
}
});
sleep(timeTwo);
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
mCounterFinishedListener.secondCounterFinished();
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void setTimes(long timeOne, long timeTwo){
this.timeOne = timeOne;
this.timeTwo = timeTwo;
}
public void start(OnCounterFinishedListener listener){
mCounterFinishedListener = listener;
t.start();
}
interface OnCounterFinishedListener{
void firstCounterFinished();
void secondCounterFinished();
}
}
Then inside your main thread you can start this counter as
final Counter counter = new Counter(this);
counter.setTimes(5000, 5000);
counter.start(new Counter.OnCounterFinishedListener() {
#Override
public void firstCounterFinished() {
// Update your first TextView
}
#Override
public void secondCounterFinished() {
// Update your second TextView
}
});
I want to remove items from a gridView one by one. the problem is that are all removed once !
This is my code
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
boolean sw = false;
while(!sw)
{
int i=0;
if(!adapter.isEmpty())
{
adapter.removeItem(i);
i+=1;
} else sw = true;
}
}
}, 700);
in adapter I've created a remove function.. This it is
public void removeItem(final int position)
{
data.remove(position);
notifyDataSetChanged();
}
Advices ?
The problem is you are running all the remove in one go, try with this
public void removeAllItem()
{
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run()
{
if(!adapter.isEmpty())
{
adapter.removeLastItem();
removeAllItem();
}
}
}, 700);
}
and removeLastItem() is
public void removeLastItem()
{
int lastIndex = data.size() - 1;
data.remove(lastIndex);
notifyDataSetChanged();
}
Which will remove every item from the last index every 700 miliseconds.
Declare object in class level
boolean sw = false;
Change code like below
while (!sw) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
int i = 0;
if (!adapter.isEmpty()) {
adapter.removeItem(i);
i += 1;
} else sw = true;
}
}, 700);
}
Hope it will help you !!
I hit my head against the wall, trying to solve this problem for some time. I'm not an experienced Android guy, so its solution might be really simple. That being said, I have the following code:
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.adminusers);
super.onCreate(savedInstanceState);
// set user_type for Viewall
mUserType = 0;
lv_adminusersListContent.addFooterView(footerView);
mUsersAdapter = new AdminUsersAdapter(mUsersList, AdminUsers.this);
lv_adminusersListContent.setAdapter(mUsersAdapter);
getUsersList();
}
private void getUsersList() {
if (UsefulFcts.isOnline(AdminUsers.this)) {
if (mThread != null && mThread.isAlive()) {
mThread.interrupt();
}
mThread = new Thread() {
#Override
public void run() {
mHandler.post(new Runnable() {
#Override
public void run() {
pDialog = new ProgressDialog(AdminUsers.this);
pDialog.setMessage("Loading.. Please wait!");
pDialog.setCancelable(false);
pDialog.show();
}
});
mUsersList.clear();
mUsersList.addAll(mServiceManager.getUsersList(mUserType,
0 + "", AdminServiceManager.sHowManyToLoad));
mArrayLength = mUsersList.size();
Log.d("arrayLength", String.valueOf(mArrayLength));
Log.d("total_results",
String.valueOf(ParserJSON.total_results));
mHandler.post(new Runnable() {
#Override
public void run() {
// refresh listview
mUsersAdapter.notifyDataSetChanged();
if (mArrayLength < ParserJSON.total_results) {
lv_adminusersListContent
.addFooterView(footerView);
} else if (mArrayLength >= ParserJSON.total_results) {
lv_adminusersListContent
.removeFooterView(footerView);
}
if (0 == mArrayLength) {
Toast.makeText(AdminUsers.this,
"No username found!",
Toast.LENGTH_SHORT).show();
}
pDialog.dismiss();
}
});
}
};
mThread.start();
} else {
Toast.makeText(AdminUsers.this, "Internet connection required!",
Toast.LENGTH_LONG).show();
}
}
#Override
protected void linkUI() {
// begin load_more view
footerView = AdminUsers.this.getLayoutInflater().inflate(
R.layout.loadmore, null);
btn_loadmore = (Button) footerView.findViewById(R.id.btn_loadmore);
// end load_more view
btn_adminusersViewall = (Button) findViewById(R.id.btn_adminusersViewall);
btn_adminusersAdmins = (Button) findViewById(R.id.btn_adminusersAdmins);
btn_adminusersClients = (Button) findViewById(R.id.btn_adminusersClients);
btn_adminusersDevs = (Button) findViewById(R.id.btn_adminusersDevs);
btn_adminAdd = (ImageButton) findViewById(R.id.btn_adminAdd);
lv_adminusersListContent = (ListView) findViewById(R.id.lv_adminusersListContent);
}
#Override
protected void setAction() {
// begin Buttons UnderHeader
btn_adminusersViewall.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
UsefulFcts.setButtonClicked(btn_adminusersViewall,
btn_adminusersClients, btn_adminusersAdmins,
btn_adminusersDevs);
mUserType = 0;
mArrayLength = -1;
lv_adminusersListContent.removeFooterView(footerView);
getUsersList();
}
});
btn_adminusersClients.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
UsefulFcts.setButtonClicked(btn_adminusersClients,
btn_adminusersDevs, btn_adminusersAdmins,
btn_adminusersViewall);
mUserType = 1;
mArrayLength = -1;
// view.setVisibility(View.GONE);
getUsersList();
}
});
btn_adminusersDevs.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
UsefulFcts.setButtonClicked(btn_adminusersDevs,
btn_adminusersClients, btn_adminusersAdmins,
btn_adminusersViewall);
mUserType = 2;
mArrayLength = -1;
lv_adminusersListContent.removeFooterView(footerView);
getUsersList();
}
});
btn_adminusersAdmins.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mUserType = 3;
mArrayLength = -1;
lv_adminusersListContent.removeFooterView(footerView);
getUsersList();
}
});
// end Buttons UnderHeader
btn_loadmore.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mThread2 = new Thread() {
#Override
public void run() {
mHandler.post(new Runnable() {
#Override
public void run() {
progressBar = new ProgressDialog(
AdminUsers.this);
progressBar
.setMessage("Loading more projects.. ");
progressBar.setCancelable(true);
progressBar.show();
}
});
mUsersList.addAll(mServiceManager.getUsersList(
mUserType, String.valueOf(mArrayLength),
AdminServiceManager.sHowManyToLoad));
mArrayLength = mUsersList.size();
mHandler.post(new Runnable() {
#Override
public void run() {
mUsersAdapter.notifyDataSetChanged();
if (mArrayLength >= ParserJSON.total_results) {
lv_adminusersListContent.removeFooterView(footerView);
}
progressBar.dismiss();
}
});
};
};
mThread2.start();
}
});
Note: I basically have a listview which content is requested from a server depending on which button (looking like a tab) I'm pressing (mUserType = 0/1/2/3). I also use a Load More button (footerView = simple layout with a button).
Everything seems to work just fine, but after "playing" with the buttons a few times (random number!) it crashes, with the following exception thrown:
02-21 11:00:49.252: E/AndroidRuntime(11728): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131296461, class android.widget.ListView) with Adapter(class android.widget.HeaderViewListAdapter)]
So, I looked back at the code, but couldn't see any "not on UI thread" listview notification.
1. What could the problem be?
2. Is it necessary to use 2 threads (mThread and mThread2), or could I use the same one, as one's work should start when the other ends (or isn't it ?) and should I declare them static or not ?
Also pointing me to any "Good practices" tricks I could use in the future, or "Wrong practices" that I did would be much appreciated!
PS. linkUI() and setAction() are called in super().onCreate().
The error message seems understandable : you are modifying the adapter from mThread:
mUsersList.clear();
mUsersList.addAll(mServiceManager.getUsersList(mUserType,
0 + "", AdminServiceManager.sHowManyToLoad));
You can try something like this:
final List newUserList = mServiceManager.getUsersList(mUserType,
0 + "", AdminServiceManager.sHowManyToLoad);
mArrayLength = newUserList.size();
Log.d("arrayLength", String.valueOf(mArrayLength));
Log.d("total_results",
String.valueOf(ParserJSON.total_results));
mHandler.post(new Runnable() {
#Override
public void run() {
mUsersList.clear();
mUsersList.addAll(newUserList);
// refresh listview
mUsersAdapter.notifyDataSetChanged();