So imagine that at first we load 10 items in our RecyclerView. Adding or removing one element gives us a nice animation (the adapter has stable ids).
The problem is, I have a search bar, I can look for something and then the result should replace the current items of the RecyclerView. If some item was already there, there is a nice "moving" animation. But if all items are new, there is a quite ugly fade-in transition that it's too fast and looks like a glitch. Is it possible to override that animation? I'd like to have a fade-out-fade-in one but slower.
By the way, when the query returns with results, I do this in the adapter:
mItems.clear();
mItems.addAll(resultItems);
notifyDataSetChanged();
Also, it's worth to say that if I make a search with no results, then I see the RecyclerView empty and then if I get some results again, the transition from empty state to some results looks ok.
You can batch remove and insert items in a RecyclerView.
adapter.notifyItemRangeRemoved(0, mItems.size());
mItems.clear();
mItems.addAll(resultItems);
adapter.notifyItemRangeInserted(0, mItems.size());
EDIT: After looking at your problem some more you probably don't want to do what I suggested above. Instead you should not clear your list and instead remove some items and then notify the adapter of the change with notifyItemRemove(index)
If you do range methods like RangeRemoved/RangeAdded, you loose out in the animation side. So, do it one by one in a loop to preserve the animation effect of one by one, including a delay in the loop. Here's how I have implemented:
MainActivity.java
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();
}
});
RecyclerViewAdapter.java
//Only remove & restore functions are shown
public void removeItem(int position) {
lineItems.remove(position);
notifyItemRemoved(position);
}
public void restoreItem(LineItem item, int position) {
lineItems.add(position, item);
notifyItemInserted(position);
}
Related
I have three fragments, Play, Dropbox and Settings inside a tab host.
Inside the Dropbox fragment, I have a class variable, 'progressBar'. I allocate it in the onActivityCreated.
if(progressBar == null)
{
progressBar = (ProgressBar) getActivity().findViewById(R.id.progressbar_Horizontal);
}
I update it with this:
DropboxAPI.DropboxFileInfo info = mDBApi.getFile(directoryOfFile, null, outputStream, new ProgressListener() {
#Override
public long progressInterval() { return 2000; }
public void onProgress(long downloadedSoFar, long totalSize) {
final double dProgress = ((double)downloadedSoFar / totalSize)*100.0;
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
System.out.println(dProgress);
progressBar.setProgress((int) dProgress);
}
});
}
This works perfectly, however, when I move to another fragment and come back it stays stuck on where it was left. It's annoying because I can still see my System.out.println(dProgress) working. Nothing I seem to do allows me to reallocate the progress bar and it work. Is there any way I can do this? Anyone just point me in the right direction as to how this achieved? I had a look at a few apps as to how they do it ( I'm an iOS guy ) and they all just prompted the user with a progress screen open, but I want users to be able to do something while downloading.
p.s I've tried this without the null condition.
Replace this ..
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
System.out.println(dProgress);
progressBar.setProgress((int) dProgress);
}
});
with this..
Thread thread = new Thread() {
#Override
public void run() {
System.out.println(dProgress);
progressBar.setProgress((int) dProgress);
}
};
thread.start();
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. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131296513, class xyz.ScrollDetectableListView) with Adapter(class android.widget.HeaderViewListAdapter)]
I am getting above exception sometimes while scrolling through the dynamic listview and then clicking on item.I researched a lot but unable to find the exact reason that why i am getting this error sometimes and how it can be resolved?
private ScrollDetectableListView mFListView;
public FAdapter mFAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_feed_view, container, false);
View headerView = getActivity().getLayoutInflater().inflate(R.layout.view_feed_header, null);
try{
mFListView = (ScrollDetectableListView) rootView.findViewById(R.id.feed_list_view);
mFContainer = (SwipeRefreshLayout) rootView.findViewById(R.id.feed_container);
mFListView.addHeaderView(headerView);
mFListView.setEmptyView(rootView.findViewById(R.id.empty_view));
mFContainer.setColorSchemeResources(R.color.green, R.color.pink, R.color.fbcolor,
R.color.instagramcolor, R.color.googlecolor, R.color.flickrcolor);
mFView = getActivity().getLayoutInflater().inflate(R.layout.view_footer, null);
ImageView rotateImageView = (ImageView) mFooterView.findViewById(R.id.spinner);
Animation rotation = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate);
rotation.setFillAfter(false);
rotateImageView.startAnimation(rotation);
mFContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh()
{
initializeFListView();
}
});
initializeFListView();
mProgressDialog.setVisibility(View.VISIBLE);
mHActivity.setDataChangedListener(new DataChangedListener() {
#Override
public void onDataChanged() {
mFContainer.setRefreshing(true);
mProgressDialog.setVisibility(View.GONE);
initializeFListView();
}
});
}catch(Exception e){}
return rootView;
}
public void initializeFListView()
{
FApi.getTrending(getActivity(), xyz, new APIResponseListener() {
#Override
public void onResponse(Object response) {
setFeedAdapter((List<Video>) response);
}
#Override
public void onError(VolleyError error) {
if (error instanceof NoConnectionError) {
String errormsg = getResources().getString(R.string.no_internet_error_msg);
Toast.makeText(getActivity(), errormsg, Toast.LENGTH_LONG).show();
}
}
});
}
private void setFAdapter(List<Video> response)
{try {
List<Video> videos = response;
mFAdapter = new FAdapter(getActivity(), videos, mProfileClickListener, mCommentClickListener);
mFListView.setOnScrollListener(new EndlessScrollListenerFeedView(getActivity(), mFListView, mFView, mFAdapter, videos, mFType, ""));
mFListView.setAdapter(mFAdapter);
mProgressDialog.setVisibility(View.GONE);
if (mFContainer.isRefreshing()) {
mFContainer.setRefreshing(false);
}
if (mFAdapter.getCount() < mCount) {
mFView.setVisibility(View.GONE);
mFListView.removeFooterView(mFooterView);
}
}catch(Exception e){}
}
}
My suggestion try to set ur list adapter on UI Thread,,,
private void setFAdapter(List<Video> response)
{
try {
List<Video> videos = response;
mFAdapter = new FAdapter(getActivity(), videos, mProfileClickListener, mCommentClickListener);
mFListView.setOnScrollListener(new EndlessScrollListenerFeedView(getActivity(), mFListView, mFView, mFAdapter, videos, mFType, ""));
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
mFListView.setAdapter(mFAdapter);
}
});
mProgressDialog.setVisibility(View.GONE);
if (mFContainer.isRefreshing()) {
mFContainer.setRefreshing(false);
}
if (mFAdapter.getCount() < mCount) {
mFView.setVisibility(View.GONE);
mFListView.removeFooterView(mFooterView);
}
}catch(Exception e){}
}
}
Keep one singleton class object in hand. So that you can synchronize two thread on it. Care to be taken to not to block the ui thread.
Reduce number of interfaces to only one method to start preparing data for your list and only one method to call your notifydatasetchanged/setAdapter on list.
Means there should be only one method like prepareData() which will be executed by a background thread. synchronise this method on your singleton object.
MyListAdaper adapter = null;
// Call this from a background thread
public void prepareData() {
synchronized (SingleTonProvider.getInstance()) {
List<AnyDataTypeYouWant> data = null;
// populate data here by your application logic.
adapter = new MyListAdaper(data);
}
}
And have only one method to refresh list.
// Also Call this from a background thread only
public void refreshList() {
synchronized (SingleTonProvider.getInstance()) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mFListView.setAdapter(adapter);
}
});
}
}
have no other code on any place to prepare data and set data on list.
Call the methods I mentioned from a background thread only.
I just gave general solution to your problem. You have to work on your specific case by yourself.
If I click the button it works normally but when pressing more than 1 button the image view hangs for seconds before changing
My Code:
My Code:
ArrayList<Integer> ids = null;
ids=new ArrayList<Integer>();
ids.add(R.drawable.comp2);
ids.add(R.drawable.comp3);
ids.add(R.drawable.comp4);
ids.add(R.drawable.comp5);
ids.add(R.drawable.comp6);
ids.add(R.drawable.comp7);
ids.add(R.drawable.comp8);
ids.add(R.drawable.comp9);
ids.add(R.drawable.comp10);
button a = (Button)findViewById(R.id.button);
public void onclick{
name()
}
int i =o;
public void name() {
new Thread(new Runnable() {
public void run() {
imageview.post(new Runnable() {
public void run() {
if(i<ids.size()) {
imageview.setImageResource(ids.get(i));
i++;
}else i=0;
}
});
}
}).start();
They way you are doing is of no good. You are creating one thread and inside that thread you are changing ImageView src. I would suggest you to use transition.
You can define it in xml as well as in dynamically using code.
I have a fragment that incorporates a ViewPager with custom views and a spinner in a top bar. Every time a change the page in the viewpager, I have to update strings in the spinner from a db. To do this, in the onPageSelected of the viewPager listener I launch a thread that retrieve data from the db and updates via runOnUiThread the spinner's adapter. The problem is that the notifyDataSetChanged() called for the spinner's adapter lags the viewpager: it has to rebuild few views, but it lags it a bit.
#Override
public void onPageSelected(final int pos) {
new Thread(new Runnable() {
#Override
public void run() {
...
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
mSpinnerAdapter.notifyDataSetChanged();
}
});
}
}).start();
}
How to resolve this? It is possible to run code on the main thread with lowest priority?
First, remove the Thread, then try to remove the runOnUiThread.
you should be able to get away with :
#Override
public void onPageSelected(final int pos) {
... // your hidden code
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
mSpinnerAdapter.notifyDataSetChanged();
}
});
}
The new thread stuff isnt needed.
I have a custom view group which allowed my screen to swip to the right when I call the menu button letting appear a second layout (my menu).
I have similars activities in my project without the menu and it works fine.
it's a simple view with a listview in it which displays data from webservices.
when I come on this activity, the content of my listView is not refreshed when data arrives. but when i press the menu button, the view swip and the content of my list view appears (without images, that appears when i scroll my listView).
here is the event rased when the menu button is pressed :
public void customScroll(final int i) {
new Thread(new Runnable() {
#Override
public void run() {
if (i == 0) {
for (int inc = 200; inc >= 0; inc -= 5) {
final int inc2 = inc;
runOnUiThread(new Runnable() {
#Override
public void run() {
//cvg is the implementation of my customViewGroup
cvg.scrollTo(inc2);
}
});
try {
synchronized (this) {
wait(5);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
for (int inc = 0; inc <= 200; inc += 5) {
final int inc2 = inc;
runOnUiThread(new Runnable() {
#Override
public void run() {
cvg.scrollTo(inc2);
}
});
try {
synchronized (this) {
wait(5);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
And that is the thread wich is run in "runOnUiThread" when my data are downloaded :
private Runnable returnRes = new Runnable() {
#Override
public void run() {
if (doubleVideos != null && doubleVideos.size() > 0) {
videoAdapter.notifyDataSetChanged();
for (int i = 0; i < doubleVideos.size(); i++) {
videoAdapter.add(doubleVideos.get(i));
}
}
videoAdapter.notifyDataSetChanged();
}
the behavior of my activity is quite strange, ask me if you don't understand.
i've tried already tried :
cvg.invalidate();
cvg.scrollTo(20);
//listView.notify();
listView.refreshDrawableState();
cvg.refreshDrawableState();
listView.bringToFront();
listView.invalidate();
cvg.invalidate();
listView.refreshDrawableState();
billion thanks for those who red this.
I have the same problem before.., I think you didn't implement ViewGroup in the proper way! In the custom ViewGroup call the measure method for each child before calling the layout method on the onLayout.