Sending multiple pieces of data from a fragment to MainActivity (Android Studio) - java

I currently have a fragment containing two spinners and I want to send the information from both spinners to MainActivity. Is this possible? While my code works when I send the information from just one spinner, as soon as I try and send the information from both spinners (per below), none of the information appears to be transmitted:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
onNumberInPartyListener = (onNumberInPartyListener) activity;
onMethodOfSplitListener = (onMethodOfSplitListener) activity;
}
catch (Exception ex){}
}
Do I need to create two onAttach methods, two fragments or is there another way?
Thanks
Update:
So I ended up doing away with the above and instead used an 'Interface' java class to send the information from Fragment 1 to Main Activity, however now I'm having issues sending the information from Main Activity to Fragment 2.
In my Main Activity, I'm sending the information to Fragment 2 with the following code (where 'evenSplit_CalculationFragment2' is Fragment 2 and 'tellMeWhatEachPersonOwesES is the method I've implemented in Fragment 2):
//Send data to Even Split Fragment 2
evenSplit_CalculationFragment2.tellMeWhatEachPersonOwesES(eachPersonOwesESString);
And in Fragment 2 I've implemented this as follows:
//What Each Person Owes (from Main Activity)
public void tellMeWhatEachPersonOwesES (String eachPersonOwesThisESString) {
amountEachPersonOwesES.setText(eachPersonOwesThisESString);
}
However, I'm coming up with a Null Pointer exception for both of these. I've tried testing this by substituting 'eachPersonOwesThisESString' with an actual string (e.g. "test") but most baffling of all I still get a Null Pointer exception. Any help appreciated.

You should use Handler
h = new Handler() {
public void handleMessage(android.os.Message msg) {
// Getting data from Handler
tvInfo.setText("Data from Spinner1: " + msg.what);
if (msg.what == 10)
// do what you need
};
};
You can use one handler with different msg.what codes to distinguish them. Initialise it in activity and send to fragment, it will fire up when you write h.sendMessage

If I wanted to solve this problem, what I'd do is use an event bus (although if you really want, you can technically use LocalBroadcastManager with Parcelables).
With Otto event bus, it'd look like this
public enum SingletonBus {
INSTANCE;
private Bus bus;
private Handler handler = new Handler(Looper.getMainLooper());
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public <T> void postToSameThread(final T event) {
bus.post(event);
}
public <T> void postToMainThread(final T event) {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
public <T> void register(T subscriber) {
bus.register(subscriber);
}
public <T> void unregister(T subscriber) {
bus.unregister(subscriber);
}
}
public class YourFragment extends android.support.v4.Fragment {
public static class SpinnersSelectedEvent {
public String firstSpinnerData;
public String secondSpinnerData;
public SpinnersSelectedEvent(String firstSpinnerData, String secondSpinnerData) {
this.firstSpinnerData = firstSpinnerData;
this.secondSpinnerData = secondSpinnerData;
}
}
#OnClick(R.id.yourfragment_thebutton)
public void theButtonClicked() {
SingletonBus.INSTANCE.postToSameThread(new SpinnersSelectedEvent(firstSpinner.getSelectedItem(), secondSpinner.getSelectedItem()); //pseudo code on the spinner part
}
}
public class MainActivity extends AppCompatActivity {
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
//...
SingletonBus.INSTANCE.register(this);
}
public void onDestroy() {
super.onDestroy();
SingletonBus.INSTANCE.unregister(this);
}
#Subscribe
public void onSpinnersSelectedEvent(YourFragment.SpinnersSelectedEvent e) {
String firstData = e.firstSpinnerData;
String secondData = e.secondSpinnerData;
// do things
}
}

Related

sometimes a field annotated with #ViewById returns null when accessed on UIThread

I have a class which uses AndroidAnnotations and #ViewById annotation. I've seen many crashes because of some of the fields with #ViewById annotations being null.
Attempt to invoke virtual method 'void com.orangegangsters.github.swipyrefreshlayout.library.SwipyRefreshLayout.setRefreshing(boolean)' on a null object reference
a problem with two of them which
Here's part of the class:
#EFragment(R.layout.fragment_categories_tab)
public class CategoriesTabFragment extends BaseFragment implements CategoryAdapter.OnItemSelected, SwipyRefreshLayout.OnRefreshListener {
#ViewById(R.id.swipeRefreshLayout)
protected SwipyRefreshLayout swipeRefreshLayout;
#ViewById(R.id.placeholder_textview)
protected CustomTextView statusLabel;
private void loadChildren() {
AndroidUtil.runOnUiThread(new Runnable() {
#Override
public void run() {
swipeRefreshLayout.setRefreshing(true); //crash happens here, but not always
}
});
}
categoryService.getCategories(new CommListener<Pair<Category[], Boolean>>() {
#Override
public void onSuccess(NetworkResult result, Pair<Category[], Boolean> object) {
AndroidUtil.runOnUiThread(new Runnable() {
#Override
public void run() {
if (getActivity() != null) {
statusLabel.setText(""); //another point that crash happens, but again sometime and only in early stage of the fragment's life
}
}
}
}
}
And this is the AndroidUtil class:
public class AndroidUtil {
private static Handler mainHandler = new Handler(Looper.getMainLooper());
public static void runOnUiThread(Runnable runnable) {
mainHandler.post(runnable);
}
}
I'm new to Android; and I don't seem to find the reason. I'd appreciate any help, in advance.
Change AndroidUtil.runOnUiThread to getActvity() and check null before using.
In your case, it's crashing because sometimes your fragment is not attached to the Activity so all you layout inside is null.

How to interact with UI from a different class

I would like to update my UI from a different class. I am familiar with runOnUiThread() method, but don't know how to implement it in this scenario?
public class UploadAct extends MainActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upload);
}
//my code and functions would go here
}
Then, my UploadData class
public class UploadData extends UploadAct {
public void doSomethig(){
printThis("I want to print this message to the UI");
}
public void printThis(String messsage) {
final String mess = message;
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),mess,Toast.LENGTH_LONG).show();
// I want this to display on the main thread
txt_upload.setText(mess);// and this also
}
});
}
}
Use BroadcastReceiver
// define a Broadcast Intent Action in String resources
<string name="broadcast_id">MY_BROADCAST_ID</string>
// register receiver in constructor/onCreate()
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
IntentFilter myIntentFilter = new IntentFilter();
myIntentFilter.addAction(context.getString(R.string.broadcast_id));
context.registerReceiver(myBroadcastReceiver, myIntentFilter);
// place your BroadcastReceiver in MainActivity, your UploadData class
public class MyBroadcastReceiver extends BroadcastReceiver {
public MyBroadcastReceiver(){
super();
}
#Override public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Broadcast received");
if(intent.getAction() != null && intent.getAction().equals(context.getString(R.string.broadcast_id)) ){
// do something
}
}
}
// send Broadcasts from where you want to act, your UploadAct class.
Intent intent = new Intent();
intent.setAction(context.getString(R.string.broadcast_id));
context.sendBroadcast(intent);
Log.d(TAG, "Broadcast sent.");
// you can unregister this receiver in onDestroy() method
context.unregisterReceiver(myBroadcastReceiver);
You can also use an interface to update your UI as a listener.
First, Create an interface
public interface UpdateTextListener {
void updateText(String data);
}
Then, Call its method in your UploadData class
public class UploadData extends UploadAct {
UpdateTextListener listener;
public void doSomethig(){
listener.updateText("data to be loaded");
}
}
Then, Update your UploadAct by listening to this method
public class UploadAct extends MainActivity implements UpdateTextListener {
#Override
public void updateText(String data) {
textview.setText(data);
}
}
First of all - there is no such thing like UI of some class. There are activities that can have handles to UI widgets (ex TextView). If you want to make some changes to UI from your UploadData class you have to pass somehow reference to this class. Possibly by constructor:
public class UploadData extends UploadAct{
private TextView txt_upload;
public UploadData(TextView tv)
{
txt_upload = tv;
}
public void doSomethig(){
printThis("I want to print this message to the UI")
}
public void printThis(String messsage) {
final String mess = message;
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(),mess,Toast.LENGTH_LONG).show();// I want this to display on the main thread
txt_upload.setText(mess);// and this also
}
});
}
}
I assume that you create DataUpload in your MainActivity.
Everyone use so much library to be trendy as they forget built in functions in Android :)
For sure isn't any hard thing to use AsyncTask, beside it provides the doInBackground function it has the https://developer.android.com/reference/android/os/AsyncTask.html#publishProgress(Progress...) function too, what you have asked for.
Just create a class (UploadTask) which extends AsyncTask and override 1-2 function.

Android Individual AsyncTask

I am having a slight problem in Android Async Task. In my MainActivity, I am calling GetEventAsyncTask which will execute the method inside called retrieveEventJSON:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
context = this;
public void onSingleTap(float x, float y) {
final Point point = mMapView.toMapPoint(x, y);
eventModel.setEventX(String.valueOf(point.getX()));
eventModel.setEventY(String.valueOf(point.getY()));
new MyAsyncTask(new MyAsyncTask.OnRoutineFinished() {
public void onFinish() {
CreateEvent.createEventDialog(context, point.getX(),
point.getY(), eventAddress); //this will be called after the task finishes
}
}).execute(eventModel);
}
});
new GetEventAsyncTask().execute();
}
In my GetEventAsyncTask, basically I am just retrieving the data returned from JSON and save them into an array:
public class GetEventAsyncTask extends AsyncTask<Event, Integer, Double> {
EventController eventCtrl = new EventController();
String eventAddress;
Event eventModel = new Event();
public interface OnRoutineFinished{ //interface
void onFinish();
}
private OnRoutineFinished mCallbacks;
public GetEventAsyncTask(OnRoutineFinished callback){ //constructor with interface
mCallbacks = callback;
}
public GetEventAsyncTask(){} //empty constructor to maintain compatibility
#Override
protected Double doInBackground(Event... params) {
try {
eventAddress = eventCtrl.getStreetAddressFromGeometry(eventModel.getEventX(), eventModel.getEventY());
eventCtrl.retrieveEventJSON();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Double result) {
if(mCallbacks !=null)
mCallbacks.onFinish(); //call interface on finish
}
protected void onProgressUpdate(Integer... progress) {
}
}
Then when the navigation drawer item onselected, I am calling the plotting marker on map method which takes in the array I saved just now:
case 0:
eventCtrl.plotEventOnMap(context);
break;
I tried to print out the data retrieved in retrieveJSON and it did printed out. But somehow, when I tried to plot onto the map, it does not shows anything. I wonder which part that I overlapped or reinitialize some Object?
The strange thing is if I put getEventAsyncTask under MainActivity, it did run and retrieved the data. But however, if I shifted the getEventAsyncTask out as an individual class, it stopped working. I wonder why is it so?
Thanks in advance.

Detect connectivity change within Android Activity subclass

I'm relatively new to Android,
I have read related articles on detecting network connectivity changes and have implemented this BroadcastReceiver subclass, made the necessary additions to AndroidManifest.xml and I receive the requisite state change broadcasts as expected:
public class NetworkStateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
}
}
Question is: how can I receive or forward these notifications in/to my Activity subclasses? Apparently creating an instance of NetworkStateReceiver in my Activity subclass and overriding onReceive there doesn't do the trick.
Thanks in advance for any pointers...
Edit:
I ended up broadcasting an Intent from onReceive above like so:
Intent target = new Intent(CONNECTIVITY_EVENT);
target.putExtra(CONNECTIVITY_STATE, networkInfo.isConnected());
context.sendBroadcast(target);
And receiving that in my Activity like so:
#Override
protected String[] notifyStrings() {
return ArrayUtils.addAll(super.notifyStrings(), new String[] {NetworkStateReceiver.CONNECTIVITY_EVENT});
}
#Override
protected void notifyEvent(Intent intent, String action) {
super.notifyEvent(intent, action);
if (action != null) {
if (action.equalsIgnoreCase(NetworkStateReceiver.CONNECTIVITY_EVENT)) {
boolean isConnected = intent.getBooleanExtra(NetworkStateReceiver.CONNECTIVITY_STATE, true);
// Do something...
}
}
}
I would recommend using either
1) An interface approach. So declare an interface that has a networkChanged() method, and have the class which owns this BroadcastReceiver keep a list of classes who want to be notified of network changes with a local List<InterfaceName>
2) Skip the interface creating and use a subscription utility. My two favorites are
https://github.com/greenrobot/EventBus
and
https://gist.github.com/bclymer/6708819 (smaller, less used, also disclaimer: I wrote this)
With these you would create event classes with properties, and then subscribe and post instances of those classes.
In your activity
#Override
public void onCreate() {
...
EventBus.getInstance().subscribe(this, MyType.class);
}
#Override
public void onDestroy() {
...
EventBus.getInstance().unsubscribe(this, MyType.class);
}
#Override
public void newEvent(Object event) {
if (event instanceOf MyType) {
// do stuff
}
}
And then in your BroadcastReceiver
#Override
public void onReceive(Context context, Intent intent) {
EventBus.post(new MyType(true));
}
Example MyType
public class MyType {
public boolean networkEnabled;
public MyType(boolean networkEnabled) {
this.networkEnabled = networkEnabled;
}
}
This examples use the 2nd subscription utility (mine).

Java, using an interface as a callback

I have been developing a simple touch handler for Android with the possibilites of firing callbacks like onUpdate (when the screen is touched) without having to setup threads. My problem is that my knowledge of Java is fairly limited and i can't do it because i know very little of how to use interfaces. I'm pretty sure that my problem may be a simple typo or something, but i get a NullPointerException when i execute the method from the touch handler (which processed the touch information) so that i can do what i need in the main activity class.
This is the main class code (cut from the irrelevant stuff):
//package and imports
public class Test extends Activity implements TouchHelper {
StringBuilder builder = new StringBuilder();
TextView textView;
TouchReader touchReader;
List<TouchTable> touchTablesArray;
TouchTable touchTable;
public static final String Tag = "TouchTest";
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
textView = new TextView(this);
Log.d(Tag, "TextView initialized " + textView);
textView.setText("Touch and drag (multiple fingers supported)!");
touchReader = new TouchReader(textView);
Log.d(Tag, "touchReader initialized");
touchTablesArray = touchReader.getTouchTables();
setContentView(textView);
}
#Override
public void onTouchUpdate(int pointerId)
{
Log.d(Tag, "onTouchUpdate called");
touchTable = touchTablesArray.get(pointerId);
Log.d(Tag, "touchTable get successful");
//writing on stringbuilder
}
}
This is the code of the handler itself:
//package and imports
public class TouchReader implements OnTouchListener
{
public final static String Tag = "TouchReader";
List<TouchTable> touchTables;
TouchHelper helper;
TouchTable touchTable = new TouchTable();
public TouchReader(View view)
{
view.setOnTouchListener(this);
touchTables = new ArrayList<TouchTable>(10);
Log.d(Tag, "TouchReader initialized");
}
public boolean onTouch(View v, MotionEvent event)
{
synchronized(this)
{
//all the common code handling the actual handling, with switches and such
touchTables.add(pointerId, touchTable); //obviously the pointerId is defined earlier
Log.d(Tag, "Values updated");
helper.onTouchUpdate(pointerId); //the exception is here
Log.d(Tag, "Update called");
}
return true;
}
public List<TouchTable> getTouchTables()
{
synchronized(this)
{
return touchTables;
}
}
}
As you can see the error is most likely due to my inability to correctly use an interface, and yet all the official docs confused me even more.
Finally, the tiny code of the interface:
//package
public interface TouchHelper
{
public void onTouchUpdate(int pointerId);
}
I hope this question isn't too noobish to post it here :)
EDIT: Thanks to all for the help, in the end i followed Bughi's solution.
Your TouchHelper helper; is null, it needs a instance of the interface to be able to call methods on it -in your case the main activity class that implements your interface-
Make a set method for the listener
public void setOnTouchListener(TouchHelper helper)
{
this.helper = helper;
}
Then call it from on create:
public class Test extends Activity implements TouchHelper {
...
#Override
public void onCreate(Bundle savedInstanceState)
{
...
touchReader = new TouchReader(textView);
touchReader.setOnTouchListener(this);
...
}
}
Also add a null check to your on touch method:
public boolean onTouch(View v, MotionEvent event)
{
synchronized(this)
{
//all the common code handling the actual handling, with switches and such
touchTables.add(pointerId, touchTable); //obviously the pointerId is defined earlier
Log.d(Tag, "Values updated");
if (helper != null)
helper.onTouchUpdate(pointerId); //the exception is here
Log.d(Tag, "Update called");
}
return true;
}
If the NullPointerException is here:
helper.onTouchUpdate(pointerId);
Then simply helper is null, where do you initialize it?
I see that you define it:
TouchHelper helper;
But do you ever have?
helper = ...
I know this is old, but I was stuck on this myself. Sam's post above helped me think of it.
I finally added an onAttach method that that checks that the interface is initialized as well as implemented to the main activity that it interfaces with. I added a Log.i inside the main activity to test.
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mainActivityCallback = (OnSomethingSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnSomethingSelectedListener");
}
}
In TouchReader you define a TouchHelper but nowhere in the code an object is created or an existing object is assigned to that attribute. So it is still null when you try to use it.
helper is null in your in TouchReader
To fix this make the TouchReader take a TouchHelper:
public TouchReader(View view, TouchHelper helper) {
...
this.helper = helper;
...
}
Then in your activity:
touchReader = new TouchReader(textView, this);
Try initializing it in your constructor; all reference that aren't initialized are set to null.
// I see no reason why this should be a member variable; make it local
StringBuilder builder = new StringBuilder();
TextView textView;
TouchReader touchReader;
List<TouchTable> touchTablesArray;
TouchTable touchTable;
public TouchReader(View view)
{
// textView is null
// touchReader is null
view.setOnTouchListener(this);
// why "10"? why a List of touchTables and a touchTable member variable? why both?
touchTables = new ArrayList<TouchTable>(10);
Log.d(Tag, "TouchReader initialized");
// touchTable is null;
}

Categories

Resources