I'm having some trouble, and I have done research on the problem but it did not help me.
I have a Main.java class, which is template made pager view of fragments.
I did not modify it in any way, except for getItem() method which now returns my own fragments.
My fragment A is collecting accelerometer data, saving last 120 records in an ArrayList.
It also has graph plots, with Series objects, which I would like to access from fragment B.
This is what I want to do:
In fragment B user presses a button "record data"
This results in fragment A boolean field "recordingOn" changing to true
Now in fragmentA onSensorChange method, after checking for "recordingOn", if it's true it will pass data to an ArrayList that is a field in fragment B
How do I access these fields?
from fragment call
Fragment frag = getActivity().getFragmentManager()..findFragmentByTag("fragment to find tag");
Greensy answer is a valid option (I upvoted him) but you can also refrain from doing so much DataPassing between fragments and let the Activity be a manager for all that.
For that you can create public methods and interfaces in the fragments and let/force the activity implement the interfaces.
Simple example:
public class Act extends Activity implements OnClickListener{
#Override
OnClick(View v){
if(v.getId()==R.id.btn_recordData){
fragA.startRecording();
}
}
}
then FragA you must create that method:
public class FragA extends Fragment{
private boolean isRecording
public void startRecording(){ isRecording = true; // also init the sensor stuff }
public void stopRecording() { isRecording = false; // also stop the sensor stuff }
}
then on FragB you can:
public class FragB extends Fragment{
onCreateView(...){
// initialise all the views and find the button, let's call it Button btn;
btn.setOnClickListener((OnClickListener)getActivity());
}
}
does that make sense?
A way to do that is to use your own Application class and store your ArrayList in there
You can then access it in any point of your application, if you got a Context :
ArrayList<AccelerometerData> myAccelerometerData = ((MyApplication) mContext.getApplicationContext()).getAccelerometerData();
Be carefull about this solution since your ArrayList will be alive during all the application run
Related
I'm using Android Studio. I haven't been able to find an answer online, so even a link to a solution would be helpful.
I have an Activity, which includes a number of Fragments. One of these Fragments is called BookGridFragment, which uses a class called BookGrid.
BookGridFragment looks like this (I've left out irrelevant bits):
public class BookGridFragment extends Fragment {
BookGrid myBookGrid;
public BookGridFragment() {}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
// Inflate layout
View rootView = inflater.inflate(
R.layout.fragment_book_grid, container, false);
myBookGrid = rootView.findViewById(book_grid);
return rootView;
}
public void setBook(Book thisBook) {
myBookGrid.setBook(thisBook);
}
}
The BookGrid class is:
public class BookGrid extends View {
private Book mBook;
public BookGrid(Context thisContext, AttributeSet attrs) {
super(thisContext, attrs);
}
public void setBook(Book newBook) {
mBook = newBook;
}
protected void onDraw(Canvas canvas) {
if (mBook == null) return;
canvas.save();
draw_book_details();
// draw_book_details() is a function which just takes
// the book info and displays it in a grid
canvas.restore();
}
public boolean onTouchEvent(MotionEvent event) {
// This function responds to the user tapping a piece of
// book info within the grid
// THIS IS WHERE I'M HAVING PROBLEMS
}
}
So, that all works fine. The issue is, that I need the BookGridFragment to know when the user touches the BookGrid and to pass that information to another Fragment (via the Activity). So, I assume that when the onTouchEvent is reached, that should somehow notify the BookGridFragment that the BookGrid was touched, but I can't figure out how to do that.
Everything I've found online is about passing information between Fragments, but that approach doesn't work here as the BookGrid class doesn't "know" that it's within a BookGridFragment.
You could use the same idea that is used to communicate a Fragment and an Activity. Create an interface:
public interface OnBookGridTouched{
void onTouchGrid();
}
Add a variable to your BookGrid:
private OnBookGridTouched mCallback;
Add a setter to this variable:
public void setCallback(OnBookGridTouched callback){
mCallback = callback;
}
Then make your fragment implement the interface:
public class BookGridFragment extends Fragment implements OnBookGridTouched {
You'll be forced to implement the method onTouchGrid
In your fragment onCreateView pass the fragment to your custom view:
myBookGrid.setCallback(this);
Finally, in your custom view you can call the callback to reference the fragment:
public boolean onTouchEvent(MotionEvent event) {
// This function responds to the user tapping a piece of
// book info within the grid
// THIS IS WHERE I'M HAVING PROBLEMS
mCallback.onTouchGrid();
}
A solution could be to set the onTouch/onClick listener in the fragment instead of in the BookGrid itself. From there you can use the fragment method getActivity() to call an activity method, parsing on the correct data to the correct fragment.
I think this situation is very similar to a Fragment containing a Button.
The Button has a method which accepts something implementing a certain interface (for the Button: View.OnClickListener). The Fragment calls that method (for the Button: setOnClickListener()) to pass in the desired Object implementing all the required methods, either an anonymous class or maybe a field or the Fragment itself. There are pros and cons for all three approaches, it depends on your situation which one is best.
They have in common that BookGrid should have an interface as well as a method so other classes can set the current Object implementing that interface.
I am not quite sure about the exact scenario that you are having there. However, if the problem is the communication between the fragment and an activity which hosts the fragment, then you might think of the following implementation.
Let me point out some of your concerns first.
Everything I've found online is about passing information between
Fragments, but that approach doesn't work here as the BookGrid class
doesn't "know" that it's within a BookGridFragment.
BookGrid class will know the context of its existence when you will pass the Context towards it while calling a function of it. So I would like to suggest passing the context of the Activity or Fragment when you are calling a function from your BookGrid class.
public class BookGrid extends View {
private Book mBook;
private Context context;
public BookGrid(Context thisContext, AttributeSet attrs) {
super(thisContext, attrs);
this.context = thisContext;
}
public void setBook(Book newBook) {
mBook = newBook;
}
protected void onDraw(Canvas canvas) {
if (mBook == null) return;
canvas.save();
draw_book_details();
// draw_book_details() is a function which just takes
// the book info and displays it in a grid
canvas.restore();
}
public boolean onTouchEvent(MotionEvent event) {
// Call the function of your host activity
((YourActivity)(thisContext)).onBookGridTouched();
}
}
Now write a public method in your activity class which hosts the fragment named onBookGridTouched.
public void onBookGridTouched() {
// Communicate with other fragments here
}
However, a noble approach of solving this problem in a more generic way is to use an interface and then implement the interface wherever necessary like #LeviAlbuquerque suggested.
I am just putting another workaround which is a bit static.
Assuming that you have ONE Activity responsible of all fragments:
1.Create an interface in your BookGrid:
public interface ActionHappened {
void onActionHappened();
}
2.Create an instance of your interface within your BookGrid class and trigger the method onActionHappened where you want it to be triggered. For instance, if you would like it to happen in your onDraw(), then do the following:
ActionHappened actionHappened;
protected void onDraw(Canvas canvas) {
if (mBook == null) return;
canvas.save();
draw_book_details();
// draw_book_details() is a function which just takes
// the book info and displays it in a grid
canvas.restore();
actionHappened.onActionHappened();
}
3.Implement your interface within your activity
public class ActivityA extends AppCompatActivity implements BookGrid.ActionHappened {}
4.Within your implemented method, trigger the method:
#Override
public void onActionHappened() {
Fragment fragmentA = getSupportFragmentManager().findFragmentByTag(R.id.fragmentA);
Fragment fragmentB = getSupportFragmentManager().findFragmentByTag(R.id.fragmentB);
//Trigger that method from your activity to fragmentA or fragmentB
fragmentA.doWork();
fragmentB.doWork();
}
Wether you would like to pass data to fragmentA or fragmentB, doWork() method will do that for you. Make you create such a method in the corresponding fragment.
I am trying to put in app billing in my android app. I am currently stuck on how to call back to my MainActivity to perform a UI change from the helper billing class once it is found the user has previously bought something.
I have searched and searched but I cannot find what I'm looking for, I'm sure I need to implement a callback or a listener or both?
My code is the following:
MainActivity
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
MyBilling bill;
private Menu nav_Menu;
#Override
protected void onCreate(Bundle savedInstanceState) {
bill = new MyBilling(this);
bill.onCreate();
}
private void displaySelectedScreen(int itemId) {
///calls fragment and inflates
}
public void UpdateUI(){
//Make some changes to UI
nav_Menu.findItem(R.id.remove_ad_button).setVisible(false);
//Recall fragment
displaySelectedScreen(R.id.distance_check);
}
}
Billing helper class (Google In App Billing)
public class MyBilling {
Activity activity;
public MyBilling(Activity launcher) {
this.activity = launcher;
}
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null)
return;
// Is it a failure?
if (result.isFailure()) {
// complain("Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
/*
* Check for items we own. Notice that for each purchase, we check
* the developer payload to see if it's correct! See
* verifyDeveloperPayload().
*/
// Do we have the premium upgrade?
Purchase removeAdsPurchase = inventory.getPurchase(SKU_REMOVE_ADS);
AdCheck = (removeAdsPurchase != null && verifyDeveloperPayload(removeAdsPurchase));
//Yes there has been purchases!
if(AdCheck == true){
removeAds(); // sets global flag
//// Want to call UI change here....
MainActivity main = new MainActivity();
main.UpdateUI();
}
}
};
}
What I understand from debugging is IabHelper.QueryInventoryFinishedListener is called during oncreate of billing class, it is asynchronous, so my main activity continues to compile. When setup and reading of purchases are complete I need to go back to my MainActivity and make changes.
If anyone can point me in the right direction here please - I've tried to keep the code straight forward by just provided the needed snippets.
Use an Interface. Add the UpdateUI() method declaration in the Interface. Then create instance of the Interface in the MyBilling class and initialise using constructor. Call the UPdateUI() method throught the the instance of the Interface. Then implement the Interface in your MainActivity. If you are unclear about implementing, I'd be happy to post the code.
The code :
In your MyBilling class, add the following inside the class
public interface Update {
void UpdateUI();
}
Use this to call the UpdateUI() method from the MyBilling class.
myActivity.UpdateUI();
Use this as the constructor in MyBilling class
Update myActivity;
public MyBilling (Update activity) {
myActivity = activity;
}
Now change to the following in the MainActivity
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, MyBilling.Update
If a red line appear beneath it, Alt+enter it to implement the Update interface in MainActivity. I'm guessing since you already has UpdateUI() in your MainActivity, there wont be a red line error. Hope it helps!
I was trying to communicate between two fragments, but I'm just a beginner so I want to know if there is any solution not using interface....
If there are Fragment A, B, and their Activity:
Sending values from Fragment A to Activity
(In here, Fragment A is a current state)
Sending values from Activity to Fragment B
I know direct communication between two fragments is almost impossible,
but I don't think that makes me to use an interface.
Also, is there any method to use like putExtra() for fragment? I only know using Serializable.
Have a look at the Android deverlopers page: http://developer.android.com/training/basics/fragments/communicating.html#DefineInterface
Basically, you define an interface in your Fragment A, and let your Activity implement that Interface. Now you can call the interface method in your Fragment, and your Activity will receive the event. Now in your activity, you can call your second Fragment to update the textview with the received value
// You Activity implements your interface
public class YourActivity implements FragmentA.TextClicked{
#Override
public void sendText(String text){
// Get Fragment B
FraB frag = (FragB)
getSupportFragmentManager().findFragmentById(R.id.fragment_b);
frag.updateText(text);
}
}
// Fragment A defines an Interface, and calls the method when needed
public class FragA extends Fragment{
TextClicked mCallback;
public interface TextClicked{
public void sendText(String text);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (TextClicked) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement TextClicked");
}
}
public void someMethod(){
mCallback.sendText("YOUR TEXT");
}
#Override
public void onDetach() {
mCallback = null; // => avoid leaking, thanks #Deepscorn
super.onDetach();
}
}
// Fragment B has a public method to do something with the text
public class FragB extends Fragment{
public void updateText(String text){
// Here you have it
}
}
You can communicate between fragments directly by using EventBus - send ordinary or sticky events by one fragment and subscribe to that event in another one.
If don't want message to be lost, use sticky events - it work as sticky Intent in Android. It will be around until it is removed by targer fragment or because another event is pending.
Yes you can transfer data between fragments using bundle like you do in Activity using putExtra
Bundle = bundle = new Bundle();
bundle.putString("key","value");
bundle.putSerializable("serialzedKey",SerializedValue);
FragmentTransaction fts = ((BaseActivity) mContext).getSupportFragmentManager().beginTransaction();
fragment.setArguments(bundle);
fts.add(R.id.fragmentHolder, fragment);
fts.addToBackStack(fragment.getClass().getSimpleName());
fts.commit();
In other fragment you can retrieve data using getArguments()
String key = getArguments().getString("key");
SerializedModel = getArguments().getSerializable("serialzedKey");
you can call a method from the parent activity class that calls a method from fragment B like ((YourActivity)getActivity()).callMethod(T yourData)
Take a look at my Github repo on using interfaces to communicate between fragments.
This is just a really simple example but displays the key concepts.
https://github.com/stoddayy/FragmentInteractionExample
I have a class extending ParseQUeryAdapter so I can use the notifyDataSetChanged feature. The adapter class is called mainAdapter.
Here's my notifyDataSetCHanged method in the mainAdapter:
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
MainActivity mainActivity = new MainActivity();
mainActivity.getItems();
}
Here's my getItems() method in MainActivity:
public void getItems(){
if(adapter == null){
Toast.makeText(MainActivity.this, "null", Toast.LENGTH_SHORT).show();
}
}
The app crashes on loading. As you can see, I planted an if so that I can see if adapter was null. But it still crashes.
According to the debugger, it says in green after getting to the if line, "adapter:null". However, I have this in onCreate():
adapter = new mainAdapter(this);
And I declared it:
mainAdapter adapter
Is there a method I can put in that will solve my issue? Although I am implementing the class, why is it still null? I clearly stated that adapter = new mainAdapter()
You should never instantiate your activity classes with new; they should be interacted with using startActivity() and related APIs.
An activity created with new won't be registered with the ActivityManager, won't show up on the screen, and won't have any of it's lifecycle callbacks called.
Since your MainActivity instance's onCreate() method has not been run, adapter has not been created.
In your case, it seems like you would want your ParseQueryAdaptor subclass to have a reference to your activity in some way, so that it can access the right one.
If you want to reference an existing activity then you have to pass the activity object to where you want to use it (or use getContext or getActivity when your in a class that has that available).
One of the things you can do is create a method that passes the MainActivity object into your ParseQUeryAdapter. Then when you are calling stuff in your adapter do: activityObject.whatevermethodyouwantocallontheactiviy() be shure to error check the activity object first though.
IE:
private MainActivity activity;
public void setup(MainActivity activity){ this.activity = activity;}
#Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
if(activity != null){
activity.getItems();
}
}
Im new to android dev and Im currently struggling with this annoying problem for days.
Tried google and many articles. None helped. Also tried IRC for some on line help but couldnt get a straight answer. Maybe you can direct me in the right direction...
My app is basically a ViewPager that holds a single Fragment class. Inside the Fragment class, there is a switch case block that determines based on position, which image to load from the resources folder. The thing is, I have a TextView View in my parent Activity that needs to get updated according to the current picture. Like an image title.
I used:
TextView tv = (TextView) getActivity().findViewById(R.id.titleTV);
in onCreateView() to get access to that TextView from within the Fragment. And added a:
tv.setText("Picture 1");
for instance, to the switch case block. This way, when a picture is shown, the text view gets updated.
The problem is, the method that retrieves a new Fragment with each slide, getItem(int position) in the parent Activity, gets called twice to load more than one Fragment to memory. This causes the current position to be one int ahead. Meaning, the switch case stands on case 0 for instance and it shows a specific picture, but the text gets updated from case 1.
I cant get it to work properly because of that.
What am I doing wrong??
Thank you
If you are able to determine text based on current fragment position only you can use ViewPager.OnPageChangeListener. In you Activity implement that interface and use it by calling mViewPager.setOnPageChangeListener(SomeOnPageChangeListener). Determine what text put into TextView with OnPageChangeListener.onPageSelected() method.
Unfortunately there is a bug in SDK and onPageSelected() is not called for page at position 0 when it is shown for the first time. You can find workaround under above link, use ViewPager.setCurrentPage(1) or just set text for that position manually in onCreate() method.
Example code:
//activity onCreate()
protected void onCreate(Bundle savedInstanceState) {
...
mTextView = (TextView) findViewById(R.id.text_view);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
String someText = determineText(position)
mTextView.setText(someText);
});
//because onPageSelected will not be called for page 0 first time
String someText = determineText(0)
mTextView.setText(someText);
}
private String determineText(int position){ //static?
//switch?
}
Ok I'll give a piece of code, you can use that, not just for a String but for any data at all that you want to send to the Activity from the Fragment, or even from other type of classes between each other.
That's the type of code is also used on .'setOnSomethingHappenListener(listener);'
public interface TitleChangeListener{
public void onTitleChanged(String title);
}
then on your activity you will
public class MyActivity extends Activity implements TitleChangeListener{
#Override
onTitleChanged(String title){
// set here your value
}
}
then on your fragment
public void MyFragment extends Fragment{
private TitleChangeListener listener;
#Override
public void onAttach(Activity activity){
if(activity instanceof TitleChangeListener)
listener = (TitleChangeListener)activity;
else
// throw some error ???
}
#Override
public void onResume(){
listener.onTitleChanged("my title");
}
}
and remember I typed all of that by heart, there is bound to have some small mistakes.
But just follow the idea and you'll be fine.