In my adnroid app when the user goes to their own profile, there is a fragment there with two buttons - X points and settings.
For the button X points I want to change the text to whatever the amount of points they have, for example 12 points.
I've tried numerous things but nothing seems to work:
Attempt 1:
myProfileActionButtonsHolder = (TableRow) findViewById(R.id.myProfileActionButtonsHolder);
getSupportFragmentManager().beginTransaction().replace(R.id.myProfileActionButtonsHolder, new MyProfileActionButtonsFragment()).commit();
MyProfileActionButtonsFragment.bMyProfilePoints = (Button) findViewById(R.id.bMyProfilePoints);
MyProfileActionButtonsFragment.bMyProfilePoints.setText("asd");
Attempt 2:
MyProfileActionButtonsFragment myProfileActionButtonsFragment = (MyProfileActionButtonsFragment) getSupportFragmentManager().findFragmentById(R.id.myProfileActionButtonsHolder);
((Button)myProfileActionButtonsFragment.getView().findViewById(R.id.bMyProfileSettings)).setText("asd");
Attempt 3
myProfileActionButtonsFragment.setBMyProfileSettingsText("asd"); //setBMyProfileSettingsText is a custom method defined inside the fragment
Here is how my fragment looks:
public class MyProfileActionButtonsFragment extends SherlockFragment {
public static Button bMyProfilePoints;
public Button bMyProfileSettings;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view= inflater.inflate(R.layout.my_profile_action_buttons_fragment, container, false);
bMyProfilePoints = (Button) view.findViewById(R.id.bMyProfilePoints);
bMyProfileSettings = (Button) view.findViewById(R.id.bMyProfileSettings);
return view;
}
public void setBMyProfileSettingsText(String text) {
bMyProfilePoints.setText(text);
}
}
Im ALWAYS getting a NullPointerException on the line where I try to set the text to the button.
Declare an interface in Fragment, and implement the interface in the activity.
Call the interface through callback in Fragment when button is clicked.
You can have a public function in Fragment to update the TextView, so activity directly call the function to update the text.
Something like this
public class FragmentB extends Fragment implements onClickListener{
ClickOnB listener;
public void setOnFragmentBClickListener(ClickOnB listener){
this.listener = listener;
}
#Override
public void onClick(View v){
//stringMessage is a `String` you will pass to the activity to update its `TextView`
listener.onClickOnB(stringMessage);
}
interface ClickOnB{
public void onClickOnB(String message);
}
}
and the activity
public class MainActivity extends Activity implements ClickOnB{
#Override
protected onCreate(Bundle savedInstanceState){
//Get a reference of `Fragment` B somewhere in your code after you added it dynamically and set the listener.
((FragmentB)getFragmentManager().findFragmentByTag("FragmentB")).setOnFragmentBClickListener(this);
}
#Override
public void onClickOnB(String message){
//Set the text to the `TextView` here (I am assuming you get a reference of the `TextView` in onCreate() after inflating your layout.
mTextView.setText(message);
}
}
for more details:
update TextView in fragment A when clicking button in fragment B
Try this code-
getSupportFragmentManager().beginTransaction().replace(R.id.myProfileActionButtonsHolder, new MyProfileActionButtonsFragment(),"your_tag").commit();
MyProfileActionButtonsFragment fragment = getSupportFragmentManager().findFragmentByTag("your_tag");
if(null!=fragment){
fragment.setBMyProfileSettingsText("asd");
}
hope this works.
Related
I have a rather simple screen that only has 4 buttons. I'm implementing it as a Fragment like so:
public class MainFragment extends Fragment implements View.OnClickListener {
// ...
#Override
public void onClick(View view) {}
}
Each button already has onClick specified to a function in the Activity that the Fragment is attached. The issue I'm having is that the onClick functions aren't called when the buttons are clicked. I've left MainFragment.onClick() empty - but is that the right approach? Does it need to be implemented for the functions to be invoked? If so, the onClick attributes in the Button layouts would seem redundant.
Any help will be appreciated.
Thanks
The right approach is to use a fragment listener to communicate back with the activity:
public static class MainActivity extends Activity
implements MainFragment.onFragmentInteraction{
...
public void onFragmentInteraction() {
// Do something
callFunction();
}
}
Then in your fragment:
mYourButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
if (mListener != null) {
mListener.onFragmentInteraction();
}
}
});
FWIW I never use the xml onClick attributes. Although they may save a couple of lines of typing, they make it more difficult to follow what's happening in your code.
If your class implements View.OnClickListener and you have correctly overriden the onClick method (which it looks like you have), then you can safely remove any onClicks in your layout files and instead assign methods to your widget clicks in the following way:
public class MainFragment extends Fragment implements View.OnClickListener {
private Button viewOne, viewTwo, viewThree;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.your_layout, container, false);
viewOne = (Button) rootView.findViewById(R.id.view_one);
viewTwo = //etc...
//"this" refers to the current object. As the object is of a class that implements OnClickListener,
//passing "this" satisfies the View.OnClickListener parameter required for the setOnClickListener() method.
viewOne.setOnClickListener(this);
viewTwo.setOnClickListener(this);
viewThree.setOnClickListener(this);
return rootView;
}
#Override
public void onClick(View view) {
//To identify the correct widget, use the getId() method on the view argument
int id = view.getId();
switch (id) {
case R.id.view_one:
//viewOne clicked
break;
case R.id.view_two:
//And so on...
}
}
}
If you set the onClick in your XML, the click events will go to your container Activity. But you can have the click events go directly to your Fragment by setting the onClickListener to your Fragment's implementation of it. So in your Fragment's onCreateView() method, you would inflate your layout, then set the Button's onClickListener to your Fragment's implementation like this...
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.your_fragment, container, false);
Button button = (Button) view.findViewById(R.id.your_button);
button.setOnClickListener(this);
return view;
}
By setting the setOnClickListener() to this, you are sending all click events for that button to your Fragment instead of your Activity. Then you would just handle your onClick events as you're already doing...
#Override
public void onClick(View view) {
Log.d("YOUR BUTTON", "This is called from your Fragment instead of your Activity");
}
I have 2 Fragments - ButtonFragment and ListViewFragment - in my Activity MainActivity.
ButtonFragment contains a Button, ListViewFragment contains a ListView.
Each time I click on the ButtonFragment Button I want the ListViewFragment to show/hide.
How do I code this properly?
Currently my code looks like this:
MainActivity.java
public class MainActivity extends Activity implements Communicator {
ButtonFragment buttonFrag;
ListViewFragment listviewFrag;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonFrag= new ButtonFragment();
listviewFrag = new ListViewFragment();
manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.button_fragment, buttonFrag, "Fragment1");
transaction.add(R.id.listview_fragment, listviewFrag, "Fragment2");
transaction.commit();
}
}
ButtonFragment.java
public class DynamicButtonsFragment extends Fragment implements View.OnClickListener {
Button btn;
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.button_fragment_layout, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onClick(View v) {
//?? hide listview fragment from here ??
}
}
ListViewFragment.java
public class ListViewFragment1 extends Fragment {
protected ArrayAdapter<String> adapter1;
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.list_view_fragment, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
}
So my question is where do I implement the showing/hiding of ListViewFragment? I feel like I should send data to the MainActivity through the onClick method of ButtonFragment. But I do not know how to do so.
Or do I only add code in the MainActivity since the MainActivity has access to all the Fragments?
I am having trouble becase the Button is in a Fragment, not part of the MainActivity. I haven't really seen cases like this...
Can someone please help?
You cannot show/hide a Fragment directly. You may show/hide a UI object like Listview. If you like, you can show/hide Fragment indirectly by using the FragmentTransaction, and you can call its method add, remove or replace.
A link for sample code is Fragments
Do this ..
android.app.Fragment fragment = getActivity().getFragmentManager().findFragmentByTag("YOUR_FRAGMENT_TAG");
getActivity().getFragmentManager().beginTransaction().hide(fragment);
inside your click event!
One more thing when you add fragments like this..
transaction.add(R.id.button_fragment, buttonFrag, "Fragment1");
transaction.add(R.id.listview_fragment, listviewFrag, "Fragment2");
you're expected to provide the container id instead of the id of the fragment.
Example: For MainActivity container use R.id.containerMain
If you in fragment want to do some MainActivity function , you can try
#Override
public void onClick(View v) {
//?? hide listview fragment from here ??
((MainActivity)getActivity()).hidelistView();
//hidelistView you should imp in your MainActivity
}
If you have fragments within the same layout, you can use the following code:
http://www.java2s.com/Code/Android/Core-Class/Demonstrationofhidingandshowingfragments.htm
If not, than you can use several possibilities...
You can use an Intent to send data to MainActivity.
You can have a singleton instance where you store pointer to your MainActivity.
You can also use Handler to send messages, but the ways discribed above are easier to implement and should be enough for you.
What I ought to do is change the view/layout of Fragment without creating another class for fragment on click of a button.
For example I have an activity - ContactsActivity and I have a fragment - ContactsFragment.
The Standard way of using Fragments:
From ContactsActivity I call ContactsFragment by -
getFragmentManager().beginTransaction().replace(android.R.id.content, new ContactsFragment())
.commit();
Code for setting View in ContactsFragment class -
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.contacts_primary, container, false);
return rootView;
}
**Now comes how I do what I want to do ** (Change the view of fragment)
I change only the view of ContactsFragment by doing a bad kind of hack.
I change the onCreateView() shown above to this -
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
//Set the view to R.layout.contacts_primary
rootView = inflater.inflate(R.layout.contacts_primary, container, false);
//Set the view to R.layout.contacts_secondary
if(getActivity().getIntent()!=null && getActivity().getIntent().getBooleanExtra("s", false)) {
rootView = inflater.inflate(R.layout.contacts_secondary, container, false);
Log.e(tag,getActivity().getIntent().getExtras().toString());
return rootView;
}
//This is the onClickListener which again calls the ContactsActivity class,
//this time with an Intent which I used above to change the view from
//R.layout.contacts_primary to R.layout.contacts_secondary
Button button = (Button)rootView.findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getActivity(), ContactsActivity.class).putExtra("s",true));
}
});
Now everything works as I want and flawlessly.
But I have a very strong feeling that either all of it is wrong and Fragments aren't supposed to work this way or I am using a hectic hack to achieve what can be done by few lines of code.
So please let me know what is it? And if there is a standard way of doing what I am trying to do.
For me passing additional argument on which base fragment decides wich layout to use seems totally ok. But there is cleaner way of doing what you want to achieve without starting another activity.
First of all pass argument to fragment by making standard static new instance method in fragment (we cannot pass this argument in constructor as android always recreates fragments using empty constructor). Something like this:
public static ContactsFragment newInstance(boolan firstView) {
ContactsFragment fragment = new ContactsFragment();
Bundle args = new Bundle();
args.putBoolean("yourArg", firstView);
fragment.setArguments(args);
return fragment;
}
Every time you have to initiate your fragment do this with this method.
Then declare interface in your fragment to communicate with your activity. Like this
public interface NewViewListener {
public void showNewView(boolen firstView);
}
Than make your activity implement it so your activity han a method where it can place new fragment in container view. In your fragments onAttach and onDetach methodsmake sure your activity implements this interface and hold reference to your activity in private NewViewListener field in your fragment. Like this:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (NewViewListener ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement NewViewListener ");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
Then in on button click method call showNewView method on your activity with whatever argument you want indicating which view you want in new fragment instance. And in your activity method showNewVew fragment in the container. Like this:
#Override
public void showNewView(boolean firstView) {
getFragmentManager().beginTransaction().replace(android.R.id.content, ContactsFragment.newInstance(firstView)
.commit();
}
In your fragments onCreateView you may get passed arguments and decide which view you want to use.
I have a class file MainActivity and i want switch to Testing.class file. So this is following code.
public static class MyFragment extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.xyz, container, false);
final TextView tt=(TextView) rootView.findViewById(R.id.txt);
Button button=(Button) rootView.findViewById(R.id.btn1);
View.OnClickListener sx = new OnClickListener()
{
public void onClick(View v)
{
call();
}
};
button.setOnClickListener(sx);
return rootView;
}
private void call()
{
// Here getting the Error //
Intent intent = new Intent(MainActivity.this, Testing.class);
startActivity(intent);
}
}
So, this is the above code , i want to switch to new class file called Testing but
i am facing an error-: No enclosing instance of the type MainActivity is accessible in scope
I think creating an new static class called MyFragment within MainActivity creating problem.
So, please help me out and give me some good solution.
Okay so you cannot call MainActivity.this since you are in the fragment class. You have to access the context in a different way. Such as
getActivity()
which returns the activity associated with the fragment. The activity is a context (Since Activity extends Context)
You can use getActivity() method inside your fragment but take care that this method might return null in case of calling it before getting onActivityCreated() method
I have a fragment which has a TextView, an EditText and a Button. I also have 2 activities which include this fragment and at onClick of the button in one of the activities, the other is started. Via the intent, the text in the edittext is passed which becomes the text of the textview of the other activity.
I had two design decisions to choose from
Create two such fragments classes with appropriate methods that construct the appropriate intents. Access the UI elements from inside the respective fragment object and start the activities.
Create only one fragment class. onClick the, event is passed down to a particular method in the activities (both the activities have this method) and the activities have the logic to build the intent and start the other activity
Consider what would happen if there are 100 such activities. The first method would have us write 100 different fragment classes with custom methods, but in the second method, it is a single class and the activities have the custom logic in a particularly named method.
Therefore I chose to go with the second choice and I realized that the UI elements could not be instantiated in the onCreate method of activity as the fragment's layout is not inflated yet. I am doing the instantiation in onStart as a workaround.
Is that bad practice or is there a better design pattern to follow?
The recommended pattern is to create a holder interface which any activity that wants to instantiate your fragment must implement. Also to set data for views in your new fragment then create a newInstance() factory method on your fragment.
I tend to approach it like this;
class FooFragment implements Fragment {
private static final String TEXT_FOR_TEXTVIEW = "textForTextView";
private FooFragmentHolder mHolder;
/*
* Rather than creating your fragment in your layout directly
* you should instead instantiate it using this class in your
* activity.
*/
public static FooFragment newInstance(String text) {
Bundle data = new Bundle();
data.putString(TEXT_FOR_TEXTVIEW, text);
FooFragment fooFragment = new FooFragment();
fooFragment.setArguments(data);
return fooFragment;
}
public interface FooFragmentHolder {
public void buttonPressed(String editTextContent);
}
/*
* When we create the fragment with the activity we use onAttach to get
* our holder implementation (the activity)
*/
#Override
public void onAttach(Activity activity) {
if (activity instanceof FooFragmentHolder) {
mHolder = (FooFragmentHolder) activity;
} else {
throw new IllegalStateException("Containing activity must implement FooFragmentHolder");
}
}
#Override
public void onCreateView(Inflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_foo, container, false);
final EditText editText = (EditText) view.findViewById(R.id.edit_text);
Button button = (Button) view.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(Button button) {
mHolder.buttonPressed(editText.getText());
}
})};
TextView textView = (TextView) view.findViewById(R.id.text_view);
Bundle args = getArguments();
if (args != null) {
textView.setText(args.getString(TEXT_FOR_TEXTVIEW));
}
return view;
}
}
Now in your activity you just need to implement the FooFragmentHolder interface and use the newInstance method we created;
class FooActivity extends Activity implements FooFragment.FooFragmentHolder {
private static final String TEXT_FOR_TEXTVIEW = "textForTextView";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentLayout(R.layout.activity_foo);
// Instead of creating your fragment in your layout, create a holder
// layout and attach a new instance of your fragment using a fragment
// transaction.
FooFragment fooFragment = FooFragment.newInstance(getIntent().getStringExtra(TEXT_FOR_TEXTVIEW));
getFragmentManager().beginTransaction()
.replace(R.id.content, fooFragment)
.commit();
}
#Override
public void buttonPressed(String editTextContent) {
// In this case just starting the next FooActivity, but logic could be
// applied for any other activity.
Intent intent = new Intent(this, FooActivity.class)
.putExtra(TEXT_FOR_TEXTVIEW, editTextContent);
startActivity(intent);
}
}
I decided to settle with the following patter --
Any activity which includes this fragment should implement an interface like
public interface ViewsCreatedListener {
public void onViewsCreated();
}
The activity would then look like
public class ExampleActivity extends Activity implements ViewsCreatedListener {
.
.
.
.
#Override
public void onViewsCreated() {
//Initiate the views here and do what gotta be done
}
}
The fragment should check that any activity that includes this fragment should implement that interface using the onAttach method and onActivityCreated, the activity is notified
public class ExampleFragment extends Fragment {
ViewsCreatedListener listener = null;
.
.
.
.
#Override
public onAttach(Activity activity) {
super.onAttach(activity);
try {
listener = (ViewsCreatedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement ViewsCreatedListener");
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listener.onViewsCreated();
}
}
Doing this way, the fragment just provides the UI and the including activities decide as to what should be done with the UI elements included via the fragment. This maximizes reusability.. DRY... :-D