runOnUiThread and lags - android

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.

Related

UI is not updated during a listener callback in android

My fragment is
public class sample extends Fragment implements statusChanger{
void onResume() {
Listener.registerListener(this);
}
void onPause() {
Listener.deRegisterListener(this);
}
#Override
public void onStatusChanged() {
MyTextView.setVisibility(View.VISIBLE);
}
}
My Interface is
public interface StatusChanger {
void onStatusChanged();
}
My callback from another java class is
public void onstatusChanged() {
listener.onStatusChanged();
}
The above is the outline of my code , I could get a call back from the ordinal java class to my fragment , but the textView is not set to visible and i do not get any runtime errors.
Looks like it might be a thread issue. What if you run the onStatusChanged() code on the UI thread?
#Override
public void onStatusChanged() {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
MyTextView.setVisibility(View.VISIBLE);
}
});
}
In normal cases, following code will work:
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
// Update UI here.
}
});
Note: If you have wrap above code with Handler with a delay then it may not work.

RecyclerView animation when clearing whole adapter and adding new elements

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

What can I do about losing reference to a progress bar which is in a fragment?

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

How to refresh Android UI from an event listener?

I have a packet listener that detect when a packet arrived and I need refresh an ArrayAdapter when this happens. The problem is that if I try to access the adapter.notifyDataSetChanged() method, an Exception is thrown:
Exception in packet listener: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Any ideas?
Assuming you are in an object with access to the application's context:
If you are in an Activity:
runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
}
});
If you are in a Fragment:
Activity act = getActivity();
if (act != null) {
act.runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
}
});
}
Other options exist such as looper/message handler and so on but this is by far the simplest.

How to use notifyDataSetChanged() in thread

I create a thread to update my data and try to do notifyDataSetChanged at my ListView.
private class ReceiverThread extends Thread {
#Override
public void run() {
//up-to-date
mAdapter.notifyDataSetChanged();
}
The error occurs at line:
mAdapter.notifyDataSetChanged();
Error:
12-29 16:44:39.946: E/AndroidRuntime(9026): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
How should I modify it?
Use runOnUiThread() method to execute the UI action from a Non-UI thread.
private class ReceiverThread extends Thread {
#Override
public void run() {
Activity_name.this.runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
}
You can not touch the views of the UI from other thread. For your problem you can use either AsyncTask, runOnUiThread or handler.
All The Best
You cant access UI thread from other thread.You have to use handler to perform this.You can send message to handler inside your run method and update UI (call mAdapter.notifyDataSetChanged()) inside handler.
access the UI thread from other threads
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
my approach whe i use other Threads for work:
private AbsListView _boundedView;
private BasicAdapter _syncAdapter;
/** bind view to adapter */
public void bindViewToSearchAdapter(AbsListView view) {
_boundedView = view;
_boundedView.setAdapter(_syncAdapter);
}
/** update view on UI Thread */
public void updateBoundedView() {
if(_boundedView!=null) {
_boundedView.post(new Runnable() {
#Override
public void run() {
if (_syncAdapter != null) {
_syncAdapter.notifyDataSetChanged();
}
}
});
}
}
btw notifyDatasetChanged() method hooks to DataSetObservable class object of AbsListView which is set first by involving AbsListView.setAdaptert(Adapter) method by setting callback to Adapter.registerDataSetObserver(DataSetObserver);
You can write in this way also.
new Handler().postDelayed(new Runnable() {
public void run() {
test();
}
}, 100);
private void test() {
this.notifyDataSetChanged();
}
just test it.

Categories

Resources