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.
Related
I am trying to make a text change when a button located along with the text (layoutPasswd) in recycler view and to change it back if the button is again pressed.Like a password hiding button. The values to the adapter is from a static class object as arraylist. The problem occurring now is that the value for all the items (only for layoutPasswd) in recycler view is same.
public void onBindViewHolder(#NonNull final viewHolder holder, int position) {
holder.layoutUName.setText(users.get(position).getUserName());
pos = position;
holder.layoutPasswd.setText("********");
holder.btnViewChanger.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (holder.view1) {
holder.layoutPasswd.setText(users.get(pos).getPasswd());
holder.btnViewChanger.setText("hide");
holder.view1 = false;
} else {
holder.layoutPasswd.setText("********");;
holder.btnViewChanger.setText("Show");
holder.view1 = true;
}
}
});
You cannot rely on the ViewHolders or Views in a RecyclerView to hold any state, because they are recycled. Every time a view scrolls onto the screen, first it calls your onBindViewHolder function to update the contents of that ViewHolder to match the data.
Any configuration you set on the views or the ViewHolder instance in onBindViewHolder cannot be relied on to stay the same if the view scrolls off the screen, because the original ViewHolder might be recycled to be used for some other data, and when it scrolls back on screen, you might be looking at some other view that has been recycled from other data that just scrolled off the screen.
So if your views have configuration that you want to "stick", you have to back it up when you change it, and restore it in onBindViewHolder. The way you accomplish this will depend on how you are managing the data that you pass to the adapter.
If you can modify your User class, you can add a Boolean to it that stores whether it should show the password. Then in your onBindViewHolder, you restore the state based on this Boolean. And you also update this Boolean when the state changes.
I also updated the way the click listener works to simplify it for toggling. I removed the pos = position line, because almost certainly that is not something you should be doing.
public void onBindViewHolder(#NonNull final viewHolder holder, int position) {
final User user = users.get(position)
holder.layoutUName.setText(user.getUserName());
holder.layoutPasswd.setText(user.isShowPassword() ? user.getPasswd() : "********");
holder.btnViewChanger.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
user.setShowPassword(!user.isShowPassword());
holder.layoutPasswd.setText(user.isShowPassword() ? user.getPasswd() : "********");
holder.btnViewChanger.setText(user.isShowPassword() ? "hide" : "show");
}
});
// ...
}
If you cannot modify the User class, this is more complicated. Then the adapter should have its own ArrayList<Boolean> to store the state by position index, but you need to keep this list at least as long as the data that is bound, and reset everything to false if the whole list of data is refreshed.
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.
I have a ListView of comments, think of it like a sort of Twitter. So each comment has a button and a count, like a retweet button and a TextView that shows the number of retweets. When the button is clicked I execute an AsyncTask that updates the value on the online DB and when it gets back it updates the value in the ArrayList. Finally I call notifyDataSetChanged() and also call myTextView.SetText(//new number) but the new value is never shown and the old one stays on the UI.
I skipped the code that doesnt take part in this action.
This is the setOnClickListener called from the adapter:
agreeB.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FetchCommentToUpVote upVote = new FetchCommentToUpVote();
upVote.execute(getItem(position).getId());
}
}
This is how I update the local value of the ArrayList and how I try to display it:
#Override
protected Integer doInBackground(String... params) {
CommentsFragment.lista.set(mPosition, commentFetched);
}
#Override
protected void onPostExecute(Integer pos){
num_agree.setText(Integer.toString(CommentsFragment.lista.get(pos).getAgree()));
notifyDataSetChanged();
}
I have checked and the value in the ArrayList is certainly changed but never displayed.
Thanks in advanced!
Check that your CommentsFragment.lista and your array adapter variable are syncronized.
Edit: Create a method setArrayItems in your adapter and call this when your CommentsFragment.lista is updated.
Hope it helps!!
I am experimenting with some Animations with Android. Basically I have this Activity that displays data from an API. Now when the user wishes to refresh that data, the Front card so to speak of which contains the data, flips to the Back card which is a loading animation. The process of loading new data kicks off and when it finishes the method that flips the so to speak of cards is called so the data can be revealed.
With my intentions in place I have everything explained above 99% complete. When I have the data from the API back in the UI thread controlling the cards, I am ready to update those interface objects that are part of the front card.
But setting the text of a TextView no longer works, and I'll explain that. When the front card gets switched out, the back card comes to the foreground, but when it comes time for the front card to come back with new data, it comes back with its default XML values. I can set TextViews all day of this front card but it return blank.
Code is below, please keep in mind I like to reverse engineer things like this. So some code I do not understand, it the sample from the Google Android development guide.
Here I set up some kind of fragment manager in the onCreate method of the Activity.
mShowingBack is a boolean for if the back card is the card in the foreground.
if (savedInstanceState == null) {
// If there is no saved instance state, add a fragment representing the
// front of the card to this activity. If there is saved instance state,
// this fragment will have already been added to the activity.
getFragmentManager()
.beginTransaction()
.add(R.id.container, new CardFrontFragment())
.commit();
} else {
mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0);
}
// Monitor back stack changes to ensure the action bar shows the appropriate
// button (either "photo" or "info").
getFragmentManager().addOnBackStackChangedListener(this);
Even if I do not update the textviews with the new data, the front card is still bare and empty. Below is the method that flips the the two cards:
public void onClickFlipCard(View view) {
if (mShowingBack) {
getFragmentManager().popBackStack();
return;
}
// Flip to the back.
mShowingBack = true;
// Create and commit a new fragment transaction that adds the fragment for the back of
// the card, uses custom animations, and is part of the fragment manager's back stack.
getFragmentManager()
.beginTransaction()
// Replace the default fragment animations with animator resources representing
// rotations when switching to the back of the card, as well as animator
// resources representing rotations when flipping back to the front (e.g. when
// the system Back button is pressed).
.setCustomAnimations(
R.animator.card_flip_right_in, R.animator.card_flip_right_out,
R.animator.card_flip_left_in, R.animator.card_flip_left_out)
// Replace any fragments currently in the container view with a fragment
// representing the next page (indicated by the just-incremented currentPage
// variable).
.replace(R.id.container, new CardBackFragment())
// Add this transaction to the back stack, allowing users to press Back
// to get to the front of the card.
.addToBackStack(null)
// Commit the transaction.
.commit();
}
When that method is called, even though it is not included the method call for retrieving the new data kicks off. I am not for sure if like creates a new instance of the card layout so when I try to modify it, I am not actually in touch with it or something?
When I get the data for example all I do is use the textview object used before the data load to set the new data:
textview_example.setText("Example, but nothing appears...")
Even further, when I call the textviews getText method, it returns text but none is shown...Any help would be great. If it matters here are the two fragment classes that represent the two cards,(The front where data is displayed and the back where a loading animation is shown).
Front Card Fragment:
public class CardFrontFragment extends Fragment {
public CardFrontFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_card_front, container, false);
}
}
Back Card Fragment
public class CardBackFragment extends Fragment {
public CardBackFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_card_back, container, false);
}
}
Here is also a link to the Android guide that I follow...
Animation works but afterward as I said I can not edit any of the properties of interface objects. Thanks!
Link: http://developer.android.com/training/animation/cardflip.html
I've currently got an activity that creates a view. this view uses other classes (such as one to create a random sequence of integers). I need to run a method (which will display the sequence using bitmaps) once the view is created. So once the user clicks "Start Game" this sequence will be displayed.
I've tried calling the method after setting the content view inside the onCreate method by the sequence is not generated (all 0's) correctly. I've tries this also with onStart and onFinishInflate inside the myView class.
Is there a way i can run this method after everything is inflated and initialized? So after the user clicks "Start Game" and the view is changed, the method needs to run.
Thanks for looking.
Edit: A failed attempt.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.gameView = new GameView(getApplicationContext(), getCellSource(getApplicationContext()));
setContentView(this.gameView);
// this.gameView.displaySequence(this.gameView.gameEngine.getGenSequence()); Need this to run once view is displayed.
}
Try using ViewTreeObserver as follow:
final View yourView = View.inflate(....);
ViewTreeObserver observer = yourView .getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
yourView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// Do what you need with yourView here...
}
});
Notice that the function removeGlobalOnLayoutListener(this) is different in some sdk versions.