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");
}
Related
I have a FAB in my activity_main and I have 5 ViewPager fragments.Fragments have RecyclerView. How do I access this RecyclerView from Main activity and set on click method for FAB so that on clicking FAB, recyclerview in the active fragment scrolls to top.I tried using mRecyclerView.scrollToPosition(0) inside fragment.But it doesn't work for all fragments.I have this inside fragment.Should I place the following in main activity.java? How to access RecyclerView and scroll it to the position 0 on clicking FAB in a fragment?
public void setFloatingActionButton(){
fab = (android.support.design.widget.FloatingActionButton) getActivity().findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mRecyclerView.scrollToPosition(0);
}
});
}
Simplest and easiest solution is to use EventBus.
Two best EventBus libraries for Android are EventBus and Otto. I use otto.
All you need to do is register the bus in your fragment where recycle view is placed. And write a method e.g
public void scrollToStart(){
//body here.
}
Subscribe the above method to the bus like this.
#Subscribe // subscribing an event to the bus.
public void scrollToStart(ScrollToStartEvent event){ //you need to create an event class e.g ScrollToStartEvent
//body here.
}
Now FAB onClick Listner.
public void setFloatingActionButton(){
fab = (android.support.design.widget.FloatingActionButton)
getActivity().findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
BUS.post(new ScrollToStartEvent()); //NOTICE
}
});
}
Note : All I did is posted an event, in your case ScrollToStartEvent(). Bus will check if any method is subscribed to this event and will call that particular method. You've to read EventBus documentation to understand this correctly.
Pros : Much less code and more readable code. Complex objects can be passed through (no object serialization is required).
Cons : You'll need to learn how to use EventBus.
I hope it helps you.
I'm not sure if this will help but I set up the FAB onClick in the activity and here is how I did it. I have conditional actions based on which fragment position is being viewed.
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.fab:
if(viewPager.getCurrentItem()==0){
Toast.makeText(this,"First",Toast.LENGTH_LONG).show();
}
else{
Toast.makeText(this,"Second",Toast.LENGTH_LONG).show();
}
}
}
In my example I only have 2 fragments so I hard coded the 0 and did an if else. In your case I would use a switch within the switch. Also a good way to organize all the fragments would be with a private SparseArray<WeakReference<Fragment>> fragmentMap = new SparseArray<WeakReference<Fragment>>();
you would include this:
#Override
public void onAttachFragment(Fragment fragment) {
Bundle bundle = fragment.getArguments();
if(bundle!=null && bundle.containsKey(KEY_FRAGMENT_POSITION)){
int position = bundle.getInt(KEY_FRAGMENT_POSITION);
fragmentMap.put(position, new WeakReference<Fragment>(fragment));
}
super.onAttachFragment(fragment);
}
public Fragment getFragment(int type){
WeakReference<Fragment> weakFrag = fragmentMap.get(type);
Fragment frag = null;
if (weakFrag != null)
frag = weakFrag.get();
return frag;
}
Sometimes when an Activity event gets lost in the chain due to the way that XML or events are structured the bellow is the best structure to ensure the events go to the right places.
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
#Override
public void onClick(View v) {
Log.v(LOG_HEADER,"onClick");
}
}
public class SearchFragment extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
LinearLayout ll = (LinearLayout )inflater.inflate(R.layout.fragment_search, container, false);
ll.setOnClickListener((MainActivity)getActivity());
return ll;
}
}
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.
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.
I am porting Activity-type app to Fragments-type app. I am a bit confused where I am supposed to initiate UI elements of the fragment.
For example, if I initiate a button from Fragment class in FragmentActivity class, I get no error.
btnOne = (Button) findViewById(R.id.btn_one);
But, if I try to initiate onClick listener, then I get error of type java.lang.NullPointerException.
Then again, I cannot find method findViewById in the Fragment class.
Am I really forced to initiate in FragmentActivity and to specify listeners in Fragment?
You initiate your button in the fragment usually. An example would be:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View fragmentView = inflater.inflate(R.layout.yourlayout, container, false);
Button yourbutton = (Button) fragmentView.findViewById(R.id.button);
yourbutton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
//Do your thing
}
}
return fragmentView;
}
Let's say I have this button:
<Button
android:id="#+id/idone"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="D2"
android:onClick="myMeth"/>
I have several times used this to call methods from a layout xml as it calls the method from the activity that inflated such view.
Recently with DialogFragments, well it does not work at all. I keep getting an error telling me that such method does not exist. Where is it then looking for such method? I have added it to the DialogFragment class:
public class myActivity extends DialogFragment {
public DiceDialog() {
// empty constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.myDialog, container);
getDialog().setTitle("Hello");
return view;
}
public void myMeth(View view) {
//...
}
As well as in the activity that instantiates the FragmentManager and calls the dialog:
public Class MainActiviry Extends FragmentActivity {
//...
public void onCreate(Bundle savedInstanceState) {
// ..
FragmentManager fm = getSupportFragmentManager();
MyActivity dialog = new AddDiceDialog();
dialog.show(fm, "tag");
}
public void myMeth(View view){
//...
}
And still the messag is that MyMeth is not found.
I have already read that using interfaces and listeners is the correct way to communicate between activity and dialog fragments, but what I am trying to figure out here is where that myMeth call is being made, because well,it is called.
You can implement public myMeth(View view) in your Activity, which will then check for the currently visible Fragment, and call its method.
If you want to use more then one callable method in your Fragment, you can utilize the id's of the calling views and implement a switch, calling a different fragment method according to the id of the View.