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;
}
}
Related
I have an activity with two fragment and want to be executed first fragment when its back from second fragment using back button. And i am using the add() when navigating first fragment to second fragment. Here is my scenario and code snippet:
First fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.one_fragment, container, false);
final Button button = (Button) view.findViewById(R.id.buttonChange);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
buttonClicked(v);
}
});
return view;
}
public void buttonClicked(View view) {
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment_Container, new TwoFragment());
fragmentTransaction.addToBackStack("sdfsf");
fragmentTransaction.commit();
}
Moving to Second fragment and here is the code:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.two_fragment, container, false);
return view;
}
The problem is that, When I am navigating from first to second fragment and then back again in the first fragment using back button first fragment lifecycle method is not executing. Instead of using add() if I use replace() then lifecycle method are executing properly. I know its the difference between add() and replace() but I want to use add() and also want to have navigation callback to handle some logic when I back in the first fragment using back button.
Also tried below code:
fragmentManager.addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
Log.e(TAG, "onBackStackChanged: ");
// Update your UI here.
}
});
But its also calling multiple times and creating anomalies.
How can I handle this? Specially handle some logic in first fragment when I back from second fragment.
The easiest way I can think of is to set result when you're done with the second fragment that essentially tells the first fragment to "resume" via its onActivityResult method.
When you create an instance of Fragment B, call #setTargetFragment() and pass in Fragment A as your target fragment. Then when Fragment B is done and going to return to Fragment A, before it exits, you will set the result of it for Fragment A by calling:
getTargetFragment().onActivityResult(getTargetRequestCode(), RESULT_FRAGMENT_B_FINISHED,null)
///// horizontal scroll padding
Note that RESULT_FRAGMENT_B_FINISHED would be some static integer you define somewhere, like
public static final int RESULT_FRAGMENT_B_FINISHED = 123123;
Now in Fragment A all you need to do is override onActivityResult and check that the request code matches the request code integer from setTargetFragment and the result code also matches RESULT_FRAGMENT_B_FINISHED, if so you can run the code that would have been fired from onResume().
#getTargetFragment()
#onActivityResult()
#getTargetRequestCode()
Instead of passing data between the two fragments I recommend you to use a SharedViewModel.
The idea is that the first fragment observe some data for changes and the second one edit this data.
Example:
Shared ViewModel
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new
MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
First fragment
public class FirstFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model =
ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
Second fragment
public class SecondFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model =
ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.select(new Item("value 1","value 2");
}
}
You can read about ViewModels, LiveData and Architecture components starting from here: https://developer.android.com/topic/libraries/architecture/viewmodel#java
This answer is assuming that you want to execute some logic based on some data change. If it's not the case, you can explain what kind of logic do you want to execute and I will edit my answer.
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 two static fragments in same activity, in "fragmentA" i have a customized list, when an item is clicked must to appear a detail in "fragmentB", detail appear only when i change screen orientation, no automatically. I use this code in main activity for refresh but application restart(detail appear).
finish();
startActivity(getIntent());
Someone knows a better way to make appear detail automatically in "fragmentB" when i clicked some item from "fragmentA", always using two static fragments in same activity.
Don't use static references to hold a Fragment, it's a really bad practice.
Don't store the Context in a static reference. Or you could will leak memory.
Instead, implement an Interface:
//FragmentActivityTest
public class FragmentActivityTest extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentB fragmentB = new FragmentB();
FragmentA fragmentA = new FragmentA();
fragmentA.setFragmentBHandler(fragmentB);
//Perform transactions etc
}
}
//FragmentA
public class FragmentA extends Fragment {
private FragmentBHandler _handler;
public void setFragmentBHandler(FragmentBHandler handler) {
_handler = handler;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((ListView) getView().findViewById(R.id.list_view)).setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
_handler.updateDetail();
}
});
}
}
//FragmentB
public class FragmentB extends Fragment implements FragmentBHandler {
#Override
public void updateDetail() {
//do your work
}
}
You should use an event bus like greenrobot or otto. FragmentB subscribe to an event, and FragmentA post that event. When you click on an item, you'll send an event, and the subscriber will execute your action (show details).
Without showing code, I can only guess your current implementation.
The proper way to communicate between fragments is
Pass data to the parent activity from Fragment A on item click,
Activity passes this data to fragment B by finding the fragment in fragment manager and call a method in fragment B,
That method in fragment B should determine if it should populate the detail.
I've recently tried to use an interface for fragment-activity communication. The idea is that when a button is pressed in a fragment, it retrieves data from an EditText in the same fragment, it then sends the string to the MainActivty - this controls all my fragments - which then starts another fragment and delivers the string to this fragment for use later, however, I'm having trouble initially setting up the first interface which sends the data. Unfortunately nothing happens, and I cannot therefore get to the next fragment which should be displayed. Additionally I have tried using getActivity() but it cannot find the associated method within the fragment, leading me to believe that the fragments somehow aren't directly connected to MainActivity (I've only just grasped basics of Java and a little of Android, just learning.)
I've listed the relevant information below, thanks for the assistance!
Fragment
public class CreateWorkoutFragment extends Fragment implements OnClickListener {
View rootViewCreateWorkoutFragment;
EditText editTextWorkoutName;
// Using an ImageView for custom button
ImageView buttonNext;
String valueCreateWorkoutEditText;
OnDataPass dataPasser;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootViewCreateWorkoutFragment = inflater.inflate(R.layout.fragment_create_workout, container, false);
buttonNext = (ImageView) rootViewCreateWorkoutFragment.findViewById(R.id.button_workout_name_next);
editTextWorkoutName = (EditText) rootViewCreateWorkoutFragment.findViewById(R.id.edit_text_workout_name);
buttonNext.setOnClickListener(this);
return rootViewCreateWorkoutFragment;
}
public interface OnDataPass {
public void onDataPass(String data);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
dataPasser = (OnDataPass) activity;
}
public void passData(String data) {
dataPasser.onDataPass(data);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button_workout_name_next:
valueCreateWorkoutEditText = editTextWorkoutName.getText().toString();
passData(valueCreateWorkoutEditText);
break;
}
}
}
Main Activity
public class MainActivity extends FragmentActivity implements OnClickListener, CreateWorkoutFragment.OnDataPass {
ImageView buttonCreate;
Fragment newFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.AppThemeBlue);
setContentView(R.layout.activity_main);
buttonCreate = (ImageView) findViewById(R.id.create_foreground);
buttonCreate.setOnClickListener(this);
FragmentManager fragManager = getSupportFragmentManager();
FragmentTransaction tranManager = fragManager.beginTransaction();
CreateWorkoutFragment createWorkoutFrag = new CreateWorkoutFragment();
// fragment_change is just the area in XML where fragments switch
tranManager.add(R.id.fragment_change, createWorkoutFrag);
tranManager.commit();
newFragment = null;
}
#Override
public void onDataPass(String data) {
// CreateFragment is not to be confused with CreateWorkoutFragment
// CreateFragment is the fragment I'm trying to start when any strings
// are obtained from CreateWorkoutFragment
newFragment = new CreateFragment();
}
#Override
public void onClick(View v) {
switch (v.getId()) {
// create_foreground is just an ImageView used as a button
// Additionaly, other buttons are used to create other fragments,
// I've cut them out currently as they are not nessesary which is
// why CreateWorkoutFragment is only button and default currently
case R.id.create_foreground:
newFragment = new CreateWorkoutFragment();
break;
default:
newFragment = new CreateWorkoutFragment();
}
FragmentTransaction tranManager = getSupportFragmentManager().beginTransaction();
tranManager.replace(R.id.fragment_change, newFragment);
tranManager.addToBackStack(null);
tranManager.commit();
}
}
Sorry the code isn't exactly tidy, however, it was the most relevant code cut out from a large class. As I said, I have tried other methods yet cannot get any response from MainActivity either way. Thanks in advance!
Just before I posted: Got the app to write logcat messages to me, it manages to pass the data when the button is clicked - at least I think, and is something to do with the fragment not starting! At MainActivity>onDataPass()>new Fragment = new CreateFragment() Any ideas? As mentioned before, other buttons do exist and manage to change the fragment. However, were cutout to reduce amount of code posted.
getActivity() but it cannot find the associated method within the fragment
This is because getActivity() returns an Activity, not a MainActivity which is your custom subclass. You can easily fix this with a cast. For example, in your fragment, you can do this:
OnDataPass main = (OnDataPass) getActivity();
main.onDataPass(message);
Since such a cast is required, the interface seems to get in the way in my opinion. You can just as easily cast directly to MainActivity:
MainActivity main = (MainActivity) getActivity();
main.onDataPass(message);
I have a pager that contains three fragments
adapter.addFragment (new PlainColorFragment (Color.red));
adapter.addFragment (new PlainColorFragment (Color.green));
adapter.addFragment (new PlainColorFragment (Color.blue));
My question is whether it is possible to detect that fragmentation has focus or is being displayed to the user.
For example, when the green fragment is the one on screen or has focus, show a "toast" on the screen
I hope I have explained my question correctly.
thanks
Simple:
greenFragment.isVisible();
If you're looking for some kind of event, you would have to manage that manually wherever your fragment switching happens, or in your fragment class, you could execute your code in the fragment's OnHiddenChanged event (double checking, of course, that it is currently visible)
You could set an OnPageChangedListener to your ViewPager and show a different toast depending on the position.
You can create an Interface, implementing it in your Fragment and then, on parent activity, you can implement BackStackChangedListener as in example below:
public interface MyFragmentOnScreen {
public void onActiveFragment();
}
public class MyFragment extends Fragment implements MyFragmentOnScreen{
[...]
#Override
public void onActiveFragment() {
//Things you should do when your fragment becomes active
}
}
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
[...]
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
// Update your UI here.
Log.v(MainActivity.TAG, "Backstack changed");
if (getSupportFragmentManager().findFragmentById(R.id.frMain) instanceof MyFragmentOnScreen) {
((MyFragmentOnScreen) getSupportFragmentManager().findFragmentById(R.id.frMain)).onActiveFragment();
}
}
}
});
}
}
where frMain is the holder in MainActivity layout for your Fragment.