I am trying to implement a search function in an Android app that takes text from an AutoCompleteTextView, waits if there hasn't been made a change in the last 1.5 seconds and shows the search results. For this I use the TextWatcher class.
However, all my tries to implement this behavior ran into trouble with some functions only being allowed in the UI thread itself (via runOnUIThread) or the thread having Looper.prepare() called before.
In all attempts, the app crashes randomly when entering additional characters or deleting some, does not show any search results or reload to the start activity.
The following is a simplyfied recreation of my most recent try, where I use a Handler.
search.getResults is the long computation and matches is an array that has to be filled before delayableAdapterCreation creates the ArrayAdapterWithSpaceFilter.
public class SearchFragment extends Fragment {
public final static int MAX_NUMBER_OF_SUGGESTIONS = 4; // only show a max of 4 suggestions if more were found
public final static int SEARCH_CHAR_AMOUNT = 3; // only search if at least 3 characters were typed
public final static long SEARCH_DELAY_MILLIS = (long) 1500; // the time to wait for no text changes in milliseconds
private Search search;
private AutoCompleteTextView textView;
private String[] matches;
private String userStartRequest;
private Entry[] suggestions;
private FragmentListenter sListener;
private EntryFunctions ef = new EntryFunctions();
private Runnable delayableSearch;
private Runnable delayableAdapterCreation;
private Handler delayableSearchHandler;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
delayableSearchHandler = new Handler();
delayableSearch = new Runnable() {
#Override
public void run() {
userStartRequest = textView.getText().toString();
sListener.onFragmentFinish(userStartRequest);
suggestions = search.getResults(userStartRequest);
matches = ef.fillMatches(suggestions);
}
};
delayableAdapterCreation = new Runnable() {
#Override
public void run() {
ArrayAdapterWithSpaceFilter<String> adapter =
new ArrayAdapterWithSpaceFilter<String>(getActivity(),
android.R.layout.simple_list_item_1,
matches);
textView.setAdapter(adapter);
}
};
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_search, container, false);
}
#Override
public void onStart() {
super.onStart();
textViewHandler();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (!(context instanceof FragmentListenter)) throw new AssertionError();
sListener = (FragmentListenter) context;
}
/**
* Interface for communicate to activity
*/
public interface FragmentListenter {
void onFragmentFinish(String userStartRequest);
}
/**
* Handler for the AutoCompleteTextView
*/
private void textViewHandler() {
try {
textView = (AutoCompleteTextView) getView().findViewById
(R.id.startNaviAutoCompleteTextView);
search = new Search();
System.out.println("Created Search object");
textView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
System.out.println("TextWatcher beforeTextChanged");
}
#Override
public void onTextChanged(CharSequence s, final int start, int before, int count) {
delayableSearchHandler.removeCallbacks(delayableSearch); userStartRequest = textView.getText().toString();
sListener.onFragmentFinish(userStartRequest);
if (textView.getText().length() >=
SEARCH_CHAR_AMOUNT) {
new Thread(delayableSearch).start();
delayableSearchHandler.postDelayed
(delayableAdapterCreation, SEARCH_DELAY_MILLIS);
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
At this point, it does not matter to me, whether the calculation already starts whenever a new character is typed into the AutoCompleteTextView and an eventual old search is canceled or the search starts after the 1.5 seconds.
The above code does crash if the search term yields no results and there are problems with the results list. Sometimes it shows up for what has been entered a few keystrokes ago (so if I search for abcd slowly I get search results for abc), sometimes it doesn't show up at all. My guess would be a race condition or some problem with calling the textViewHandler or onTextChanged methods multiple times, even though delayableSearchHandler.removeCallbacks(delayableSearch) should prevent this from happening.
Can anyone explain, what the interaction between the worker thread and the UI thread would have to look like, so it is guaranteed that the search delivers it's results?
Thanks in advance,
Joe
Any long running operation (Network call, database search...) can take long time to execute thus blocking the UI. Prior to Ice cream sandwich this kind of behavior was tolerated by the android runtime.
This article might be a good read
Related
I have the following code. My problem is, that I can't get the JSON.execute() to stop/cancel. I spend quite some time looking up possible answers but I wasn't able to find anything that really worked (e.g. JSON.cancel(true)). As soon as I turn the trackerswitch on, the AsnycTask starts running every 3 seconds just like it's supposed to. Is there a way to easily stop the AsyncTask from executing as soon as the trackerswitch is turned off?
private boolean tracking = false;
private Switch trackerswitch;
private final Timer timer= new Timer();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.table_layout);
final Handler handler=new Handler();
final int delay = 4000;
trackerswitch=findViewById(R.id.trackerswitch);
trackerswitch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NetworkAccess JSON = new NetworkAccess();
if(trackerswitch.isChecked()){
trackerswitch.setText("Tracking...");
tracking=true;
handler.postDelayed(new Runnable() {
#Override
public void run() {
NetworkAccess JSON = new NetworkAccess();
JSON.execute();
handler.postDelayed(this, delay);
}
},delay);
}
else{
tracking=false;
trackerswitch.setText("Start Tracking");
}
}
});
}
}
This is what's called in the network class:
public class NetworkAccess extends AsyncTask<Void, Void, Void> {
public ArrayList<String> alldata = new ArrayList<>();
public ArrayList<String> locationlist = new ArrayList<>();
int stride;
String data;
#Override
protected Void doInBackground(Void... voids) {//4B4ADC
SOME CODE WHICH ISN'T IMPORTANT FOR MY PROBLEM
alldata.addAll(elementlist);
locationlist.addAll(loctrack);
}
}
catch(IOException | JSONException e){
MainActivity.field1.setText(e.getClass().getCanonicalName());
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
MainActivity.field1.setText(String.format("%20s %20s", alldata.get(0), alldata.get(1)));
COUPLE MORE OF THESE SETTEXT COMMANDS TO FILL A TABLE WITH DATA
}
}
Thanks for your help!
handler.postDelayed() adds objects of the Runnable you provide to the message queue, to be run at the specified interval. You need to remove all the queued objects from the message queue in order to cancel the execution. Calling JSON.cancel(true) does not affect other objects that are already added to the queue.
You'll have to retain a reference to your Runnable implementation and then call handler.removeCallbacks(r) to prevent further executions. Instead of using an anonymous class in handler.postDelayed().
This documentation page sheds more light on the matter.
Also refer this page for what happens when you call cancel(true) on an AsyncTask.
I'm using TimeDurationPicker for a dialog in an Android app I'm writing. I want the user to enter a duration for a timer, and have that duration passed back to the activity which calls it. I know there are already answered questions on SO regarding this issue, but I haven't been able to get anything to work.
Here's the activity:
public class train extends AppCompatActivity {
public Integer customTimerlength = null;
public Integer timerDurationSeconds = 180; // 3 minutes is a good default value
public boolean timerIsPaused;
public long millisLeftOnTimer;
Button startBreakTimerButton;
Button stopBreakTimerButton;
Button pauseBreakTimerButton;
TextView breakTimerOutput;
CountDownTimer countdowntimer;
private CountDownTimer mCountDownTimer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_train);
startBreakTimerButton = (Button) findViewById(R.id.startBreakTimer);
stopBreakTimerButton = (Button) findViewById(R.id.stopBreakButton);
pauseBreakTimerButton = (Button) findViewById(R.id.pauseBreakButton);
breakTimerOutput = (TextView) findViewById(R.id.breakTimerOutput);
// Break timer long-click set time
breakTimerOutput.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
//customTimerlength = timerLengthInputAlert();
new RestDurationPicker().show(getFragmentManager(), "Session break length");
and here is the fragment:
import android.widget.Toast;
import mobi.upod.timedurationpicker.TimeDurationPicker;
import mobi.upod.timedurationpicker.TimeDurationPickerDialogFragment;
public class RestDurationPicker extends TimeDurationPickerDialogFragment {
#Override
protected long getInitialDuration() {
return 0; // Default to empty
}
#Override
protected int setTimeUnits() {
return TimeDurationPicker.MM_SS;
}
#Override
public void onDurationSet(TimeDurationPicker view, long duration) {
Toast.makeText(getContext(), "New break duration set", Toast.LENGTH_LONG).show();
}
}
I've found quite a few answers here on SO mentioning intents and interfaces, but I haven't been able to get anything to work and I'm at a loss. This is my first attempt at an Android app so I'm not sure what else to do.
I really appreciate your help in advance!
Figured it out!
I added this to my activity:
// Break timer long-click set time
#Override
public void onDurationSet(long duration) {
Integer i = (int) (long) duration; // get integer i from duration (long)
customTimerlength = i / 1000; // convert millis to seconds
// Set the timer duration in seconds
timerDurationSeconds = customTimerlength;
// Assign the new custom timer duration to the timerduration variable
breakTimerOutput.setText(Integer.toString(timerDurationSeconds));
Log.d("NewTimer", "New Timer Duration: " + timerDurationSeconds);
}
public interface DurationListener {
void onDurationSet(long duration);
}
The fragment now passes the duration to the activity as desired.
In order for your Activity to receive the duration from your DialogFragment, you need to create an interface. Here is an example
public interface DurationListener {
void onDurationSet(long duration);
}
Your activity should then implement this interface. In other words, the activity will have to follow the terms of the interface contract by implementing the onDurationSet() method.
public class TrainActivity extends AppCompatActivity implements DurationListener {
//skipping most of your code
#Override
public void onDurationSet(long duration) {
//Do something with the duration
}
}
Now in your DialogFragment, you need to change your constructor to accept a DurationListener and you need to call onDurationSet() on the listener when the duration is changed by the user.
public class RestDurationPicker extends TimeDurationPickerDialogFragment {
private final DurationListener mListener;
public RestDurationPicker() {}
//The modified constructor, which accepts a listener as a parameter
public RestDurationPicker(DurationListener listener) {
mListener = listener;
}
//Most of your code goes here
#Override
public void onDurationSet(TimeDurationPicker view, long duration) {
//When the duration is set by the user, notify the listener
listener.onDurationSet(duration);
}
}
Now when you create the DialogFragment, just pass the Activity to the Fragment and you are done!
//This code is in your onCreate method in your activity
breakTimerOutput.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
//customTimerlength = timerLengthInputAlert();
new RestDurationPicker(this).show(getFragmentManager(), "Session break length");
}
});
SIDENOTE: You should make sure your code follows the Java and Android coding guidelines, namely by using the correct naming conventions for classes and methods and by keeping class fields private or protected unless you have a reason to make them public. This link can help you with this: https://source.android.com/source/code-style.html
I decide to learn about MVP pattern and after look through some articles i want to try it with my current project.
I have choosen one activity and begin to think how i can decouple it according MVP rules. And eventually I don't know how to do it. It seems like a not complicated activity but I don't know
Could please someone adviced me with what I have to start?
Which methods have to be in presenter, witch view have to be left in this current activity and whitch methods have to be in interface?
Just advised me who i supposed to begin.
This is my class
public final class ActivityUserDataScreen extends AppCompatActivity implements InterfaceActivityUserDataScreen{
private static String gender;
private static int inputHeight;
private static int inputWeight;
private TextInputLayout tilUserName;
private int backPressedQ = 0;
private String avatarName;
private static final String MEN = "men";
private static final String WOMEN = "men";
private Context context;
private PresenterActivityUserDataScreen presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fabric.with(this, new Crashlytics());
setContentView(R.layout.activity_user_data_screen);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setSupportActionBar((Toolbar) findViewById(R.id.tool_bar));
context = getApplicationContext();
initNumberPicker();
initVar();
presenter = new PresenterActivityUserDataScreen(this);
}
private void initNumberPicker() {
NumberPicker pickerHeight = (NumberPicker) findViewById(R.id.pickerHeight);
UtilClass.setDividerColor(pickerHeight, UtilClass.getMyColor(context, R.color.ntz_color_yellow));
pickerHeight.setOnValueChangedListener(changeListener);
pickerHeight.setMaxValue(220);
pickerHeight.setMinValue(130);
pickerHeight.setValue(States.HEIGHT_DEFAULT);
NumberPicker pickerWeight = (NumberPicker) findViewById(R.id.pickerWeight);
UtilClass.setDividerColor(pickerWeight, UtilClass.getMyColor(context, R.color.ntz_color_yellow));
pickerWeight.setOnValueChangedListener(changeListener);
pickerWeight.setMaxValue(120);
pickerWeight.setMinValue(35);
pickerWeight.setValue(States.WEIGHT_DEFAULT);
}
private void initVar() {
tilUserName = (TextInputLayout) findViewById(R.id.tilUserName);
SwitchButton switchButton = (SwitchButton) findViewById(R.id.sb_custom);
switchButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked){
gender = WOMEN;
}else {
gender = MEN;
}
}
});
EditText etAvatarName = (EditText) findViewById(R.id.etAvatarName);
etAvatarName.setText(getResources().getString(R.string.avatar));
}
private NumberPicker.OnValueChangeListener changeListener = new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
switch (picker.getId()) {
case R.id.pickerHeight:
inputHeight = newVal;
break;
case R.id.pickerWeight:
inputWeight = newVal;
break;
}
}
};
#Override
public final void onBackPressed() {
UtilClass.processClick(context);
if (backPressedQ == 1) {
backPressedQ = 0;
super.onBackPressed();
overridePendingTransition(R.anim.open_main, R.anim.close_next);
} else {
backPressedQ++;
Toast.makeText(this, "Press again to exit", Toast.LENGTH_SHORT).show();
}
//Обнуление счётчика через 5 секунд
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
backPressedQ = 0;
}
}, 5000);
}
public final void goNext(View view) {
UtilClass.processClick(context);
EditText editText = tilUserName.getEditText();
Editable editable = null;
if (editText != null) {
editable = editText.getText();
}
if (editable != null) {
avatarName = editable.toString();
}
if (!isValidAvatarName()) return;
saveUserData();
MetadataSaver saver = new MetadataSaver(context);
saver.saveFirstUserInfo();
saver.saveDeviceInfo();
PreferencesHelper.savePref(context, States.STILL_NOT_FINISH, true);
UtilClass.goToNextActivity(ActivityUserDataScreen.this, ActivityVideo.class);
}
private void saveUserData(){
saveAvatarGender();
saveAvatarHeight();
saveAvatarWeight();
saveAvatarName();
}
private void saveAvatarGender(){
if (gender == null){
gender = MEN;
}
PreferencesHelper.savePref(context, States.AVATAR_GENDER, gender);
}
private boolean isValidAvatarName() {
if (UtilClass.isTextEmpty(avatarName)) {
tilUserName.setErrorEnabled(true);
tilUserName.setError(getResources().getString(R.string.fill_your_avatar_name));
return false;
}
if (avatarName.contains(" ")) {
avatarName = avatarName.replace(" ", "");
}
if (!UtilClass.isLatinAlphabet(avatarName)) {
tilUserName.setErrorEnabled(true);
tilUserName.setError(getResources().getString(R.string.avatar_name_in_english));
return false;
}
if (!UtilClass.isNameFree(context, avatarName)) {
tilUserName.setErrorEnabled(true);
tilUserName.setError(getResources().getString(R.string.avatar_name_already_in_use));
return false;
}
return true;
}
private void saveAvatarHeight() {
int result;
if (inputHeight == 0) {
result = States.HEIGHT_DEFAULT;
} else {
result = inputHeight;
}
PreferencesHelper.savePref(context, States.AVATAR_HEIGHT, result);
}
private void saveAvatarWeight() {
int result;
if (inputWeight == 0) {
result = States.WEIGHT_DEFAULT;
} else {
result = inputWeight;
}
PreferencesHelper.savePref(context, States.AVATAR_WEIGHT, result);
}
private void saveAvatarName() {
PreferencesHelper.savePref(context, States.AVATAR_NAME, avatarName);
}
public final void switchManWoman(View view) {
UtilClass.processClick(context);
}
}
Thanks in advance!
The things to take into account are:
The view needs to be as dumb as possible. Think of it as an executor of the commands given by the presenter, and reporter to the presenter of all the stuff that happened on the UI. The interface should provide methods like "display this text", and / or calling presenter's methods like "the button was clicked".
the presenter is the one in command. It drives your view behaviour and reacts to the inputs coming from the view itself. Ideally, it should abstract from anything Android related, in this way you can test the behaviour inside vanilla tests.
Google has published a collection of samples to discuss and showcase different architectural tools and patterns for Android apps.
To begin, very usefull to you to understand how this one works. And adapt to your sample.
[...] This sample is the base for many of the variants. It showcases a simple implementation of the Model-View-Presenter pattern with no architectural frameworks. It uses manual dependency injection to provide a repository with local and remote data sources. Asynchronous tasks are handled with callbacks [...]
I highly recommend reading this article on medium: https://medium.com/#tinmegali/model-view-presenter-mvp-in-android-part-1-441bfd7998fe#.f4yiylrwa .
In essence, all things related to the android SDK should be put in your "view" (and occasionally your model), which will usually be a fragment or activity. Figuring out the difference between your model and presenter will be more up to you, however, you can think about your presenter as the thing that makes program logic decisions based on inputs to your application. Often, the mvp pattern is used in Android development to try to get around rotation and activity recreation issues so you may have luck using a static presenter for a small sample application.
Best of luck!
Following is the code to get the keyword from the user:
class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
private static String keywordToSearch = "";
private static String keyword;
public void Search(View view) {
EditText nameTextBox = (EditText) view.findViewById(R.id.keyword);
Editable editable = nameTextBox.getText();
keyword = editable.toString();
}
private void searchKeyWord()
{
keywordToSearch = keyword;
}
public static String getKeyword()
{
return keywordToSearch;
}
}
There is another class that implements Async Task and needs the keyword as its input to search the web.
But the application crashed, maybe because both the UI and the Async Thread executed simultaneously and that the Async task did not have the input led to the application crashing.
How can I hold the Async task from executing until the keyword is fetched and stored in a variable?
Call async class on button click.
Subclasses of AsyncTask can have constructors with parameters. Obtain the input, use it to construct your AsyncTask, then execute it.
Try this :-
if(keyword!=null){
//call your async task Class here
//ex:- new Hello.execute(); - Hello is your AsyncTask class
}
You can also pass your keyword inside the execute method as new Hello.execute(keyword);
EditText nameTextBox = (EditText) view.findViewById(R.id.keyword);
nameTextBox.addTextChangedListener(new TextWatcher(){
public void afterTextChanged(Editable s) {
// call aynctask here after check 's' length like "s.toString().length() > 0"
}
public void beforeTextChanged(CharSequence s, int start, int count, int after){}
public void onTextChanged(CharSequence s, int start, int before, int count){}
});
Here when edittext value changes then you get the value at after text changed listener method then you send that value in your asynctask.
I have been searching for an answer to my problem, but I seem to get none, despite of how many tutorials I followed, how many questions I've gone through and how many things I've tried to do what I want. Basically, I stumbled upon some good tips, and still couldn't manage to do what wanted.
THE PROBLEM
I am creating an Android Application that will use Fragments (alongside with tabs). In these fragments, I have crucial information relating the application, such as text boxes, and buttons. However, I want to do something really simple, which is updating one of my fragments as I come back to it (imagine I swipe back to a fragment, and I update it with the relevant information). Where is the information stored? On a node.js server, to which I call every time I want information. So for that, I created the following structure.
THE STRUCTURE
First of all, I started off creating my Activity.
public class CentralActivity extends FragmentActivity {
CentralPagerAdapter mCentralActivity;
ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_central);
tabHandler();
}
public void tabHandler() {
mCentralActivity = new CentralPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.CentralPager);
mViewPager.setAdapter(mCentralActivity);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
getActionBar().setSelectedNavigationItem(position);
}
});
//Action Bar Stuff
}
}
With this said, I need my CentralPagerAdapter, which I created as follows.
public class CentralPagerAdapter extends FragmentStatePagerAdapter {
private int nSwipes = 3;
public CentralPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
Fragment fragment = new CentralFragment();
Bundle args = new Bundle();
args.putInt(CentralFragment.ARG_OBJECT, i + 1);
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
return nSwipes;
}
}
And now, my fragment, which is only a class that contains all of my views, and options and so on.
public class CentralFragment extends Fragment {
public static final String ARG_OBJECT = "object";
private View rootView;
private RESTFunction currentFunction;
//Has the info I want
private ArrayList<Integer> tickets = new ArrayList<Integer>();
#SuppressLint("HandlerLeak")
private Handler threadConnectionHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (currentFunction) {
case GET_CLIENT_TICKETS:
handleGetTickets(msg);
break;
case BUY_CLIENT_TICKETS:
break;
default:
break;
}
}
};
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
final Bundle args = getArguments();
handleFragments(inflater, container);
getTicketInfo(null);
return rootView;
}
private void handleFragments(LayoutInflater inflater, ViewGroup container) {
Bundle args = getArguments();
if (args.getInt(ARG_OBJECT) == 1) {
rootView = inflater.inflate(R.layout.fragment_show_tickets,
container, false);
showTicketsHandler();
} else if (args.getInt(ARG_OBJECT) == 2) {
rootView = inflater.inflate(R.layout.fragment_buy_tickets,
container, false);
buyTicketsHandler();
} else {
rootView = inflater.inflate(R.layout.fragment_history_tickets,
container, false);
}
}
public void showTicketsHandler() {
//Get stuff from the tickets array that the REST call will handle
//And set them to boxes or radio buttons
}
public void buyTicketsHandler() {
//Get stuff from the tickets array that the REST call will handle
//And set them to boxes or radio buttons
//As well as button click listeners
}
public void getTicketInfo(ProgressDialog progDialog) {
//Connect to the thread to get the information
//In this case, I have no parameters
ConnectionThread dataThread = new ConnectionThread("myLink", Method.GET, null, threadConnectionHandler, progDialog);
dataThread.start();
}
//Get stuff from the resulting JSON and store it in the tickets ArrayList
private void handleGetTickets(Message msg) {
JSONObject ticketListing = (JSONObject) msg.obj;
try {
tickets.add(ticketListing.getInt("t1"));
tickets.add(ticketListing.getInt("t2"));
tickets.add(ticketListing.getInt("t3"));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
And then, I have my thread..
public class ConnectionThread extends Thread {
private ConnectionRunnable runConnection;
private Handler mHandler;
private ProgressDialog progDialog;
public ConnectionThread(String link, Method method, ArrayList<NameValuePair> payload, Handler handler, ProgressDialog progDialog) {
runConnection = new ConnectionRunnable(link, method.toString(), payload);
mHandler = handler;
this.progDialog = progDialog;
}
#Override
public void run() {
runConnection.run();
threadMsg();
if(progDialog != null)
progDialog.dismiss();
}
public JSONObject getJSON() {
return runConnection.getResultObject();
}
private void threadMsg() {
Message msgObj = mHandler.obtainMessage();
msgObj.obj = getJSON();
mHandler.sendMessage(msgObj);
}
}
And ConnectionRunnable is where I run my HttpURLConnection.
SO WHAT DO I NEED?
Basically, what I'm trying to do, is to get the ticket information from the ConnectionThread BEFORE I load all my view and update them. Plus, I want to be able to swipe back and forth, and update my information on the array as I swipe through the screens (if I go to the second screen, the tickets will update, and if I come back to the first, they will re-update). So basically, call the ConnectionThread everytime I swipe around. If that is possible that, is.
WHAT HAVE I TRIED?
I've tried several things already, and all of them didn't actually help..
The usage of ProgressDialogs to stop the UI Thread on the onCreateView method of the fragment (no use, because it returns the rootView before it handles everything);
Making the UI Thread sleep for 1 second (I don't know why, it blocks all of them);
Overriding the instantiateMethod() of the Adapter, although I think I didn't do it correctly;
Overriding the saveState() of the Adapter, in order to prevent its saved states, and to then get new ticket information;
Giving the fragments tags to update their rootViews on the Adapter, but to no avail;
Getting the information in the activity, and everytime I make a purchase (second fragment), restart the whole activity to get the tickets, which I believe is a really, really bad solution.
I've read several articles, and I still couldn't find my answers.. It's really frustrating. Because it's something so simple, however, the fact that I have to run the HTTP calls on a different thread delays the whole UI updating process.
I've also read the AsyncTask's method. However, I feel like both Threads and AsyncTasks end up in the same.
WHAT TO DO NOW?
Well, that's what I was hoping to find. Because it ends up being annoying as it is.
POSSIBLE REASONS
Is it because I'm separating all classes into spread files, therefore making my work difficult?
Thank you for your time, guys, hope we can find a solution or something.
THE EDIT
So basically, after 4 hours of reading documents and tutorials, I figured that what I needed was setOffscreenPageLimit(int). However, it can't be set to 0, so I will have to do with a setOnPageChangeListener. Now, to figure how to refresh the fragment, and I'll be as good as new.
Alright, it works perfectly! Basically, I did this:
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
((CentralFragment)((CentralPagerAdapter) mViewPager.getAdapter()).instantiateItem(mViewPager, position)).refresh();
getActionBar().setSelectedNavigationItem(position);
}
});
Where my .refresh is:
public void refresh() {
Bundle args = getArguments();
if (args.getInt(ARG_OBJECT) == 0) {
getTicketInfo(0);
} else if (args.getInt(ARG_OBJECT) == 1) {
getTicketInfo(1);
buyTicketsHandler();
} else {
//To Handle Later
}
}
It's as simple as refreshing the page before you go to it. Why didn't I remember this before..? So, here's the reference for those who ever need this!