I am implementing jeremyfeinstein sliding menu library. for this I have an activity which extends SherlockFragmentActivity. for this activity I have two fragments one is for left menu and another is for Main screen. Everything is working fine and smooth but still I have a problem that is when my Sliding menu activity starts, it becomes blank for 8-10 seconds. after 8-10 seconds my main screen fragment becomes visible.
this is my base class:
public class SlidingFragmentActivity extends SherlockFragmentActivity implements SlidingActivityBase {
private SlidingActivityHelper mHelper;
/* (non-Javadoc)
* #see android.support.v4.app.FragmentActivity#onCreate(android.os.Bundle)
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHelper = new SlidingActivityHelper(this);
mHelper.onCreate(savedInstanceState);
}
/* (non-Javadoc)
* #see android.app.Activity#onPostCreate(android.os.Bundle)
*/
#Override
public void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mHelper.onPostCreate(savedInstanceState);
}
/* (non-Javadoc)
* #see android.app.Activity#findViewById(int)
*/
#Override
public View findViewById(int id) {
View v = super.findViewById(id);
if (v != null)
return v;
return mHelper.findViewById(id);
}
/* (non-Javadoc)
* #see android.support.v4.app.FragmentActivity#onSaveInstanceState(android.os.Bundle)
*/
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mHelper.onSaveInstanceState(outState);
}
/* (non-Javadoc)
* #see android.app.Activity#setContentView(int)
*/
#Override
public void setContentView(int id) {
setContentView(getLayoutInflater().inflate(id, null));
}
/* (non-Javadoc)
* #see android.app.Activity#setContentView(android.view.View)
*/
#Override
public void setContentView(View v) {
setContentView(v, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
}
/* (non-Javadoc)
* #see android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
#Override
public void setContentView(View v, LayoutParams params) {
super.setContentView(v, params);
mHelper.registerAboveContentView(v, params);
}
/* (non-Javadoc)
* #see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#setBehindContentView(int)
*/
public void setBehindContentView(int id) {
setBehindContentView(getLayoutInflater().inflate(id, null));
}
/* (non-Javadoc)
* #see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#setBehindContentView(android.view.View)
*/
public void setBehindContentView(View v) {
setBehindContentView(v, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
}
/* (non-Javadoc)
* #see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#setBehindContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setBehindContentView(View v, LayoutParams params) {
mHelper.setBehindContentView(v, params);
}
/* (non-Javadoc)
* #see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#getSlidingMenu()
*/
public SlidingMenu getSlidingMenu() {
return mHelper.getSlidingMenu();
}
/* (non-Javadoc)
* #see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#toggle()
*/
public void toggle() {
mHelper.toggle();
}
/* (non-Javadoc)
* #see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#showAbove()
*/
public void showContent() {
mHelper.showContent();
}
/* (non-Javadoc)
* #see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#showBehind()
*/
public void showMenu() {
mHelper.showMenu();
}
/* (non-Javadoc)
* #see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#showSecondaryMenu()
*/
public void showSecondaryMenu() {
mHelper.showSecondaryMenu();
}
/* (non-Javadoc)
* #see com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivityBase#setSlidingActionBarEnabled(boolean)
*/
public void setSlidingActionBarEnabled(boolean b) {
mHelper.setSlidingActionBarEnabled(b);
}
/* (non-Javadoc)
* #see android.app.Activity#onKeyUp(int, android.view.KeyEvent)
*/
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
boolean b = mHelper.onKeyUp(keyCode, event);
if (b) return b;
return super.onKeyUp(keyCode, event);
}
}
Here is my Main activity which loads fragments
public class SliderMenuMainActivity extends SlidingFragmentActivity
{
private Fragment mContent;
ImageButton btnToggle,refresh;
String ns = Context.NOTIFICATION_SERVICE;
public static int msg_count = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(obj==null)
obj=new Progress_Dialog(this);
obj.setCancelable(false);
obj.onPreExecute("MAIN Screen");
if (savedInstanceState != null)
mContent = getSupportFragmentManager().getFragment(savedInstanceState, "mContent");
prepareScreen();
}
private void prepareScreen()
{
setContentView(R.layout.activity_slider_menu_main);
ActionBar ab = getSherlock().getActionBar();
LayoutInflater li = LayoutInflater.from(this);
View customView = li.inflate(R.layout.custom_titlebar, null);
customView.setLayoutParams(new ActionBar.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT ,ActionBar.LayoutParams.MATCH_PARENT));
ab.setCustomView(customView);
ab.setDisplayShowHomeEnabled(false);
ab.setDisplayShowCustomEnabled(true);
if (findViewById(R.id.menu_frame) == null)
{
setBehindContentView(R.layout.menu_frame);
getSlidingMenu().setSlidingEnabled(true);
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
// show home as up so we can toggle
//getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
else
{
// add a dummy view
View v = new View(this);
setBehindContentView(v);
getSlidingMenu().setSlidingEnabled(false);
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_NONE);
}
if (mContent == null)
mContent = new MainScreenFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, mContent).commit();
// set the Behind View Fragment
getSupportFragmentManager().beginTransaction().replace(R.id.menu_frame, new MenuFragment()).commit();
// customize the SlidingMenu
SlidingMenu sm = getSlidingMenu();
sm.setBehindOffsetRes(R.dimen.slidingmenu_offset);
sm.setShadowWidthRes(R.dimen.shadow_width);
sm.setShadowDrawable(R.drawable.shadow);
sm.setBehindScrollScale(0.25f);
sm.setFadeDegree(0.25f);
setSlidingActionBarEnabled(false);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
toggle();
}
return super.onOptionsItemSelected(item);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager().putFragment(outState, "mContent", mContent);
}
public void switchContent(final Fragment fragment) {
mContent = fragment;
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
Handler h = new Handler();
h.postDelayed(new Runnable() {
public void run() {
getSlidingMenu().showContent();
}
}, 50);
}
#Override
public void onResume()
{
super.onResume();
if(obj!=null)
obj.onPostExecute();
}
}
So please help me with this and show me a way to overcome this problem. thanks in advance.
EDIT 1
I have placed all the code to the onResume() but it still have same problem. now it is taking about 2 to 3 second to become activity visible.why it is taking this amount of time when I don't have any code into onCreate(). I have removed all the libraries and now I am using only support library(4 and 7) with navigation drawer with same result.
Issue:
blank screen displayed because your content view is not set up until prepareScreen executes.
Solution:
you need to put setContentView(R.layout.activity_slider_menu_main); directly after super.onCreate(savedInstanceState); inside onCreate method.
You are doing lot of operations in onCreate(). You need to just set the primary layout and stuff like that. all the operations can be called in onResume().
Related
I've got an Activity that implements a retained fragment to perform a time consuming task. This allows me to rotate the screen without loosing a reference to my async task and without stopping it from being executed. I have a button on the activity to launch the Async task. Everytime the activity is created, for example due to a rotated screen, the activity checks if its retained fragment has an async task running. If so, it shows the progressDialog to let the user know there are still some tasks running on the background. I attach the code that does what I mentioned. Actually it works. However, what is strange is that it works everytime but the first! When I push the button to launch the Async process for the first time, the progressDialog is not shown, eventhough "onPreExecute()" is called and the line "progressDialog.show()" executed. If I rotate the screen after I push the button the progressDialog shows and when the process ends, If I push the button again then it works just fine. As I said, it is working all the time except the first one. Any idea why?
Thanks!
Activity
public class Activity extends AppCompatActivity implements ActivityTaskFragment.TaskCallbacks{
private ProgressDialog progressDialog;
private static final String TAG_TASK_FRAGMENT = "task_fragment";
private ActivityTaskFragment activityTaskFragment;
//Some additional code here
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crear_turno);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//------------------------------------------------------
setListeners();
setFragment();
createProgressDialog();
showProgressDialog();
}
private void showProgressDialog()
{
if(activityTaskFragment!=null)
{
if(activityTaskFragment.isRunning())
{
progressDialog.show();
}
}
}
private void setFragment()
{
FragmentManager fm = getFragmentManager();
activityTaskFragment = (ActivityTaskFragment) fm.findFragmentByTag(TAG_TASK_FRAGMENT);
// If the Fragment is non-null, then it is currently being
// retained across a configuration change.
if (activityTaskFragment == null) {
activityTaskFragment = new ActivityTaskFragment();
fm.beginTransaction().add(activityTaskFragment, TAG_TASK_FRAGMENT).commit();
}
}
private void setListeners() {
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
activityTaskFragment.execute();
}
});
}
//fragment interface implementation
#Override
public void onPreExecute()
{
if(progressDialog!=null)
progressDialog.show();
}
#Override
public void onCancelled() {
if(progressDialog!=null)
{
if(progressDialog.isShowing())
{
progressDialog.dismiss();
}
}
}
#Override
public void onPostExecute()
{
if(progressDialog!=null)
{
if(progressDialog.isShowing())
{
progressDialog.dismiss();
}
}
}
private void createProgressDialog()
{
if(progressDialog == null)
{
progressDialog = new ProgressDialog(Activity.this);
progressDialog.setTitle("Executing job");
progressDialog.setMessage("please wait...");
}
}
}
TaskFragment
/**
* This Fragment manages a single background task and retains
* itself across configuration changes.
*/
public class ActivityTaskFragment extends Fragment {
/**
* Callback interface through which the fragment will report the
* task's progress and results back to the Activity.
*/
interface TaskCallbacks {
void onPreExecute();
void onCancelled();
void onPostExecute();
}
private TaskCallbacks mCallbacks;
private CheckTask mTask;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (TaskCallbacks) activity;
}
/**
* Hold a reference to the parent Activity so we can report the
* task's current progress and results. The Android framework
* will pass us a reference to the newly created Activity after
* each configuration change.
*/
public void execute()
{
mTask.cancel(true);
mTask = new CheckTask();
mTask.execute();
}
public void cancel()
{
if(mTask != null)
{
mTask.cancel(true);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if(context instanceof Activity) {
Activity activity = (Activity) context;
mCallbacks = (TaskCallbacks) activity;
}
}
/**
* This method will only be called once when the retained
* Fragment is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
mTask = new CheckTask();
}
/**
* Set the callback to null so we don't accidentally leak the
* Activity instance.
*/
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
/**
* A dummy task that performs some (dumb) background work and
* proxies progress updates and results back to the Activity.
*
* Note that we need to check if the callbacks are null in each
* method in case they are invoked after the Activity's and
* Fragment's onDestroy() method have been called.
*/
private class CheckTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute()
{
if (mCallbacks != null) {
mCallbacks.onPreExecute();
}
}
/**
* Note that we do NOT call the callback object's methods
* directly from the background thread, as this could result
* in a race condition.
*/
#Override
protected Void doInBackground(Void... ignore) {
/*
LONG TIME CONSUMING TASK
*/
return null;
}
#Override
protected void onCancelled() {
running = false;
if (mCallbacks != null) {
mCallbacks.onCancelled();
}
}
#Override
protected void onPostExecute(Void ignore)
{
running = false;
if (mCallbacks != null) {
mCallbacks.onPostExecute();
}
}
}
public boolean isRunning()
{
if(mTask!=null)
{
if(mTask.getStatus() == AsyncTask.Status.RUNNING)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
}
I have a messaging app for Android and am attempting to filter my user list that currently shows all signed up users. I am using Parse. I would like to query the Parse database to filter the list of all users to only show users that you have current conversations with. So, some sort of inbox scenario. I am fairly new to android development and would appreciate some help implementing this logic. I've been scouring the internet (including stack overflow) for the past 24 hours and finally caved in and tried my luck here. Any help is greatly appreciated!
UserList.java
public class UserList extends CustomActivity
{
/** The Chat list. */
private ArrayList<ParseUser> uList;
/** The user. */
public static ParseUser user;
/* (non-Javadoc)
* #see android.support.v4.app.FragmentActivity#onCreate(android.os.Bundle)
*/
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
user = ParseUser.getCurrentUser();
setContentView(R.layout.user_list);
updateUserStatus(true);
}
/* (non-Javadoc)
* #see android.support.v4.app.FragmentActivity#onDestroy()
*/
#Override
protected void onDestroy()
{
super.onDestroy();
updateUserStatus(false);
}
/* (non-Javadoc)
* #see android.support.v4.app.FragmentActivity#onResume()
*/
#Override
protected void onResume()
{
super.onResume();
loadUserList();
}
/**
* Update user status.
*
* #param online
* true if user is online
*/
private void updateUserStatus(boolean online)
{
user.put("online", online);
user.saveEventually();
}
/**
* Load list of users.
*/
private void loadUserList()
{
final ProgressDialog dia = ProgressDialog.show(this, null,
getString(R.string.alert_loading));
ParseUser.getQuery().whereNotEqualTo("username", user.getUsername())
.findInBackground(new FindCallback<ParseUser>() {
#Override
public void done(List<ParseUser> li, ParseException e) {
dia.dismiss();
if (li != null) {
if (li.size() == 0)
Toast.makeText(UserList.this,
R.string.msg_no_user_found,
Toast.LENGTH_SHORT).show();
uList = new ArrayList<ParseUser>(li);
ListView list = (ListView) findViewById(R.id.list);
list.setAdapter(new UserAdapter());
list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0,
View arg1, int pos, long arg3) {
startActivity(new Intent(UserList.this,
Chat.class).putExtra(
Const.EXTRA_DATA, uList.get(pos)
.getUsername()));
}
});
} else {
Utils.showDialog(
UserList.this,
getString(R.string.err_users) + " "
+ e.getMessage());
e.printStackTrace();
}
}
});
}
/**
* The Class UserAdapter is the adapter class for User ListView. This
* adapter shows the user name and it's only online status for each item.
*/
private class UserAdapter extends BaseAdapter
{
/* (non-Javadoc)
* #see android.widget.Adapter#getCount()
*/
#Override
public int getCount()
{
return uList.size();
}
/* (non-Javadoc)
* #see android.widget.Adapter#getItem(int)
*/
#Override
public ParseUser getItem(int arg0)
{
return uList.get(arg0);
}
/* (non-Javadoc)
* #see android.widget.Adapter#getItemId(int)
*/
#Override
public long getItemId(int arg0)
{
return arg0;
}
/* (non-Javadoc)
* #see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
*/
#Override
public View getView(int pos, View v, ViewGroup arg2)
{
if (v == null)
v = getLayoutInflater().inflate(R.layout.chat_item, null);
ParseUser c = getItem(pos);
TextView lbl = (TextView) v;
lbl.setText(c.getUsername());
lbl.setCompoundDrawablesWithIntrinsicBounds(
c.getBoolean("online") ? R.drawable.ic_online
: R.drawable.ic_offline, 0, R.drawable.arrow, 0);
return v;
}
}
}
Conversation.java
public class Conversation
{
/** The Constant STATUS_SENDING. */
public static final int STATUS_SENDING = 0;
/** The Constant STATUS_SENT. */
public static final int STATUS_SENT = 1;
/** The Constant STATUS_FAILED. */
public static final int STATUS_FAILED = 2;
/** The msg. */
private String msg;
/** The status. */
private int status = STATUS_SENT;
/** The date. */
private Date date;
/** The sender. */
private String sender;
/**
* Instantiates a new conversation.
*
* #param msg
* the msg
* #param date
* the date
* #param sender
* the sender
*/
public Conversation(String msg, Date date, String sender)
{
this.msg = msg;
this.date = date;
this.sender = sender;
}
/**
* Instantiates a new conversation.
*/
public Conversation()
{
}
/**
* Gets the msg.
*
* #return the msg
*/
public String getMsg()
{
return msg;
}
/**
* Sets the msg.
*
* #param msg
* the new msg
*/
public void setMsg(String msg)
{
this.msg = msg;
}
/**
* Checks if is sent.
*
* #return true, if is sent
*/
public boolean isSent()
{
return UserList.user.getUsername().equals(sender);
}
/**
* Gets the date.
*
* #return the date
*/
public Date getDate()
{
return date;
}
/**
* Sets the date.
*
* #param date
* the new date
*/
public void setDate(Date date)
{
this.date = date;
}
/**
* Gets the sender.
*
* #return the sender
*/
public String getSender()
{
return sender;
}
/**
* Sets the sender.
*
* #param sender
* the new sender
*/
public void setSender(String sender)
{
this.sender = sender;
}
/**
* Gets the status.
*
* #return the status
*/
public int getStatus()
{
return status;
}
/**
* Sets the status.
*
* #param status
* the new status
*/
public void setStatus(int status)
{
this.status = status;
}
}
Chat.java
public class Chat extends CustomActivity
{
/** The Conversation list. */
private ArrayList<Conversation> convList;
/** The chat adapter. */
private ChatAdapter adp;
/** The Editext to compose the message. */
private EditText txt;
/** The user name of buddy. */
private String buddy;
/** The date of last message in conversation. */
private Date lastMsgDate;
/** Flag to hold if the activity is running or not. */
private boolean isRunning;
/** The handler. */
private static Handler handler;
/* (non-Javadoc)
* #see android.support.v4.app.FragmentActivity#onCreate(android.os.Bundle)
*/
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.chat);
convList = new ArrayList<Conversation>();
ListView list = (ListView) findViewById(R.id.list);
adp = new ChatAdapter();
list.setAdapter(adp);
list.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
list.setStackFromBottom(true);
txt = (EditText) findViewById(R.id.txt);
txt.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_MULTI_LINE);
setTouchNClick(R.id.btnSend);
buddy = getIntent().getStringExtra(Const.EXTRA_DATA);
getActionBar().setTitle(buddy);
handler = new Handler();
}
/* (non-Javadoc)
* #see android.support.v4.app.FragmentActivity#onResume()
*/
#Override
protected void onResume()
{
super.onResume();
isRunning = true;
loadConversationList();
}
/* (non-Javadoc)
* #see android.support.v4.app.FragmentActivity#onPause()
*/
#Override
protected void onPause()
{
super.onPause();
isRunning = false;
}
/* (non-Javadoc)
* #see com.socialshare.custom.CustomFragment#onClick(android.view.View)
*/
#Override
public void onClick(View v)
{
super.onClick(v);
if (v.getId() == R.id.btnSend)
{
sendMessage();
}
}
/**
* Call this method to Send message to other person. It does nothing if the text
* is empty otherwise it creates a Parse object for Chat message and send it
* to Parse server.
*/
private void sendMessage()
{
if (txt.length() == 0)
return;
String s = txt.getText().toString();
final Conversation c = new Conversation(s, new Date(),
UserList.user.getUsername());
c.setStatus(Conversation.STATUS_SENDING);
convList.add(c);
adp.notifyDataSetChanged();
txt.setText(null);
ParseObject po = new ParseObject("Chat");
po.put("sender", UserList.user.getUsername());
po.put("receiver", buddy);
// po.put("createdAt", "");
po.put("message", s);
po.saveEventually(new SaveCallback() {
#Override
public void done(ParseException e)
{
if (e == null)
c.setStatus(Conversation.STATUS_SENT);
else
c.setStatus(Conversation.STATUS_FAILED);
adp.notifyDataSetChanged();
}
});
}
/**
* Load the conversation list from Parse server and save the date of last
* message that will be used to load only recent new messages
*/
private void loadConversationList()
{
ParseQuery<ParseObject> q = ParseQuery.getQuery("Chat");
if (convList.size() == 0)
{
// load all messages...
ArrayList<String> al = new ArrayList<String>();
al.add(buddy);
al.add(UserList.user.getUsername());
q.whereContainedIn("sender", al);
q.whereContainedIn("receiver", al);
}
else
{
// load only newly received message..
if (lastMsgDate != null)
q.whereGreaterThan("createdAt", lastMsgDate);
q.whereEqualTo("sender", buddy);
q.whereEqualTo("receiver", UserList.user.getUsername());
}
q.orderByDescending("createdAt");
q.setLimit(30);
q.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> li, ParseException e)
{
if (li != null && li.size() > 0)
{
for (int i = li.size() - 1; i >= 0; i--)
{
ParseObject po = li.get(i);
Conversation c = new Conversation(po
.getString("message"), po.getCreatedAt(), po
.getString("sender"));
convList.add(c);
if (lastMsgDate == null
|| lastMsgDate.before(c.getDate()))
lastMsgDate = c.getDate();
adp.notifyDataSetChanged();
}
}
handler.postDelayed(new Runnable() {
#Override
public void run()
{
if (isRunning)
loadConversationList();
}
}, 1000);
}
});
}
/**
* The Class ChatAdapter is the adapter class for Chat ListView. This
* adapter shows the Sent or Receieved Chat message in each list item.
*/
private class ChatAdapter extends BaseAdapter
{
/* (non-Javadoc)
* #see android.widget.Adapter#getCount()
*/
#Override
public int getCount()
{
return convList.size();
}
/* (non-Javadoc)
* #see android.widget.Adapter#getItem(int)
*/
#Override
public Conversation getItem(int arg0)
{
return convList.get(arg0);
}
/* (non-Javadoc)
* #see android.widget.Adapter#getItemId(int)
*/
#Override
public long getItemId(int arg0)
{
return arg0;
}
/* (non-Javadoc)
* #see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
*/
#Override
public View getView(int pos, View v, ViewGroup arg2)
{
Conversation c = getItem(pos);
if (c.isSent())
v = getLayoutInflater().inflate(R.layout.chat_item_sent, null);
else
v = getLayoutInflater().inflate(R.layout.chat_item_rcv, null);
TextView lbl = (TextView) v.findViewById(R.id.lbl1);
lbl.setText(DateUtils.getRelativeDateTimeString(Chat.this, c
.getDate().getTime(), DateUtils.SECOND_IN_MILLIS,
DateUtils.DAY_IN_MILLIS, 0));
lbl = (TextView) v.findViewById(R.id.lbl2);
lbl.setText(c.getMsg());
lbl = (TextView) v.findViewById(R.id.lbl3);
if (c.isSent())
{
if (c.getStatus() == Conversation.STATUS_SENT)
lbl.setText("Delivered");
else if (c.getStatus() == Conversation.STATUS_SENDING)
lbl.setText("Sending...");
else
lbl.setText("Failed");
}
else
lbl.setText("");
return v;
}
}
/* (non-Javadoc)
* #see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
*/
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId() == android.R.id.home)
{
finish();
}
return super.onOptionsItemSelected(item);
}
}
I am trying to implement a FlipCard behavior in a ListView for my items and the bug is that my convertView don't update its visibility state according to the visibility I set in the getView method. It's like nobody cares of of my visibility changes.
To reproduce the problem: click on an item picture (sun, cloud...), it will flip the item and present its backside. Then scroll up or down until the convertView flipped is reused by a View that is not flipped. The not flipped view will not display its content anymore.
The first item should displays its content but it displays nothing because the convertView used (the one gave by the getView parameter) had its visibility set to GONE the last time it was used.
You can find the full project here:
https://github.com/MathiasSeguy-Android2EE/ForecastYahooRest and you have to check out the branch "flipcard"
So the ArrayAdapter involves:
package com.android2ee.formation.restservice.sax.forecastyahoo.view.forecast.arrayadpater;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorSet;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.text.format.DateFormat;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android2ee.formation.restservice.sax.forecastyahoo.R;
import com.android2ee.formation.restservice.sax.forecastyahoo.transverse.model.YahooForcast;
import java.util.List;
/**
* #author Mathias Seguy (Android2EE)
* #goals
* This class aims to display the forecast in the listView
*/
public class ForecastArrayAdapter extends ArrayAdapter<YahooForcast> {
/**
* Handler to launch the animation runnable
*/
Handler handlerForAnimation;
/**
* To know when the item is flipped or not
* When flipped it show us its back side else its front side
*/
SparseBooleanArray isFlipped;
/**
* To detect the first launch
*/
int notifyDataSetChangedCallsNumber = 0;
/**
* The layout inflater
*/
LayoutInflater inflater;
/**
* The Context
*/
Context ctx;
/**
* To know if the device is postJellyBean or not
*/
boolean postJB;
/**
* To know if the device is postHoneyComb or not
*/
boolean postHC;
/**
* Drawable used for the backside of the item
*/
Drawable[] drawableBackground;
/**
*
* #param context
* #param forecast
*/
public ForecastArrayAdapter(Context context, List<YahooForcast> forecast) {
super(context, R.layout.item_forecast, forecast);
inflater = LayoutInflater.from(context);
ctx = context;
postJB = context.getResources().getBoolean(R.bool.postJB);
postHC = context.getResources().getBoolean(R.bool.postHC);
//instantiate the handler
handlerForAnimation = new Handler();
isFlipped=new SparseBooleanArray();
drawableBackground=new Drawable[5];
drawableBackground[0]=context.getResources().getDrawable(R.drawable.back1);
drawableBackground[1]=context.getResources().getDrawable(R.drawable.back2);
drawableBackground[2]=context.getResources().getDrawable(R.drawable.back3);
drawableBackground[3]=context.getResources().getDrawable(R.drawable.back4);
drawableBackground[4]=context.getResources().getDrawable(R.drawable.back5);
}
/**
* Private static better than temp
*/
private static View rowView;
/**
* Private static better than temp
*/
private static YahooForcast forcast;
/**
* Private static better than temp
*/
private static ViewHolder viewHolder;
/*
* (non-Javadoc)
*
* #see android.widget.ArrayAdapter#getView(int, android.view.View, android.view.ViewGroup)
*/
#SuppressLint("NewApi")
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.e("ForecastArrayAdapter","getView "+position);
rowView = convertView;
forcast = getItem(position);
if (rowView == null) {
// always add the layout, the parent and false
rowView = inflater.inflate(R.layout.item_forecast, null, false);
ViewHolder vh = new ViewHolder(rowView,position);
rowView.setTag(vh);
}
viewHolder = (ViewHolder) rowView.getTag();
//used for animation
viewHolder.currentPosition=position;
if (postJB) {
viewHolder.getImvIcon().setBackground(forcast.getImage());
viewHolder.getImvBack().setBackground(drawableBackground[position%5]);
} else {
viewHolder.getImvIcon().setBackgroundDrawable(forcast.getImage());
viewHolder.getImvBack().setBackgroundDrawable(drawableBackground[position % 5]);
}
if (forcast.getDate() != null) {
viewHolder.getTxvDate().setText(DateFormat.format("E dd MMM", forcast.getDate()));
} else {
viewHolder.getTxvDate().setText("unknown");
}
viewHolder.getTxvTendance().setText(forcast.getTendance());
if (forcast.getTempMax() != -1000) {
viewHolder.getTxvMax().setVisibility(View.VISIBLE);
viewHolder.getTxvMin().setVisibility(View.VISIBLE);
viewHolder.getTxvMax().setText(ctx.getString(R.string.max, forcast.getTempMax()));
viewHolder.getTxvMin().setText(ctx.getString(R.string.min, forcast.getTempMin()));
} else {
viewHolder.getTxvMax().setVisibility(View.GONE);
viewHolder.getTxvMin().setVisibility(View.GONE);
}
if (forcast.getTemp() != -1000) {
viewHolder.getTxvCurrent().setVisibility(View.VISIBLE);
viewHolder.getTxvCurrent().setText(ctx.getString(R.string.temp, forcast.getTemp()));
} else {
viewHolder.getTxvCurrent().setVisibility(View.GONE);
}
// launch animations to show the update to the user (not the first time but only when refreshing)
//because the first time is not an update, it's just loading data from db
if (notifyDataSetChangedCallsNumber >=2) {
viewHolder.launchUpdateAnimation(notifyDataSetChangedCallsNumber);
}
//and finally manage the visibility of the side : front or back side is visible
manageSideVisibility(position);
return rowView;
}
/* (non-Javadoc)
* #see android.widget.ArrayAdapter#notifyDataSetChanged()
*/
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
notifyDataSetChangedCallsNumber++;
}
/**************************************************
* Flipping Animation tricks
* **************************************************
*/
/**
* If the element has been flipped, flip it else set it has not flipped
* #param position
*/
private void manageSideVisibility(int position){
if(isFlipped.get(position)){
//the backside is visible
viewHolder.getImvBack().setVisibility(View.VISIBLE);
viewHolder.getLinRoot().setVisibility(View.GONE);
}else{
//the ffront is visible
viewHolder.getImvBack().setVisibility(View.GONE);
viewHolder.getLinRoot().setVisibility(View.VISIBLE);
}
}
/******************************************************************************************/
/** Runnable for animation **************************************************************************/
/******************************************************************************************/
public class MyRunnable implements Runnable {
/**
* The viewHolder that contains the view to animate
*/
private ViewHolder vh;
public MyRunnable(ViewHolder vh) {
this.vh=vh;
}
public void run() {
vh.animateUpdate();
}
}
/******************************************************************************************/
/** The ViewHolder pattern **************************************************************************/
/******************************************************************************************/
private class ViewHolder {
View view;
LinearLayout linRoot;
TextView txvDate;
TextView txvTendance;
ImageView imvIcon;
TextView txvCurrent;
TextView txvMin;
TextView txvMax;
TextView txvUpdating;
//For Update animation
Animation updateAnimation;
MyRunnable animationRunnable;
int dataTimeStamp=0;
//For animatibbbbbbon
ImageView imvBack;
int currentPosition;
//PostHoneyComb
Animator flipAnimatorIn;
Animator flipAnimatorOut;
Animator reverseFlipAnimatorIn;
Animator reverseFlipAnimatorOut;
AnimatorSet setFlip;
AnimatorSet setReverse;
//PreHoneyComb
Animation animInLegacy;
Animation animOutLegacy;
int id;
/**
* #param rowview
*/
private ViewHolder(View rowview,int position) {
super();
this.view = rowview;
animationRunnable=new MyRunnable(this);
id=position;
}
/**
* #return the txvDate
*/
public final TextView getTxvDate() {
if (null == txvDate) {
txvDate = (TextView) view.findViewById(R.id.date);
}
return txvDate;
}
/**
* #return the txvTendance
*/
public final TextView getTxvTendance() {
if (null == txvTendance) {
txvTendance = (TextView) view.findViewById(R.id.txv_tendance);
}
return txvTendance;
}
/**
* #return the imvIcon
*/
public final ImageView getImvIcon() {
if (null == imvIcon) {
imvIcon = (ImageView) view.findViewById(R.id.icon);
imvIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(postHC){
animateItem();
}else{
flipItemLegacy();
}
}
});
}
return imvIcon;
}
/**
* #return the imvBack
*/
public final ImageView getImvBack() {
if (null == imvBack) {
imvBack = (ImageView) view.findViewById(R.id.imvBack);
imvBack.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(postHC){
reverseAnimateItem();
}else{
reverseItemLegacy();
}
}
});
}
return imvBack;
}
/**
* #return the txvTendance
*/
public final TextView getTxvUpdating() {
if (null == txvUpdating) {
txvUpdating = (TextView) view.findViewById(R.id.txv_updating);
}
return txvUpdating;
}
/**
* #return the txvCurrent
*/
public final TextView getTxvCurrent() {
if (null == txvCurrent) {
txvCurrent = (TextView) view.findViewById(R.id.txv_current);
txvCurrent.setText("Toto");
}
return txvCurrent;
}
/**
* #return the txvMin
*/
public final TextView getTxvMin() {
if (null == txvMin) {
txvMin = (TextView) view.findViewById(R.id.txv_min);
}
return txvMin;
}
/**
* #return the txvMax
*/
public final TextView getTxvMax() {
if (null == txvMax) {
txvMax = (TextView) view.findViewById(R.id.txv_max);
}
return txvMax;
}
/**
* #return the linRoot
*/
public final LinearLayout getLinRoot() {
if (null == linRoot) {
linRoot = (LinearLayout) view.findViewById(R.id.lay_item);
}
return linRoot;
}
/**************************************************
* Animation tricks
* All Version
* The UpdateAnimation
* **************************************************
*/
/**
* Launch the Update Animation
*/
public void animateUpdate() {
if (updateAnimation==null) {
updateAnimation=AnimationUtils.loadAnimation(getContext(), R.anim.anim_item_updated);
updateAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
getTxvUpdating().setVisibility(View.VISIBLE);}
#Override
public void onAnimationEnd(Animation animation) {
getTxvUpdating().setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation animation) {}
});
}
if (isFlipped.get(currentPosition)) {
getImvBack().startAnimation(updateAnimation);
} else {
//run it
getLinRoot().startAnimation(updateAnimation);
}
}
/**
* Launch the Update Animation
*/
public void launchUpdateAnimation(int ndscCallsNumber){
if(dataTimeStamp!=ndscCallsNumber) {
//it means an already runnable is associated with this item
//we need to remove it (else it gonna run the animation twice
//and it's strange for the user)
handlerForAnimation.removeCallbacks(animationRunnable);
//then launched it in few seconds
handlerForAnimation.postDelayed(animationRunnable, 300 * currentPosition);
Log.e("tag", "launchUpdateAnimation in " + 300 * currentPosition + " for item " + currentPosition);
dataTimeStamp=ndscCallsNumber;
}
}
/**************************************************
* Animation tricks
* preHoneyComb : 4 Gingerbread in fact
* **************************************************
*/
private void flipItemLegacy(){
if(animInLegacy==null){
animInLegacy= AnimationUtils.loadAnimation(getContext(),R.anim.forecast_item_in);
}
if(animOutLegacy==null){
animOutLegacy= AnimationUtils.loadAnimation(getContext(),R.anim.forecast_item_out);
}
animOutLegacy.setAnimationListener(new Animation.AnimationListener() {
public void onAnimationStart(Animation animation) {}
public void onAnimationEnd(Animation animation) {
Log.e("ForecastArrayAdapter","flipItemLegacy onAnimationEnd called ");
getImvBack().setVisibility(View.VISIBLE);
getImvBack().startAnimation(animInLegacy);
getLinRoot().setVisibility(View.GONE);
}
public void onAnimationRepeat(Animation animation) {}
});
getLinRoot().startAnimation(animOutLegacy);
isFlipped.put(currentPosition,true);
}
private void reverseItemLegacy(){
if(animInLegacy==null){
animInLegacy= AnimationUtils.loadAnimation(getContext(),R.anim.forecast_item_in);
}
if(animOutLegacy==null){
animOutLegacy= AnimationUtils.loadAnimation(getContext(),R.anim.forecast_item_out);
}
animInLegacy.setAnimationListener(new Animation.AnimationListener() {
public void onAnimationStart(Animation animation) {}
public void onAnimationEnd(Animation animation) {
getLinRoot().setVisibility(View.VISIBLE);
getLinRoot().startAnimation(animInLegacy);
getImvBack().setVisibility(View.GONE);
}
public void onAnimationRepeat(Animation animation) {}
});
getImvBack().startAnimation(animOutLegacy);
isFlipped.put(currentPosition,false);
}
/**************************************************
* Animation tricks
* postHoneyComb
* **************************************************
*/
#SuppressLint("NewApi")
private void animateItem(){
initialiseFlipAnimator();
setFlip.start();
isFlipped.put(currentPosition,true);
}
#SuppressLint("NewApi")
private void reverseAnimateItem(){
initialiseReverseFlipAnimator();
setReverse.start();
isFlipped.put(currentPosition,false);
}
#SuppressLint("NewApi")
private void initialiseReverseFlipAnimator() {
if(reverseFlipAnimatorIn==null){
reverseFlipAnimatorIn= AnimatorInflater.loadAnimator(getContext(), R.animator.flip_in);
reverseFlipAnimatorIn.addListener(new SimpleAnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
getLinRoot().setVisibility(View.VISIBLE);
}
#Override
public void onAnimationEnd(Animator animation) {
getImvBack().setVisibility(View.GONE);
}
});
reverseFlipAnimatorIn.setTarget(getLinRoot());
reverseFlipAnimatorOut= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_out);
reverseFlipAnimatorOut.setTarget(imvBack);
setReverse=new AnimatorSet();
setReverse.playTogether(reverseFlipAnimatorIn,reverseFlipAnimatorOut);
}
}
#SuppressLint("NewApi")
private void initialiseFlipAnimator(){
Log.e("ForecastArrayAdapter","initialiseFlipAnimator");
if(flipAnimatorIn==null){
flipAnimatorIn= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_in);
flipAnimatorIn.setTarget(getImvBack());
flipAnimatorOut= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_out);
flipAnimatorOut.setTarget(getLinRoot());
flipAnimatorIn.addListener(new SimpleAnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
Log.e("tag","anim onAnimationStart");
getImvBack().setVisibility(View.VISIBLE);
}
#Override
public void onAnimationEnd(Animator animation) {
Log.e("tag","anim onAnimationEnd");
getLinRoot().setVisibility(View.GONE);
}
});
setFlip=new AnimatorSet();
setFlip.playTogether(flipAnimatorIn, flipAnimatorOut);
}
}
}
#SuppressLint("NewApi")
public abstract class SimpleAnimatorListener implements Animator.AnimatorListener {
/**
* <p>Notifies the start of the animation.</p>
*
* #param animation The started animation.
*/
public abstract void onAnimationStart(Animator animation);
/**
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* #param animation The animation which reached its end.
*/
public abstract void onAnimationEnd(Animator animation) ;
/**
* <p>Notifies the cancellation of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* #param animation The animation which was canceled.
*/
#Override
public void onAnimationCancel(Animator animation) {
onAnimationEnd(animation);
}
/**
* <p>Notifies the repetition of the animation.</p>
*
* #param animation The animation which was repeated.
*/
#Override
public void onAnimationRepeat(Animator animation) {
onAnimationStart(animation);
}
}
}
Ok, I dive into that bug and I still don't understand (I had a lot of logs) so
my problem is here, the view tells me, it's Visible,
but it's not displayed
A simple way to reproduce the problem, go in landscape mode, flip the first two items, scroll to the end of the list.
Thanks a billion to those who will try to answer.
Mathias
Youpi, I found !!
Summary
Ok, for me, it's a bug or an over optimised behavior.
So when my view is flipped I hide the front to show the back, and unflipped, I hide the back to show the front.
The problem is if I hide the front in the view v1 associated with the item n1. I scroll. Then this view is reused as a convertView in the getView method with the items n2. But for the item n2, we display the front...
And the bug occurs: The front view has been almost deleted/garbage collected or what ever but is not here anymore. It answers to functions' calls but its inner state is wrong. So It tells you, I am visible but it's not, it's a ghost.
My comprehension:
What I think (it's an hypothesis)
So the point here is an over optimisation of the ListView:
When a view went in the pool of convert views, the system destroys the ressources that are in the "Visibility Gone" state. I mean the Views of the ViewGroup root that have Visibility=Gone.
My comprehension is Wrong
So Romain Guy told me, "ListView does not destroy GONE views. Esp. since it doesn't look at children of recycled views. And if you can call a method on a View it has clearly not been GC'd. It could be a bug in the UI Toolkit drawing or in the adapter."
.... Ok, I will continue do dive in my problem to understand so.
The solution:
So the solution is obvious, I need two convert views pools, one with front visible by default, the other with back visible by default. So I create two layouts, one with the front visible, the other with the back visible, i use the getViewTypeCount() and the getItemViewType(int position) methods, and 3 minutes latter it was working.
The conclusion
As conclusion, when, in ListView, you hide and show elements in your items, you need to define as much as configurations as convertViews pools.
:( I am sad to understand that and if the bug was not there to prove it, I will never believe that.
The project
You can find the full project here:
https://github.com/MathiasSeguy-Android2EE/ForecastYahooRest and you have to check out the branch "flipcard" is updated.
The Code:
package com.android2ee.formation.restservice.sax.forecastyahoo.view.forecast.arrayadpater;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorSet;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.text.format.DateFormat;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android2ee.formation.restservice.sax.forecastyahoo.R;
import com.android2ee.formation.restservice.sax.forecastyahoo.transverse.model.YahooForcast;
import java.util.List;
/**
* #author Mathias Seguy (Android2EE)
* #goals This class aims to display the forecast in the listView
*/
public class ForecastArrayAdapter extends ArrayAdapter<YahooForcast> {
/**
* Handler to launch the animation runnable
*/
Handler handlerForAnimation;
/**
* To know when the item is flipped or not
* When flipped it show us its back side else its front side
*/
SparseBooleanArray isFlipped;
/**
* To detect the first launch
*/
int notifyDataSetChangedCallsNumber = 0;
/**
* The layout inflater
*/
LayoutInflater inflater;
/**
* The Context
*/
Context ctx;
/**
* To know if the device is postJellyBean or not
*/
boolean postJB;
/**
* To know if the device is postHoneyComb or not
*/
boolean postHC;
/**
* Drawable used for the backside of the item
*/
Drawable[] drawableBackground;
int[] drawableRes;
/**
* #param context
* #param forecast
*/
public ForecastArrayAdapter(Context context, List<YahooForcast> forecast) {
super(context, R.layout.item_forecast, forecast);
inflater = LayoutInflater.from(context);
ctx = context;
postJB = context.getResources().getBoolean(R.bool.postJB);
postHC = context.getResources().getBoolean(R.bool.postHC);
//instantiate the handler
handlerForAnimation = new Handler();
isFlipped = new SparseBooleanArray(5);
drawableRes = new int[5];
drawableRes[0] = R.drawable.back1;
drawableRes[1] = R.drawable.back2;
drawableRes[2] = R.drawable.back3;
drawableRes[3] = R.drawable.back4;
drawableRes[4] = R.drawable.back5;
drawableBackground = new Drawable[5];
drawableBackground[0] = context.getResources().getDrawable(R.drawable.back1);
drawableBackground[1] = context.getResources().getDrawable(R.drawable.back2);
drawableBackground[2] = context.getResources().getDrawable(R.drawable.back3);
drawableBackground[3] = context.getResources().getDrawable(R.drawable.back4);
drawableBackground[4] = context.getResources().getDrawable(R.drawable.back5);
}
/**
* Private static better than temp
*/
private static View rowView;
/**
* Private static better than temp
*/
private static YahooForcast forcast;
/**
* Private static better than temp
*/
private static ViewHolder viewHolder;
/*
* (non-Javadoc)
*
* #see android.widget.ArrayAdapter#getView(int, android.view.View, android.view.ViewGroup)
*/
#SuppressLint("NewApi")
#Override
public View getView(int position, View convertView, ViewGroup parent) {
rowView = convertView;
forcast = getItem(position);
if (rowView == null) {
if(getItemViewType(position)==0){
//then used the layout of flipped view
// always add the layout, the parent and false
rowView = inflater.inflate(R.layout.item_forecast, null, false);
}else{
//then used the layout for not flipped view
// always add the layout, the parent and false
rowView = inflater.inflate(R.layout.item_forecast, null, false);
}
ViewHolder vh = new ViewHolder(rowView, position);
rowView.setTag(vh);
}
viewHolder = (ViewHolder) rowView.getTag();
//used for animation
viewHolder.setCurrentPosition(position);
if (postJB) {
viewHolder.getImvIcon().setBackground(forcast.getImage());
viewHolder.getImvBack().setBackground(drawableBackground[position % 5]);
} else {
viewHolder.getImvIcon().setBackgroundDrawable(forcast.getImage());
//if you don't use setBackgroundResource nothing happens on Gingerbread (deep sadness sometimes)
viewHolder.getImvBack().setBackgroundResource(drawableRes[position % 5]);
}
if (forcast.getDate() != null) {
viewHolder.getTxvDate().setText(DateFormat.format("E dd MMM", forcast.getDate()));
} else {
viewHolder.getTxvDate().setText("unknown");
}
viewHolder.getTxvTendance().setText(forcast.getTendance());
if (forcast.getTempMax() != -1000) {
viewHolder.getTxvMax().setVisibility(View.VISIBLE);
viewHolder.getTxvMin().setVisibility(View.VISIBLE);
viewHolder.getTxvMax().setText(ctx.getString(R.string.max, forcast.getTempMax()));
viewHolder.getTxvMin().setText(ctx.getString(R.string.min, forcast.getTempMin()));
} else {
viewHolder.getTxvMax().setVisibility(View.GONE);
viewHolder.getTxvMin().setVisibility(View.GONE);
}
if (forcast.getTemp() != -1000) {
viewHolder.getTxvCurrent().setVisibility(View.VISIBLE);
viewHolder.getTxvCurrent().setText(ctx.getString(R.string.temp, forcast.getTemp()));
} else {
viewHolder.getTxvCurrent().setVisibility(View.GONE);
}
// launch animations to show the update to the user (not the first time but only when refreshing)
//because the first time is not an update, it's just loading data from db
if (notifyDataSetChangedCallsNumber >= 2) {
viewHolder.launchUpdateAnimation(notifyDataSetChangedCallsNumber);
}
//and finally manage the visibility of the side : front or back side is visible
return rowView;
}
/* (non-Javadoc)
* #see android.widget.ArrayAdapter#notifyDataSetChanged()
*/
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
notifyDataSetChangedCallsNumber++;
}
/***********************************************************
* Trying to fix the bug of the visible view not displayed
* by managing 2 pools of views
**********************************************************/
#Override
public int getViewTypeCount() {
//Two pools: the one for flipped views, the other not flipped views
return 2;
}
#Override
public int getItemViewType(int position) {
//If the View is flipped then pick in the pool 0
//else pick in the pool 1
return isFlipped.get(position)?0:1;
}
/**************************************************
* Flipping Animation tricks
* **************************************************
*/
/**
* If the element has been flipped, flip it else set it has not flipped
*
* #param position
*/
private void manageSideVisibility(int position) {
if (isFlipped.get(position)) {
Log.e("ForecastArrayAdapter","ImvBack Visible"+position);
//the backside is visible
viewHolder.getImvBack().setVisibility(View.VISIBLE);
viewHolder.getLinRoot().setVisibility(View.GONE);
viewHolder.getImvBack().invalidate();
} else {
Log.e("ForecastArrayAdapter","ImvBack GONE"+position);
//the ffront is visible
viewHolder.getImvBack().setVisibility(View.GONE);
viewHolder.getLinRoot().setVisibility(View.VISIBLE);
viewHolder.getLinRoot().invalidate();
}
printView("ImvBack",viewHolder.getImvBack(),position);
printView("LinRoot",viewHolder.getLinRoot(),position);
}
public void printView(String viewName,View view,int position){
Log.e("ForecastArrayAdapter","("+viewName+","+position+") getWidth()="+view.getWidth());
Log.e("ForecastArrayAdapter","("+viewName+","+position+") getHeight()="+view.getHeight());
Log.e("ForecastArrayAdapter", "(" + viewName + "," + position + ") getHeight()=" + view.getBackground());
Log.e("ForecastArrayAdapter", "(" + viewName + "," + position + ") getVisibility()=" + getVisibility(view));
}
public String getVisibility(View view){
switch (view.getVisibility()){
case View.GONE:
return "GONE";
case View.VISIBLE:
return "VISIBLE";
case View.INVISIBLE:
return "INVISIBLE";
}
return "Unknow";
}
/******************************************************************************************/
/** Runnable for animation **************************************************************************/
/**
* **************************************************************************************
*/
public class MyRunnable implements Runnable {
/**
* The viewHolder that contains the view to animate
*/
private ViewHolder vh;
public MyRunnable(ViewHolder vh) {
this.vh = vh;
}
public void run() {
vh.animateUpdate();
}
}
public void printisFlipp(String methodName) {
for (int i = 0;i < 5; i++){
Log.e("ForecastArrayAdapter", "in("+methodName+") isFlipped[" + i + "]=" + isFlipped.get(i));
}
}
/******************************************************************************************/
/** The ViewHolder pattern **************************************************************************/
/******************************************************************************************/
private class ViewHolder {
View view;
LinearLayout linRoot;
TextView txvDate;
TextView txvTendance;
ImageView imvIcon;
TextView txvCurrent;
TextView txvMin;
TextView txvMax;
TextView txvUpdating;
//For Update animation
Animation updateAnimation;
MyRunnable animationRunnable;
int dataTimeStamp=0;
//For animation
ImageView imvBack;
int currentPosition;
public int getCurrentPosition() {
return currentPosition;
}
public void setCurrentPosition(int currentPosition) {
this.currentPosition = currentPosition;
}
//PostHoneyComb
Animator flipAnimatorIn;
Animator flipAnimatorOut;
Animator reverseFlipAnimatorIn;
Animator reverseFlipAnimatorOut;
AnimatorSet setFlip;
AnimatorSet setReverse;
//PreHoneyComb
Animation animInLegacy;
Animation animOutLegacy;
int id;
/**
* #param rowview
*/
private ViewHolder(View rowview,int position) {
super();
this.view = rowview;
animationRunnable=new MyRunnable(this);
id=position;
}
/**
* #return the txvDate
*/
public final TextView getTxvDate() {
if (null == txvDate) {
txvDate = (TextView) view.findViewById(R.id.date);
}
return txvDate;
}
/**
* #return the txvTendance
*/
public final TextView getTxvTendance() {
if (null == txvTendance) {
txvTendance = (TextView) view.findViewById(R.id.txv_tendance);
}
return txvTendance;
}
/**
* #return the imvIcon
*/
public final ImageView getImvIcon() {
if (null == imvIcon) {
imvIcon = (ImageView) view.findViewById(R.id.icon);
imvIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(postHC){
animateItem();
}else{
flipItemLegacy();
}
}
});
}
return imvIcon;
}
/**
* #return the imvBack
*/
public final ImageView getImvBack() {
if (null == imvBack) {
imvBack = (ImageView) view.findViewById(R.id.imvBack);
imvBack.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(postHC){
reverseAnimateItem();
}else{
reverseItemLegacy();
}
}
});
}
return imvBack;
}
/**
* #return the txvTendance
*/
public final TextView getTxvUpdating() {
if (null == txvUpdating) {
txvUpdating = (TextView) view.findViewById(R.id.txv_updating);
}
return txvUpdating;
}
/**
* #return the txvCurrent
*/
public final TextView getTxvCurrent() {
if (null == txvCurrent) {
txvCurrent = (TextView) view.findViewById(R.id.txv_current);
txvCurrent.setText("Toto");
}
return txvCurrent;
}
/**
* #return the txvMin
*/
public final TextView getTxvMin() {
if (null == txvMin) {
txvMin = (TextView) view.findViewById(R.id.txv_min);
}
return txvMin;
}
/**
* #return the txvMax
*/
public final TextView getTxvMax() {
if (null == txvMax) {
txvMax = (TextView) view.findViewById(R.id.txv_max);
}
return txvMax;
}
/**
* #return the linRoot
*/
public final LinearLayout getLinRoot() {
if (null == linRoot) {
linRoot = (LinearLayout) view.findViewById(R.id.lay_item);
}
return linRoot;
}
/**************************************************
* Animation tricks
* All Version
* The UpdateAnimation
* **************************************************
*/
/**
* Launch the Update Animation
*/
public void animateUpdate() {
if (updateAnimation==null) {
updateAnimation=AnimationUtils.loadAnimation(getContext(), R.anim.anim_item_updated);
updateAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
getTxvUpdating().setVisibility(View.VISIBLE);}
#Override
public void onAnimationEnd(Animation animation) {
getTxvUpdating().setVisibility(View.GONE);
}
#Override
public void onAnimationRepeat(Animation animation) {}
});
}
if (isFlipped.get(currentPosition)) {
getImvBack().startAnimation(updateAnimation);
} else {
//run it
getLinRoot().startAnimation(updateAnimation);
}
}
/**
* Launch the Update Animation
*/
public void launchUpdateAnimation(int ndscCallsNumber){
if(dataTimeStamp!=ndscCallsNumber) {
//it means an already runnable is associated with this item
//we need to remove it (else it gonna run the animation twice
//and it's strange for the user)
handlerForAnimation.removeCallbacks(animationRunnable);
//then launched it in few seconds
handlerForAnimation.postDelayed(animationRunnable, 300 * currentPosition);
dataTimeStamp=ndscCallsNumber;
}
}
/**************************************************
* Animation tricks
* preHoneyComb : 4 Gingerbread in fact
* **************************************************
*/
private void flipItemLegacy(){
initLegacyAnimation();
getLinRoot().startAnimation(animOutLegacy);
isFlipped.put(getCurrentPosition(), true);
}
private void reverseItemLegacy(){
initLegacyAnimation();
getImvBack().startAnimation(animInLegacy);
isFlipped.put(getCurrentPosition(),false);
}
private void initLegacyAnimation() {
if(animInLegacy==null){
animInLegacy= AnimationUtils.loadAnimation(getContext(), R.anim.forecast_item_in);
animInLegacy.setAnimationListener(new Animation.AnimationListener() {
public void onAnimationStart(Animation animation) {}
public void onAnimationEnd(Animation animation) {
getLinRoot().setVisibility(View.VISIBLE);
getImvBack().setVisibility(View.GONE);
}
public void onAnimationRepeat(Animation animation) {}
});
}
if(animOutLegacy==null){
animOutLegacy= AnimationUtils.loadAnimation(getContext(),R.anim.forecast_item_out);
animOutLegacy.setAnimationListener(new Animation.AnimationListener() {
public void onAnimationStart(Animation animation) {
}
public void onAnimationEnd(Animation animation) {
getImvBack().setVisibility(View.VISIBLE);
getLinRoot().setVisibility(View.GONE);
}
public void onAnimationRepeat(Animation animation) {
}
});
}
}
/**************************************************
* Animation tricks
* postHoneyComb
* **************************************************
*/
#SuppressLint("NewApi")
private void animateItem(){
initialiseFlipAnimator();
setFlip.start();
isFlipped.put(getCurrentPosition(), true);
printisFlipp("animateItem");
}
#SuppressLint("NewApi")
private void reverseAnimateItem(){
initialiseReverseFlipAnimator();
setReverse.start();
isFlipped.put(getCurrentPosition(), false);
printisFlipp("animateItem");
}
#SuppressLint("NewApi")
private void initialiseReverseFlipAnimator() {
if(reverseFlipAnimatorIn==null){
reverseFlipAnimatorIn= AnimatorInflater.loadAnimator(getContext(), R.animator.flip_in);
reverseFlipAnimatorIn.addListener(new SimpleAnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
getLinRoot().setVisibility(View.VISIBLE);
}
#Override
public void onAnimationEnd(Animator animation) {
getImvBack().setVisibility(View.GONE);
}
});
reverseFlipAnimatorIn.setTarget(getLinRoot());
reverseFlipAnimatorOut= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_out);
reverseFlipAnimatorOut.setTarget(imvBack);
setReverse=new AnimatorSet();
setReverse.playTogether(reverseFlipAnimatorIn,reverseFlipAnimatorOut);
}
}
#SuppressLint("NewApi")
private void initialiseFlipAnimator(){
if(flipAnimatorIn==null){
flipAnimatorIn= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_in);
flipAnimatorIn.setTarget(getImvBack());
flipAnimatorOut= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_out);
flipAnimatorOut.setTarget(getLinRoot());
flipAnimatorIn.addListener(new SimpleAnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
getImvBack().setVisibility(View.VISIBLE);
}
#Override
public void onAnimationEnd(Animator animation) {
getLinRoot().setVisibility(View.GONE);
}
});
setFlip=new AnimatorSet();
setFlip.playTogether(flipAnimatorIn, flipAnimatorOut);
}
}
}
#SuppressLint("NewApi")
public abstract class SimpleAnimatorListener implements Animator.AnimatorListener {
/**
* <p>Notifies the start of the animation.</p>
*
* #param animation The started animation.
*/
public abstract void onAnimationStart(Animator animation);
/**
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* #param animation The animation which reached its end.
*/
public abstract void onAnimationEnd(Animator animation) ;
/**
* <p>Notifies the cancellation of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* #param animation The animation which was canceled.
*/
#Override
public void onAnimationCancel(Animator animation) {
onAnimationEnd(animation);
}
/**
* <p>Notifies the repetition of the animation.</p>
*
* #param animation The animation which was repeated.
*/
#Override
public void onAnimationRepeat(Animator animation) {
onAnimationStart(animation);
}
}
}
I am trying to implement this tutorial, for handling Configuration changes while running background tasks. Everything works fine, and the app does not crash after a configuration change. In the tutorial, a progress bar is used to display progress. But in my own implementation i want to use a Progress Dialog.
I have used progress Dialog's lots of times, so calling it and getting to appear is not the problem. My problem is that unlike the progress Bar, the progress dialog gets dismissed on configuration change. Just like that.
Here is my code:
My MainActivity:
private TaskFragment mTaskFragment;
private ProgressDialog mProgressDialog;
private TextView mPercent;
private Button mButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate(Bundle)");
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Initialize views
mButton = (Button) findViewById(R.id.task_button);
mButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (mTaskFragment.isRunning()) {
mTaskFragment.cancel();
} else {
mTaskFragment.start();
}
}
});
mProgressBar = new ProgressDialog(this);
FragmentManager fm = getSupportFragmentManager();
mTaskFragment = (TaskFragment) fm.findFragmentByTag("task");
// If the Fragment is non-null, then it is currently being
// retained across a configuration change.
if (mTaskFragment == null) {
mTaskFragment = new TaskFragment();
fm.beginTransaction().add(mTaskFragment, "task").commit();
}
if (mTaskFragment.isRunning()) {
mButton.setText(getString(R.string.cancel));
} else {
mButton.setText(getString(R.string.start));
}
}
/****************************/
/***** CALLBACK METHODS *****/
/****************************/
#Override
public void onPreExecute() {
Log.i(TAG, "onPreExecute()");
mProgressBar.setTitle("Wacky");
mProgressBar.setMessage("wack");
mProgressBar.setIndeterminate(true);
mProgressBar.show();
mButton.setText(getString(R.string.cancel));
mButton.setText(getString(R.string.cancel));
Toast.makeText(this, R.string.task_started_msg, Toast.LENGTH_SHORT).show();
}
#Override
public void onProgressUpdate(int percent) {
//Log.i(TAG, "onProgressUpdate(" + percent + "%)");
}
#Override
public void onCancelled() {
Log.i(TAG, "onCancelled()");
mButton.setText(getString(R.string.start));
Toast.makeText(this, R.string.task_cancelled_msg, Toast.LENGTH_SHORT).show();
}
#Override
public void onPostExecute() {
Log.i(TAG, "onPostExecute()");
mButton.setText(getString(R.string.start));
Toast.makeText(this, R.string.task_complete_msg, Toast.LENGTH_SHORT).show();
}
My headless Fragment that holds my asyncTask
/**
* This Fragment manages a single background task and retains itself across
* configuration changes.
*/
public class TaskFragment extends Fragment {
public static final String TAG = TaskFragment.class.getSimpleName();
/**
* Callback interface through which the fragment can report the task's
* progress and results back to the Activity.
*/
public static interface TaskCallbacks {
public void onPreExecute();
public void onProgressUpdate(int percent);
public void onCancelled();
public void onPostExecute();
}
public TaskCallbacks mCallbacks;
public DummyTask mTask;
public boolean mRunning;
/**
* Android passes us a reference to the newly created Activity by calling this
* method after each configuration change.
*/
#Override
public void onAttach(Activity activity) {
Log.i(TAG, "onAttach(Activity)");
super.onAttach(activity);
if (!(activity instanceof TaskCallbacks)) {
throw new IllegalStateException("Activity must implement the TaskCallbacks interface.");
}
// Hold a reference to the parent Activity so we can report back the task's
// current progress and results.
mCallbacks = (TaskCallbacks) activity;
}
/**
* This method is called only once when the Fragment is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate(Bundle)");
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
/**
* This method is <em>not</em> called when the Fragment is being retained
* across Activity instances.
*/
#Override
public void onDestroy() {
Log.i(TAG, "onDestroy()");
super.onDestroy();
cancel();
}
/*****************************/
/***** TASK FRAGMENT API *****/
/*****************************/
/**
* Start the background task.
*/
public void start() {
if (!mRunning) {
mTask = new DummyTask(this, mCallbacks);
mTask.execute();
mRunning = true;
}
}
/**
* Cancel the background task.
*/
public void cancel() {
if (mRunning) {
mTask.cancel(false);
mTask = null;
mRunning = false;
}
}
/**
* Returns the current state of the background task.
*/
public boolean isRunning() {
return mRunning;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.i(TAG, "onActivityCreated(Bundle)");
super.onActivityCreated(savedInstanceState);
}
}
My Background Task (in a seperate outer class)
/**
* A dummy task that performs some (dumb) background work and proxies progress
* updates and results back to the Activity.
*/
public class DummyTask extends AsyncTask<Void, Integer, Void> {
private TaskFragment fragment;
private TaskCallbacks callbacks;
private ProgressDialog mProgressBar;
MainActivity activity;
public DummyTask(TaskFragment taskFragment, TaskCallbacks mCallbacks) {
// TODO Auto-generated constructor stub
this.fragment = taskFragment;
this.callbacks = mCallbacks;
}
#Override
protected void onPreExecute() {
// Proxy the call to the Activity
fragment.mCallbacks.onPreExecute();
fragment.mRunning = true;
}
#Override
protected Void doInBackground(Void... ignore) {
for (int i = 0; !isCancelled() && i < 100; i++) {
//Log.i(TAG, "publishProgress(" + i + "%)");
SystemClock.sleep(100);
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... percent) {
// Proxy the call to the Activity
fragment.mCallbacks.onProgressUpdate(percent[0]);
}
#Override
protected void onCancelled() {
// Proxy the call to the Activity
fragment.mCallbacks.onCancelled();
fragment.mRunning = false;
}
#Override
protected void onPostExecute(Void ignore) {
// Proxy the call to the Activity
fragment.mCallbacks.onPostExecute();
fragment.mRunning = false;
}
}
I am thinking it is the context which i am passing the progress dialog in the onCreate method of my Main Activity. Thanks for your help.
First, Activity is subclass of Context, you should know this already. Second, if Activity is destroyed, it is don't have a window anymore. Third, Dialog uses Context (read Activity) not because it wants so, but because it uses window associated with Activity to display itself.
It should be perfectly understandable, why after destroying activity during configuration change, Dialog no longer visible to you.
Method of preserving objects you using is good, but it can't preserve anything that would be destroyed during configuration change, such as any object that related to non-application Context, all this objects you need create manually every time Context changes.
You should use onSaveInstanceState(Bundle) to store state of ProgressDialog (shown or not) and show it again in your onCreate(Bunde) using value stored in Bundle.
Just an example
#Override
protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putBoolean("SHOW_DIALOG", mProgressBar.isShowing());
}
//...
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
if (savedInstanceState != null){
if (savedInstanceState.getBoolean("SHOW_DIALOG") && mTaskFragment.isRunning()){
mProgressBar.setTitle("Wacky");
mProgressBar.setMessage("wack");
mProgressBar.setIndeterminate(true);
mProgressBar.show();
}
}
//...
I have build a slide.
I have a ViewPage Activity that call a fragment every time that i swip to left or to right.
My question is:
I have a variable int that in the initial state have the value 6 (week_of_year). When i slide to right is call a new fragment and the variable is not increment as i intend. The variable is only increment in the the second slide.
For example: 6 -> 6 -> 7 -> 8 instead of 6 -> 7 -> 8 -> And i don't have ideia why this happens.
Here is my code:
ViewPage Activity
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_slide);
/**
* Instantiate a ViewPager and a PagerAdapter
*/
date = Calendar.getInstance();
mPager = (ViewPager)findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
mPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
if(itemOld < arg0){//right
auxFront = auxFront + 1;
oldStartWeek = oldStartWeek + auxFront;
auxFront=0;
}
else if(itemOld > arg0){//left
auxBack = auxBack - 1;
oldStartWeek = oldStartWeek + auxBack;
auxBack=0;
}
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
#Override
public void onBackPressed(){
if(mPager.getCurrentItem()==0){
super.onBackPressed();
}else{
mPager.setCurrentItem(mPager.getCurrentItem()-1);
}
}
/**
* A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in sequence
*/
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter{
public ScreenSlidePagerAdapter(FragmentManager fm){
super(fm);
}
#Override
public ScreenSlidePageFragment getItem(int position) {
/**
* Pass values from ViewPage activity to Fragment
*/
ScreenSlidePageFragment f = new ScreenSlidePageFragment();
Bundle args = new Bundle();
if(position == 0){
oldStartWeek = date.get(Calendar.WEEK_OF_YEAR);
args.putInt("START_WEEK", oldStartWeek);
f.setArguments(args);
return f;
}
else if(position != 0){
args.putInt("START_WEEK", oldStartWeek);
f.setArguments(args);
itemOld = mPager.getCurrentItem();
return f;
}
return f;
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
}
Fragment that is call in each slide--------------------------------------------
public class ScreenSlidePageFragment extends Fragment implements
AdapterView.OnItemClickListener {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Toast.makeText(getActivity(), "week....." +getArguments().getInt("START_WEEK"), Toast.LENGTH_LONG).show(); //show the correct value
headerWeek.setText(" Week Number: "+getArguments().getInt("START_WEEK",0)); //But is not change corretly in the text view ?????
Thanks for your help and time.
To see why this happens in your ViewPager, you can Log the onCreate, onPause, onResume, etc methods for each fragment. What happens is that the first two fragments are actually created about the same time. So the second fragment is created before it actually displays in a ViewPager, so by the time the user scrolls to the second fragment, there is often old data there.
My solution to this is a little hacky. Note that instead of displayDialog() you would do whatever you needed to update the view when the user is present.
#Override
public void onResume() {
super.onResume();
fragmentActive = true;
if (userVisible()){
displayDialog();
}
else{
recursiveWait();
}
}
private void recursiveWait(){
new WaitAndDoXTask(1000, new DoXListener() {
#Override
public void doX() {
Log.d(TAG, "doX");
if (userVisible()){
displayDialog();
}
else if (fragmentActive){
recursiveWait();
}
}
}).execute();
}
private boolean userVisible(){
boolean vis = getUserVisibleHint();
Log.d(TAG, "user visible = " + vis);
return vis;
}
#Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause");
fragmentActive = false;
}
Here is the WaitAndDoXTask:
public class WaitAndDoXTask extends AsyncTask<Void, Void, Void>{
DoXListener mListener;
private int mTimeToWait;
/** This class will wait for the set time then do what is put into the listener.
*
*
* Run like this:
* new WaitAndDoXTask(1000, new DoXListener() {
*
* #Override
* public void doX() {
* // TODO Auto-generated method stub
*
* }
* }).execute();
**/
public WaitAndDoXTask(int timeToWait, DoXListener listener){
super();
mTimeToWait = timeToWait;
mListener = listener;
}
public interface DoXListener{
public void doX();
}
#Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(mTimeToWait);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
if (mListener!=null){
mListener.doX();
}
}
}
Alternatively, if you could set up your Fragment with some Adapter and get notified via onDataSetChanged that might work better.