I have this piece of code inside my MainActivity , what I'm trying to do is to tint a background for an element, it works nice, but the handler is not supposed to do the job of waiting when the views are all inflated, so, I want to move this code inside my onBindViewHolder but Im kinda lost, any hint ?
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
for (int i = 0; i < mArrayListSelectedTAGS.size(); i++) {
View view = mRecyclerView.getLayoutManager().findViewByPosition(mArrayListaTAGs.indexOf(mArrayListSelectedTAGS.get(i)));
view.setBackgroundColor(getApplicationContext().getResources().getColor(R.color.colorVincular));
}
}
}, 500);
Te question is, how do I get the exact view on my onBindView holder in order to tint it, and how to call it from MainActivity
thanks
For those having the same issue , I just solved this with a simple Interface, in my onBindViewHolder, after I bind all my views, I call an interface named onBindFinished(boolean isFinished);
after that in my adapter I just set that value to true if the onBind have finished
and in my class the only thing I do is checking if onBindFinished is true I just paint the views.
Related
I tried to get the width of a LinearLayout.
Here is the code of the MainActivity.java:
public class MainActivity extends AppCompatActivity {
BoardClass board;
private int widthareagame;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final LinearLayout gamearea;
ImageView im1 ;
Button abutton;
abutton = (Button) findViewById(R.id.buttonnew);
gamearea = ( LinearLayout) findViewById(R.id.boardarea);
gamearea.post(new Runnable(){
public void run(){
widthareagame = gamearea.getWidth();
}
});
board = new BoardClass(this,widthareagame);
gamearea.addView(board);
}
The value of widthareagame at new BoardClass(this,widthareagame); is still Zero.
Thanks
Here is what documentation says about View#post():
Causes the Runnable to be added to the message queue. The runnable
will be run on the user interface thread.
Your task, of modifying the value of widthareagame variable, has been pushed to the message queue of the view. It doesn't guarantee that it will get executed at the very same instance. The control then proceeds to the next line, where you still get the unmodified value.
You can try something like this, to ensure that you are able to use the modified value:
gamearea.post(new Runnable(){
public void run(){
widthareagame = gamearea.getWidth();
board = new BoardClass(this,widthareagame);
gamearea.addView(board);
}
});
This is because post method call queued the setting of widthareagame where as your view is rendering.You didn't guarantee the order of execution.
You have to make sure the statements inside the run method execute first and then new Board(.. is invoked.For that you can do something like this
final AtomicBoolean done = new AtomicBoolean(false);
run(){
//inside run method
done.set(true);
notify();
}
then do something like this
synchronized(task) {
while(!done.get()) {
task.wait();
}
new Board(..
}
where task is your runnable task defined something like this
final Runnable task = new Runnable() {
#Override
public void run() {
The reason is zero is because within the onCreate the LinearLayout has not been measured yet.
And the reason it only works when within the Runnable is because since this one has been posted then it will run on the next execution cycle, which is after the onCreate and the rest of the Activity lifecycle methods (onStart, onResume, etc.) and even onAttachedToWindow have been called, at which point will be already measured and give the correct size.
Said all that, a safer way to get your layout metrics with certainty would be to listen when the layout state changes.
gamearea.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// Remove the listener here unless you want to get this callback for
// "every" layout pass, which can get you into an infinite loop if you
// modify the layout from within this method
gamearea.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// A this point you can get the width and height
widthareagame = gamearea.getWidth();
}
});
I am trying to refresh the content in text view upon call back. In my onCreateView() when I first load the fragment:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_expense,container,false);
total = 0;
// some logic to get the total
txtTotal.setText(String.valueOf(total));
return v;
}
From first fragment I set the call back when button on click go to second fragment:
// set call back
addFragment.setTransactionListCallBack(ExpenseActivity.this);
Then when finish updating and retrieve in second fragment using async task, I pass back the updated results:
if(mcallback != null){
mcallback.callback(GetMonthlyTransactionAsyncTask.allTransaction);
}
Then I perform some insert into database, but before I return I am pulling the data again so I will pass back the updated one. In my callback in first fragment:
#Override
public void callback(ArrayList<Transaction> list)
{
// logic to sum up the total
Log.d("UPDATED TOTAL", String.valueOf(total));
txtTotal.setText(String.valueOf(total));
}
The call back is working perfectly as I did printed out the log to check the before update (inside onCreateView) and after update (inside callback), the value printed out is correct. So I guess there is not a need to show the code.
The problem now is inside my callback in first fragment, the updated value I printed out in Log is correct which is the updated one, but not the content displayed in the text view. The text view is still stuck with the old value.
Any ideas how to refresh the textview content upon call back?
invalidate() must be called from a UI thread. To call from a non-UI thread, call postInvalidate()
#Override
public void callback(ArrayList<Transaction> list)
{
// logic to sum up the total
Log.d("UPDATED TOTAL", String.valueOf(total));
txtTotal.setText(String.valueOf(total));
txtTotal.postInvalidate();
}
Also, check if there is a textview with the same id.
You may be trying to set the textview on a different thread if asynctask is involved, try setting the text view on the UI thread:
runOnUiThread(new Runnable() {
#Override
public void run() {
txtTotal.setText(String.valueOf(total));
}
});
Edit: also try txtTotal.invalidate() after you set text.
I have a LinearLayout, and this LinearLayout will hold dynamically placed views. I need to find out what the width of the children of LinearLayout, however this has to be done in onCreate method. From researching I've found out that you can't use getWidth from this method. So instead I'm using onWindowFocusChanged, which works for the parent LinearLayout (returning me the actual size), but it doesn't work with its children.
Another thing I noticed is that when the screen is fading away and the screen is locked, I can see at the logs the actual width of the children being returned (I think the activity is being paused).
I'm really stuck and this is needed because I need to dynamically place those views depending on the children width.
You might be able to get with the below. But as others pointed out, this probably isn't a great idea.
LinearLayout.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
LinearLayout.getMeasuredWidth();
inside the onCreate , views still can't know the state of the nearby views and the children ,etc... so only after all is prepared and the layout process is done , you can get the size of views .
here's a quick code for getting the size of the view just before it's being drawn:
private static void runJustBeforeBeingDrawn(final View view, final Runnable runnable)
{
final ViewTreeObserver vto = view.getViewTreeObserver();
final OnPreDrawListener preDrawListener = new OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
Log.d(App.APPLICATION_TAG, CLASS_TAG + "onpredraw");
runnable.run();
final ViewTreeObserver vto = view.getViewTreeObserver();
vto.removeOnPreDrawListener(this);
return true;
}
};
vto.addOnPreDrawListener(preDrawListener);
}
alternatively , you can use addOnGlobalLayoutListener instead of addOnPreDrawListener if you wish.
example of usage :
runJustBeforeBeingDrawn(view,new Runnable()
{
#Override
public void run()
{
int width=view.getWidth();
int height=view.getHeight();
}
});
another approach is to use onWindowFocusChanged (and check that hasFocus==true) , but that's not always the best way ( only use for simple views-creation, not for dynamic creations)
EDIT: Alternative to runJustBeforeBeingDrawn: https://stackoverflow.com/a/28136027/878126
https://stackoverflow.com/a/3594216/1397218
So you should somehow change your logic.
The example is pretty straightforward: i want to let the user know about what the app is doing by just showing a text (canvas.drawText()). Then, my first message appears, but not the other ones. I mean, i have a "setText" method but it doesn't updates.
onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(splash); // splash is the view class
loadResources();
splash.setText("this");
boundWebService();
splash.setText("that"):
etc();
splash.setText("so on");
}
The view's text drawing works by doing just a drawText in onDraw();, so setText changes the text but doesn't show it.
Someone recommended me replacing the view with a SurfaceView, but it would be alot of trouble for just a couple of updates, SO... how the heck can i update the view dinamically at runtime?
It should be quite simple, just showing a text for say 2 seconds and then the main thread doing his stuff and then updating the text...
Thanks!
Update:
I tried implementing handler.onPost(), but is the same story all over again. Let me put you the code:
public class ThreadViewTestActivity extends Activity {
Thread t;
Splash splash;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
splash = new Splash(this);
t = new Thread(splash);
t.start();
splash.setTextow("OA");
try { Thread.sleep(4000); } catch (InterruptedException e) { }
splash.setTextow("LALA");
}
}
And:
public class Splash implements Runnable {
Activity activity;
final Handler myHandler = new Handler();
public Splash(Activity activity) {
this.activity=activity;
}
#Override
public void run() {
// TODO Auto-generated method stub
}
public synchronized void setTextow(final String textow) {
// Wrap DownloadTask into another Runnable to track the statistics
myHandler.post(new Runnable() {
#Override
public void run() {
TextView t = (TextView)activity.findViewById(R.id.testo);
t.setText(textow);
t.invalidate();
}
});
}
}
Although splash is in other thread, i put a sleep on the main thread, i use the handler to manage UI and everything, it doesn't changes a thing, it only shows the last update.
I haven't hit this yet, but I think the usual pattern is to do lengthy initialization in a background thread, and use Handler.post() to update the UI. See http://developer.android.com/reference/android/widget/ProgressBar.html for a different, but possibly related, example.
Also see this answer, especially the first paragraph:
The problem is most likely that you
are running the splash screen (some
sort of Dialog such as ProgressDialog
I assume) in the same thread as all
the work being done. This will keep
the view of the splash screen from
being updated, which can keep it from
even getting displayed to the screen.
You need to display the splash screen,
kick off an instance of AsyncTask to
go download all your data, then hide
the splash screen once the task is
complete.
Update (based on your update and your comment): You are not supposed to update the UI in any thread except the one where your Activity is created. Why is it impossible for you to load your resources in a background thread?
First: onCreate is executed on main UI thread of application so no UI updates until you leave it. Basically you need one thread to execute long running tasks and some mechanism to push updates into the UI.
Most usual approach is to extend AsyncTask see this link for further info
i suppose that your view is an extended view and you call onDraw for drawing the view, so, maybe the view isn´t 'refresh' their state, so try this
onCreate(Bundle bundle) {
setContentView(splash); // splash is the view class
loadResources();
splash.setText("this");
splash.invalidate();
boundWebService();
splash.setText("that"):
splash.invalidate();
etc();
splash.setText("so on");
splash.invalidate();
}
I use an BaseAdapter to display a list of objects. This objects are fetched from a server. The getView() method of the BaseAdapter is the following:
/* (non-Javadoc)
* #see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
*/
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout content = (LinearLayout) View.inflate(lexs, R.layout.favorite_item, null);
LinearLayout paragraphView = new LinearLayout(lexs);
paragraphView.setBackgroundColor(Color.RED);
paragraphView.setGravity(Gravity.CENTER_VERTICAL);
paragraphView.setOrientation(LinearLayout.HORIZONTAL);
ImageView img = new ImageView(lexs);
img.setImageResource(R.drawable.down);
paragraphView.addView(img);
img.setPadding(0, 0, 5, 0);
img.setOnClickListener(new WorkspaceOnClickListener(position));
TextView text = new TextView(lexs);
text.setTextColor(Color.WHITE);
text.setText(favorites.get(position).getParentChapter().getBook().getName() + ": §" + favorites.get(position).getName());
text.setOnClickListener(new ParagraphOnClickListener(position));
DisplayMetrics metrics = new DisplayMetrics();
lexs.getWindowManager().getDefaultDisplay().getMetrics(metrics);
int maxWidth = metrics.widthPixels - 100;
text.setMaxWidth(maxWidth);
text.setMinWidth(maxWidth);
paragraphView.addView(text);
ImageView imgClose = new ImageView(lexs);
imgClose.setMinimumHeight(30);
imgClose.setMinimumWidth(30);
imgClose.setImageResource(R.drawable.close);
imgClose.setPadding(5, 0, 0, 0);
paragraphView.addView(imgClose);
imgClose.setOnClickListener(new CloseListener(position));
content.addView(paragraphView);
content.setPadding(0, 0, 0, 10);
if (favorites.get(position).isExpanded()) {
LinearLayout textLayer = new LinearLayout(lexs);
textLayer.setBackgroundColor(Color.rgb(214, 214, 214));
LinearLayout left = new LinearLayout(lexs);
left.setOrientation(LinearLayout.VERTICAL);
ImageView moveUp = new ImageView(lexs);
moveUp.setImageResource(R.drawable.move_up);
moveUp.setOnClickListener(new MoveListener(UP, position));
moveUp.setPadding(0, 0, 0, 10);
left.addView(moveUp);
ImageView moveDown = new ImageView(lexs);
moveDown.setImageResource(R.drawable.move_down);
moveDown.setOnClickListener(new MoveListener(DOWN, position));
left.addView(moveDown);
TextView paragraphText = new TextView(lexs);
paragraphText.setText(favorites.get(position).getText());
paragraphText.setTextColor(Color.BLACK);
LinearLayout right = new LinearLayout(lexs);
right.setOrientation(LinearLayout.HORIZONTAL);
right.addView(paragraphText);
textLayer.addView(left);
textLayer.addView(right);
content.addView(textLayer);
img.setImageResource(R.drawable.up);
}
return content;
}
}
So I'd like that this whole method is called in background and during the method is executed, a ProgresDialog is shown. I defined the ProgressDialog the following way:
public class LoadingInformation {
private static ProgressDialog progressDialog;
public static void showProgressInformation(Context view) {
progressDialog = ProgressDialog.show(view, "Please wait...", "Doing Extreme Calculations...", true);
System.out.println("Start Loading Screen");
}
public static void stopShowingProgressInformation() {
Handler handler=new Handler();
handler.post(new Runnable(){public void run(){progressDialog.dismiss();}});
System.out.println("Stop Loading Screen");
}
}
But the problem is, i don't know how to implement the calls correctly. I tried to replace all lines:
adapter.notifyDataSetChanged();
With the following code:
public void updateFavoriteList() {
LoadingInformation.showProgressInformation(lexs);
new Thread() {
public void run() {
lexs.runOnUiThread(new Runnable() {
#Override
public void run() {
adapter.notifyDataSetChanged();
LoadingInformation.stopShowingProgressInformation();
}
});
}
}.start();
}
But unfortunately this doesn't work as expected. Has anybody a hint how to do it betteR?
The getView() method of the adapter isn’t responsible for downloading the content in the list, all it does is build the current row being displayed in the ListView. getView() doesn’t have anything to do with actually downloading the data from a network resource, which is where you wan’t the ProgressDialog.
When you instantiate the adapter (not shown in your code) you pass the data you want your BaseAdapter to populate the ListView with. Either the data has already been downloaded and simply handed off to the adapter, or the adapter itself does in the constructor.
If you wan’t the dataset for your ListView to change, you’re going to have to create some setter method or have a method in the adapter that refreshes the dataset from the network automatically. In any case though, this is where you want the ProgressDialog to show, not while the getView() method is setting up the ListView row.
P.S. One other suggestion I'd give is to make use of the convertView param passed to getView(). It'll improve the performance of your list by a lot if you have a large list. Check out Romain Guy's presentation about ListView in either the Google I/O 2009 or 2010 talks.
EDIT:
So “favorties” is an array (or List, etc.) of data for your ListView. You most likely pass the “favorites” data to the adapter via a constructor argument. At that point the data already exists in the adapter, or you’re passing a network location so the adapter can fetch the data and display it in the ListView.
For simplicity sake, lets say you download some string data into a String array, before passing it to the adapter. Each element contains a string to be displayed in your ListView via a TextView. You pass that array to the adapter and it handles formatting the ListView for you with the logic in the getView() method.
Now while you are downloading the data to populate the ListView with, you want to show the ProgressDialog to let the user know something is happening. Like some people have already said, you would use an AsyncTask to accomplish this. AsyncTask will allow you to display a ProgressDialog via onPreExecute() while you actually download the data in doInBackground() and get rid of the dialog in onPostExecute().
I hope that makes sense.
The time consuming tasks should be handled with an AsyncTask.
Read https://developer.android.com/resources/articles/painless-threading.html
It has been added to the Android framework to help you do these time consuming things and update progress dialogs without having to code the boilerplate of Tasks and Handlers yourself.