I’m using ksoap to call a web service.
When Application starts or when i scroll to end of page Application fetches data from Web Service correctly and it shows data as well as i want, But When I want to show Progress Dialog in a AsyncTask it doesn’t work correctly. It shows Progress Dialog after PostExecute in a blik of eyes. So, What's wrong in my code ? I can't understand where is my problem. ;-)
I have a web service class called ElvatraWebService:
Here is a Method witch is fetch latest post with Asynctasks
private String GetLastPublicPosts(int counter){
...
...
...
...
return resTxt; //Returns JSON String
}
public String getLatestPost(int counter, Activity a){
CallLatestPost task = new CallLatestPost(counter,a);
try {
strJSON = task.execute("getLatest").get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Log.i("sss", "--" + strJSON);
return strJSON;
}
private class CallLatestPost extends AsyncTask<String, Void, String> {
private int Counter = 0;
private ProgressDialog dialog;
private Activity activity;
private Context context;
public CallLatestPost (int c,Activity actt) {
super();
this.Counter = c;
activity = actt;
context = actt;
dialog = new ProgressDialog(this.context);
}
#Override
protected String doInBackground(String... params) {
ElvatraWebService elws = new ElvatraWebService();
String tmp = elws.GetLastPublicPosts(this.Counter);
//Log.i("strJSON",strJSON);
return tmp;
}
#Override
protected void onPostExecute(String result) {
//Set response
super.onPostExecute(result);
if (dialog.isShowing()) {
dialog.dismiss();
}
}
#Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(context);
dialog.setMessage("Connecting...");
//dialog.setIndeterminate(true);
dialog.setCancelable(true);
dialog.show();
Log.i("###Async", "=== " + "PreExec");
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
I have a Class called post. It will call getLatestPost from Elvatrawebservice.
Then it will convert JSON String to JsonArray with PostAdapter class. PostAdapter is a class extends BaseAdapter.
In Main Activity I call a local Method called LoadContent in onCreate method.
Here is MainActivity Class
public class MainActivity extends Activity {
String displayText="";
public TextView tv;
public ListView lv;
public ProgressBar pg;
int theCounter=20;
String[] strTAG = new String[] {"title","photo","date","desc","linkurl"};
//int[] intField = new int[] {R.id.title,R.id.imgPost, R.id.Date, R.id.desc, R.id.link};
ListAdapter sa;
List<HashMap<String,Object>> list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recentposts);
lv = (ListView) findViewById(R.id.list);
lv.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
final int lastItem = firstVisibleItem + visibleItemCount;
if(lastItem == totalItemCount) {
//load more data
// Load content of listview
LoadContent();
}
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public void onDestroy() {
super.onDestroy(); // Always call the superclass
// Stop method tracing that the activity started during onCreate()
android.os.Debug.stopMethodTracing();
}
public void LoadContent(){
lv = (ListView) findViewById(R.id.list);
Post p = new Post(theCounter);
theCounter+=20;
if (p.GetLatestPosts(lv,MainActivity.this))
{
//tv.setText("true");
}
}
}
You have
task.execute("getLatest").get();
You should not call get() as this blocks the ui thread waiting for the result making it asynctask asynchronous no more. Just use
task.execute("getLatest");
You can return result in doInBackground and update ui in onPostExecute.
Related
The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: 0, found: 3 Pager
id
I have this error in my Android project. Here I am using a view pager for that. I got images from webservices as byte code.
public class MainActivity extends AppCompatActivity {
private String TAG = MainActivity.class.getSimpleName();
private ProgressDialog pDialog;
ArrayList<String> alListImage;
private static ViewPager mPager;
private static int currentPage = 0;
private static int NUM_PAGES = 0;
private static final Integer[] IMAGES= {R.drawable.one,R.drawable.two,R.drawable.three,R.drawable.one,R.drawable.two,R.drawable.three,};
private ArrayList<String> ImagesArray = new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
alListImage=new ArrayList<String>();
new GetContacts().execute();
init();
}
private void init() {
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(new Adapter(MainActivity.this,alListImage));
// Auto start of viewpager
final Handler handler = new Handler();
final Runnable Update = new Runnable() {
public void run() {
if (currentPage == alListImage.size()) {
currentPage = 0;
}
mPager.setCurrentItem(currentPage++, true);
}
};
Timer swipeTimer = new Timer();
swipeTimer.schedule(new TimerTask() {
#Override
public void run() {
handler.post(Update);
}
}, 3000, 3000);
}
private class GetContacts extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
// Showing progress dialog
pDialog = new ProgressDialog(MainActivity.this);
pDialog.setMessage("Please wait...");
pDialog.setCancelable(false);
pDialog.show();
}
#Override
protected Void doInBackground(Void... arg0) {
HttpHandler sh = new HttpHandler();
String url = "http://webapirestro.appro.com.sg/api/Category/GetCategory?CompanyId=771416A2-56C6-46A5-8EB5-655E4361A22A";
//String url = "http://webapirestro.appro.com.sg/api/TvImage/Gettv_image?CompanyId=771416A2-56C6-46A5-8EB5-655E4361A22A";
// Making a request to URL and getting response
final String jsonStr = sh.makeServiceCall(url);
if (jsonStr != null) {
try {
JSONObject jsonObj = new JSONObject(jsonStr);
// Getting JSON Array node
JSONArray ListData = jsonObj.getJSONArray("ListData");
// Looping through All Contacts
for (int i = 0; i < ListData.length(); i++) {
JSONObject c = ListData.getJSONObject(i);
String sImageByte = c.getString("ImageByte");
String sCatname = c.getString("CategoryName");
if(sImageByte.equals(false)){
sImageByte = "ABC";
}
// Adding contact to contact list
alListImage.add(sImageByte);
}
}
catch (final JSONException e) {
Log.e(TAG, "JSON parsing error: " + e.getMessage());
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),
"Json parsing error: " + e.getMessage(),
Toast.LENGTH_LONG)
.show();
}
});
}
}
else {
Log.e(TAG, "Couldn't get json from server.");
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),
"Couldn't get json from server. Check LogCat for possible errors!",
Toast.LENGTH_LONG)
.show();
}
});
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// Dismiss the progress dialog
if (pDialog.isShowing())
pDialog.dismiss();
/**
* Updating parsed JSON data into ListView
* */
}
}
}
public class Adapter extends PagerAdapter {
private ArrayList<String> IMAGES;
private LayoutInflater inflater;
private Context context;
public Adapter(Context context,ArrayList<String> IMAGES) {
this.context = context;
this.IMAGES=IMAGES;
inflater = LayoutInflater.from(context);
notifyDataSetChanged();
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public int getCount() {
return IMAGES.size();
}
#Override
public Object instantiateItem(ViewGroup view, int position) {
View imageLayout = inflater.inflate(R.layout.single_pager, view, false);
assert imageLayout != null;
final ImageView imageView = (ImageView) imageLayout
.findViewById(R.id.image);
for(int i=0;i<IMAGES.size();i++) {
byte[] decode = Base64.decode(IMAGES.get(i), Base64.DEFAULT);
Bitmap bitmap = BitmapFactory.decodeByteArray(decode, 0, decode.length);
imageView.setImageBitmap(bitmap);
}
view.addView(imageLayout, 0);
return imageLayout;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view.equals(object);
}
#Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
#Override
public Parcelable saveState() {
return null;
}
}
GetContacts is an AsyncTask and in its doInBackground method, you put in this line:
alListImage.add(sImageByte); // This makes the adapter item number equal to 3
To avoid it, you could execute GetContacts after some time using postDelayed.
I Build an app that load data with Retrofit, into a recyclerview, my recyclerview load perfectly all data from JSON file, but when I try of an update with swipeRefreshLayout, and I intent load again the method loadFirstPage();, but simply add again the same data.
I search in google for any solution, but all my intents do not work in my code.
I intent used adapter.clear and load again, but not work fine.
The idea is if 1 item change from JSON file, update again the data in Recyclerview.
public class historial extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
User user = SharedPrefManager.getInstance(this).getUser();
private static final String TAG = "MainActivity";
PaginationAdapter adapter;
LinearLayoutManager linearLayoutManager;
private SwipeRefreshLayout swipeRefreshLayout;
RecyclerView recyclerView;
ProgressBar progressBar;
private static final int PAGE_START = 1;
private boolean isLoading = false;
private boolean isLastPage = false;
// limiting to 5 for this tutorial, since total pages in actual API is very large. Feel free to modify.
private int TOTAL_PAGES = 5;
private int currentPage = PAGE_START;
private NetworkInterface geosInterface;
int position;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.historial);
Toolbar toolbar = findViewById(R.id.toolbar);
//setting the title
toolbar.setTitle("elGEos School - Historial");
//placing toolbar in place of actionbar
setSupportActionBar(toolbar);
progressBar = findViewById(R.id.progressBar);
recyclerView = findViewById(R.id.recycler_view);
adapter = new PaginationAdapter(this);
swipeRefreshLayout = findViewById(R.id.swipe_refresh_layout);
swipeRefreshLayout.setOnRefreshListener(historial.this);
linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
recyclerView.setAdapter(adapter);
recyclerView.addOnScrollListener(new PaginationScrollListener(linearLayoutManager) {
#Override
protected void loadMoreItems() {
isLoading = true;
currentPage += 1;
// mocking network delay for API call
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadNextPage();
}
}, 1000);
}
#Override
public int getTotalPageCount() {
return TOTAL_PAGES;
}
#Override
public boolean isLastPage() {
return isLastPage;
}
#Override
public boolean isLoading() {
return isLoading;
}
});
//init serecyclerViewice and load data
geosInterface = NetworkApi.getClient().create(NetworkInterface.class);
loadFirstPage();
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
//CODE FOR UPDATE HERE ???
if (swipeRefreshLayout != null && swipeRefreshLayout.isRefreshing()) {
swipeRefreshLayout.setRefreshing(false); // This hides the spinner
}
}
});
}
//TOOL BAR Y MENU
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.menu3, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.aboutback:
onBackPressed();
//Toast.makeText(this, "You clicked about", Toast.LENGTH_SHORT).show();
break;
}
return true;
}
private void loadFirstPage() {
swipeRefreshLayout.setRefreshing(true);
Log.d(TAG, "loadFirstPage: ");
callTopMessage().enqueue(new Callback<TopMessage>() {
#Override
public void onResponse(Call<TopMessage> call, Response<TopMessage> response) {
// Got data. Send it to adapter
List<Result> results = fetchResults(response);
progressBar.setVisibility(View.GONE);
adapter.addAll(results);
if (currentPage <= TOTAL_PAGES) adapter.addLoadingFooter();
else isLastPage = true;
swipeRefreshLayout.setRefreshing(false);
}
#Override
public void onFailure(Call<TopMessage> call, Throwable t) {
t.printStackTrace();
// TODO: 08/11/16 handle failure
swipeRefreshLayout.setRefreshing(false);
}
});
}
/**
* #param response extracts List<{#link Result>} from response
* #return
*/
private List<Result> fetchResults(Response<TopMessage> response) {
TopMessage topMessage = response.body();
return topMessage.getResults();
}
private void loadNextPage() {
swipeRefreshLayout.setRefreshing(true);
Log.d(TAG, "loadNextPage: " + currentPage);
callTopMessage().enqueue(new Callback<TopMessage>() {
#Override
public void onResponse(Call<TopMessage> call, Response<TopMessage> response) {
adapter.removeLoadingFooter();
isLoading = false;
List<Result> results = fetchResults(response);
adapter.addAll(results);
if (currentPage != TOTAL_PAGES) adapter.addLoadingFooter();
else isLastPage = true;
swipeRefreshLayout.setRefreshing(false);
}
#Override
public void onFailure(Call<TopMessage> call, Throwable t) {
t.printStackTrace();
// TODO: 08/11/16 handle failure
swipeRefreshLayout.setRefreshing(false);
}
});
}
private Call<TopMessage> callTopMessage() {
return geosInterface.getTopMessage(
"Comfama",
"B",
"TTY651",
"1",
currentPage
);
}
#Override
public void onRefresh() {
}
}
Try this:
it is very simple to update and remove data in recycler view
REMOVE Data : There are 4 steps to remove an item from a RecyclerView adapter class like this:
list.remove(position);
recycler.removeViewAt(position);
adapter.notifyItemRemoved(position);
mAdapter.notifyItemRangeChanged(position, list.size());
make sure your data is set in adapter class like this after that you notifyData
private void setAdapter() {
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recycler_view.setLayoutManager(mLayoutManager);
mAdapter = new BankDetailsAdapter(getBankList,this);
recycler_view.setAdapter(mAdapter);
}
UPDATE data: There are 1 steps to update data like this:
adapter.notifyDataSetChanged();
it helps you
I have this method updatePosts() that needs to run inside of a thread. it looks like this
public void updatePosts(){
new Thread(){
public void run(){
posts.addAll(pholder.fetchPosts());
System.out.println("size of posts is " + posts.size());
// UI elements should be accessed only in
// the primary thread, so we must use the
// handler here.
}
}.start();
}
It is literally updating the List called "posts". I have this in my onCreate. The problem is, since it is running in a separate thread, it does not complete before my ImageAdapter needs to use the "posts" List.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
updatePosts();
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this, posts));
gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Toast.makeText(MainActivity.this, "" + position,
Toast.LENGTH_SHORT).show();
}
});
}
What options do I have to ensure that the updatePosts can finish before the ImageAdapter get created?
You should use an AsyncTask or any other mechanism that lets you offload a task to a background thread with a callback to the UI on completion.
private class BackgroundTask extends AsyncTask<Void, Void, List<Post>> {
#Override
protected List<Post> doInBackground(Void... params) {
// TODO
return posts;
}
#Override
protected void onPostExecute(List<Post> posts) {
// TODO: update UI
}
}
Another option is to use RxJava,
Observable
.fromCallable(new Callable<List<Post>>() {
#Override
public List<Post> call() throws Exception {
// TODO: get posts
return posts;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(posts -> {
// TODO: update UI
}, throwable -> {
// TODO
})
I think you need to use Handler.
private ImageAdapter mAdapter;
private MyHandler mHandler;
private List<YourData> posts;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
GridView gridview = (GridView) findViewById(R.id.gridview);
mAdapter = new ImageAdapter(this, posts);
gridview.setAdapter(mAdapter);
gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Toast.makeText(MainActivity.this, "" + position,
Toast.LENGTH_SHORT).show();
}
});
mHandler = new MyHandler(this);
updatePosts();
}
public void updatePosts(){
new Thread(){
public void run(){
posts.addAll(pholder.fetchPosts());
System.out.println("size of posts is " + posts.size());
if (mHandler != null) {
mHandler.sendMessage(mHandler.obtainMessage(1));
}
// UI elements should be accessed only in
// the primary thread, so we must use the
// handler here.
}
}.start();
}
private static class MyHandler extend Handler{
private WeakReference<Your Activity name> outer;
public MyHandler(Your Activity name activity){
outer = new WeakReference<>(activity);
}
#Override
public void handleMessage(Message msg){
switch(msg.what){
case 1:
if(outer.get()!=null){
outer.get().mAdapter.refresh(outer.get().posts);
}
break;
default:break;
}
}
}
And in your ImageAdapter,you need add a method:
public void refresh(List<YourData> dataSet){
this.dataSet = dataSet;
notifyDataSetChanged();
}
A search api is returning me some meta data along with a URL "eventURL". I am placing the data in the listview, each row containing some data and a unique URL.I want when the user taps on the row in the listview,that unique URL should open in a webview.I have created a WebViewActivity for it,I am having issue with implementing the onClickListener.
MainActivity
public class MainActivity extends Activity {
//private EditText m_search_text;
private EditText m_zip;
private ListView m_search_results;
private Button m_search_btn;
private JSONArray m_results;
private LayoutInflater m_inflater;
private InputMethodManager m_ctrl;
private Spinner m_radius;
private Spinner m_activity_selector;
public static int radius = 0;
public static String activities;
static final private int EXIT_ID = Menu.FIRST;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// m_search_text = (EditText) findViewById(R.id.search_text);
m_zip = (EditText) findViewById(R.id.zip);
m_search_btn = (Button) findViewById(R.id.search_button);
// m_search_results = (ListView) findViewById(R.id.lview);
m_search_btn .setOnClickListener(go_handler);
m_ctrl = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
m_inflater = LayoutInflater.from(this);
addListenerOnSpinnerItemSelection();
addListenerOnSpinner1ItemSelection();
m_search_results = (ListView)findViewById(R.id.lview);
}
public void addListenerOnSpinnerItemSelection() {
m_radius = (Spinner) findViewById(R.id.spinner);
m_radius.setOnItemSelectedListener(new CustomOnItemSelectedListener());
}
public void addListenerOnSpinner1ItemSelection(){
m_activity_selector = (Spinner) findViewById(R.id.spinner1);
m_activity_selector.setOnItemSelectedListener(new ActivitySelectedListener());
}
#Override
public boolean onCreateOptionsMenu(Menu menu){
super.onCreateOptionsMenu(menu);
menu.add(0, EXIT_ID, 0, R.string.exit);
return true;
}
#Override
public boolean onOptionsItemSelected (MenuItem item){
switch (item.getItemId()){
case EXIT_ID:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
OnClickListener go_handler = new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
m_ctrl.hideSoftInputFromWindow(m_zip.getWindowToken(), 0);
//String searchText = Uri.encode(m_search_text.getText().toString());
String zip = Uri.encode(m_zip.getText().toString());
new SearchTask().execute("?k=Fall+Classic" + "&m=meta:channel=" + activities + "&l="+ zip + "&r=" + radius);
// Show a toast showing the search text
Toast.makeText(getApplicationContext(),
getString(R.string.search_msg) + " " +
activities, Toast.LENGTH_LONG).show();
}
};
private class SearchTask extends AsyncTask<String, Integer, String>
{
ProgressDialog dialog;
#Override
protected void onPreExecute() {
dialog = ProgressDialog.show(MainActivity.this,"","Please Wait...");
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
try {
String result = ActiveHelper.download(params [0]);
return result;
} catch (ApiException e) {
e.printStackTrace();
Log.e("alatta", "Problem making search request");
}
return "";
}
#Override
protected void onPostExecute(String result) {
dialog.hide();
try {
JSONObject obj = new JSONObject(result);
m_results = obj.getJSONArray("_results");
if (m_results == null ||m_results.length() == 0)
{
Toast.makeText(getApplicationContext(),
"No Results found for " + activities,
Toast.LENGTH_LONG).show();
}
else
m_search_results.setAdapter(new JSONAdapter(getApplicationContext()));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
private class JSONAdapter extends BaseAdapter
{
public JSONAdapter(Context c){
}
public int getCount()
{
return m_results.length();
}
public Object getItem(int arg0){
return null;
}
public long getItemId(int pos){
return pos;
}
public View getView(int pos, View convertView, ViewGroup parent) {
View tv;
TextView t;
if (convertView == null)
tv = m_inflater.inflate (R.layout.item, parent, false);
else
tv = convertView;
try {
/* For each entry in the ListView, we need to populate
* its text and timestamp */
t = (TextView) tv.findViewById(R.id.text);
JSONObject obj = m_results.getJSONObject(pos);
t.setText (obj.getString("title").replaceAll("\\<.*?\\>", ""));
t = (TextView) tv.findViewById(R.id.created_at);
JSONObject meta = obj.getJSONObject("meta");
t.setText ("When:" + "\t"+meta.getString("startDate")+"\n"+"Location:" +"\t" +meta.getString("location")+"\n" +"More Info:"+"\t" +meta.getString("eventURL")+"\n");
} catch (JSONException e) {
Log.e("alatta", e.getMessage());
}
return tv;
}
}}
WebViewActivity
public class WebViewActivity extends MainActivity {
private WebView webView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.webview);
webView = (WebView) findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("url");
}}
Thanks.
You need to use OnItemClickListener:
MainActivity implements OnItemClickListener {
onCreate() {
m_search_results.setOnItemClickListener(this);
Make this method:
onItemClick(AdapterView<?> parent, View view, int position, long id) {
url = JSON.getHowever(position);
//get the intent, load the url, and launch the activity
}
You also need to handle the intent in your WebViewActivity, but that's beside the question
I have a TabActivity with 3 tabs. There is an async task that when run by clicking a menu item for refresh, retrieves updated data from the server. This data is stored in the controller and is accessed by all views, so that the model only needs to be loaded once.
My problem is that after the async activity runs and the model is updated, how do I signal all three tabs to update their content?
My activity
public class DashboardActivity extends TabActivity {
private ProfileModel profile;
private TabHost tabHost;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
profile = Controller.getProfile();
this.setContentView(R.layout.dashboard);
tabHost = getTabHost();
setupTab(new TextView(this), "Home", new Intent().setClass(DashboardActivity.this, HomeActivity.class));
setupTab(new TextView(this), "History", new Intent().setClass(DashboardActivity.this, PaymentsActivity.class));
setupTab(new TextView(this), "My Wallet", new Intent().setClass(DashboardActivity.this, MyWalletActivity.class));
tabHost.setCurrentTab(0);
ActionBar actionBar = (ActionBar)findViewById(R.id.actionbar);
actionBar.setTitle(profile.Name);
}
private void setupTab(final View view, final String tag, Intent content) {
View tabview = createTabView(tabHost.getContext(), tag);
TabSpec setContent = tabHost.newTabSpec(tag)
.setIndicator(tabview)
.setContent(content);
tabHost.addTab(setContent);
}
private static View createTabView(final Context context, final String text) {
View view = LayoutInflater.from(context).inflate(R.layout.tabs_bg, null);
TextView tv = (TextView) view.findViewById(R.id.tabsText);
tv.setText(text);
return view;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mainmenu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.menu_settings:
return true;
case R.id.menu_refresh:
new RefreshDashboardTask().execute();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private class RefreshDashboardTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute()
{
ActionBar actionBar = (ActionBar)findViewById(R.id.actionbar);
if(actionBar != null)
actionBar.setProgressBarVisibility(View.VISIBLE);
}
#Override
protected Void doInBackground(Void... params)
{
try {
profile = DataHelper.getProfile();
Controller.setProfile(profile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (HttpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ServerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result)
{
ActionBar actionBar = (ActionBar)findViewById(R.id.actionbar);
if(actionBar != null)
actionBar.setProgressBarVisibility(View.GONE);
}
}
}
EDIT For further elaboration, here is some more code.
My controller
public class Controller extends Application {
private static Controller instance;
private static DefaultHttpClient client;
private static ProfileModel profile;
public Controller() {
instance = this;
client = new DefaultHttpClient();
profile = null;
}
public static Context getContext() {
return instance;
}
public static DefaultHttpClient getHttpContext() {
return client;
}
public static ProfileModel getProfile() {
return profile;
}
public static void setProfile(ProfileModel profile) {
Controller.profile = profile;
}
#Override
public void onCreate()
{
super.onCreate();
}
}
And one of my activities that is inside the tab view. This one is the simplest one, as it is just a single list. The home view is 2 lists, separated by headers, and the wallet view is dynamically generated lists with headers, created from a collection within a collection.
public class PaymentsActivity extends Activity {
ProfileModel profile;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.payment_history);
profile = Controller.getProfile();
ListView itemList = (ListView)this.findViewById(R.id.payment_history_list);
itemList.setTextFilterEnabled(true);
itemList.clearChoices();
itemList.setAdapter(new ItemSummaryAdapter(PaymentsActivity.this, R.layout.list_item_payment, profile.Items));
//statementList.setOnItemClickListener(clickListener);
}
}
The goal here is that the refresh button updates the data in the controller. All my views inside the tabs are updated.
UPDATED:
If I were you I would the Observer pattern. First, add a set of listeners to your Controller class:
public class Controller extends Application {
private static Controller instance;
private static DefaultHttpClient client;
private static ProfileModel profile;
private static Set<ControllerUpdateListener> updateListeners = new HashSet<ControllerUpdateListener>();
//...
public static void addListener(ControllerUpdateListener listener)
{
updateListeners.add(listener);
}
public static interface ControllerUpdateListener {
void onControllerUpdate(ProfileModel model);
}
}
Then have your individual tab activities implement ControllerUpdateListener. Finally, add the trigger to the Controller class:
public static void setProfile(ProfileModel profile) {
Controller.profile = profile;
for(ControllerUpdateListener l : updateListeners) {
l.onControllerUpdate(profile);
}
}
Don't forget to register each activity as a Listener with the Controller:
public class PaymentsActivity extends Activity implements Controller.ControllerUpdateListener {
ProfileModel profile;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.payment_history);
Controller.addListener(this); // <-- Don't forget this...
profile = Controller.getProfile();
ListView itemList = (ListView)this.findViewById(R.id.payment_history_list);
itemList.setTextFilterEnabled(true);
itemList.clearChoices();
itemList.setAdapter(new ItemSummaryAdapter(PaymentsActivity.this, R.layout.list_item_payment, profile.Items));
//statementList.setOnItemClickListener(clickListener);
}
public void onControllerUpdate(ProfileModel model) {
//update these views...
}
}
Now each of your individual tab activities should trigger their notifyDataSetChanged() (or whatever other method triggers their update) in the onControllerUpdate(ProfileModel) method.
Are you calling notifyDataSetChanged() on your adapter after refreshing the content in your views? If its a ListView then just notifying the adapter should trigger a refresh. This in fact is the only way you should be dealing with rows inside ListView.