What context Should be Use in setUserVisibleHint Method - android

Because All fragment loaded with together In Pager Sliding tab , I need to use setUserVisibleHint() for laoding that fragment selected .
I have Problem with Context in the setUserVisibleHint() method :
It raised NPE Exception when I use getActivity for my Context. Thanks

Because getActivity will return null before fragment attached to Activity you need check this value will null or check fragment attached to activity or after onActivityActtached function.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (getActivity() != null) {
//do something
}
}
//or
boolean isAttached = false;
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
isAttached = true;
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isAttached) {
//do something
}
}

Its better to use newInstance Method and put your String over there. And then in fragment use
getArguments().getString("KEY")

Related

Recyclerview adapter not working inside onResume method of fragment in Viewpager

I am trying to listen to a click event inside my onResume method of fragment. I have two fragments in my viewpager and if I use onResume in both the fragments only one of them is working other is not working. Below is what I am doing.
Fragment 1
public void onResume() {
super.onResume();
adapter.setOnItemClickListener(new catAdapterBlack.MyClickListener() {
#Override
public void onItemClick(String position, int pos, int posi, View v) {
... doing some stuff...
}
});
subAdapter.setOnItemClickListener(new subcatAdapter.MyClickListener() {
#Override
public void onItemClick(String position, int pos, View v) {
... doing some stuff...
});
}
Fragment 2
public void onResume() {
super.onResume();
adapter.setOnItemClickListener(new catAdapterBlack.MyClickListener() {
#Override
public void onItemClick(String position, int pos, int posi, View v) {
... doing some stuff...
}
});
subAdapter.setOnItemClickListener(new subcatAdapter.MyClickListener() {
#Override
public void onItemClick(String position, int pos, View v) {
... doing some stuff...
});
}
I am using the same adapters in both the fragments.
Now when i searched here i found this solution below but it's also not working. When i open my first fragment the app crashes with error calling MyClickListener on a null object reference.So far i have tried every method. I tried calling onresume in first Fragment and the below (setUserVisibleHint)solution in fragment two. but again its not working while the app is not crashing this way. I want to call the Myclicklistener inside both the fragments.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser) {
init();
} else {
}
}
Move your code from onResume in onCreateView after you set the adapters.
This will work:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser) {
// Here you need to check if your adapter is not null and initialize your adapters again
} else {
}
}
All you need is to check if your adapter is not null you may need to initialize your adapter again.

Unable to get visible/live Fragment

I am stuck in this problem.
I want to maintain live instance of Fragment in application context. What I did is
1st Attempt
public class BaseProjectFragmentParent extends BaseProjectFragment {
#Override
public void onResume() {
super.onResume();
App.getInstance().setLiveFragment(this);
}
#Override
public void onPause() {
super.onPause();
App.getInstance().setLiveFragment(null);
}
}
and I extend all fragments by this base class. I expected live instance to be there in Application class.
Problem is in ViewPager, I have two Fragment in ViewPager where both Fragment's onResume() is called and live instance is set the second Fragment. I am working on some common code so I don't want to use getCurrentItem().
Second Attempt
Next I tried to get this Live Fragment by getSupportFragmentManager().getFragments()
isVisible() is true for both Fragments. (Not useful)
isResumed() is true for both Fragments. (Not useful)
You can use
setUserVisibleHint(boolean isVisibleToUser)
In fragments. This flag is set by the OS when a fragment becomes visible:
class MyFragment extends Fragment {
public MyFragment() {
setUserVisibleHint(false) //This is needed because by default its true
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser)
//true if fragment is visible
}
}
However this wont get called inside Viewpager. You have to add a listener to the viewpager and then set your active page manually:
viewPager.addOnPageChangeListener(new OnPageChangeListener() {
public void onPageScrollStateChanged(int state) {}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
public void onPageSelected(int position) {
// Check if this is the page you want.
}
});

Method call on changing tabs in viewpager

How to call a method in a fragment when the tab is changed.
I tried using onPause() in fragment, but it is not getting invoked.
I am using ViewPager.
Thanks!
You can override setUserVisibleHint(boolean isVisibleToUser) method and use it for this purpose. isVisibleToUser will be true when the tab is changed and the fragment is selected and in other fragment which was previously visible, this method will be called with false.
EDIT:
setUserVisibleHint will be called multiple times. But in that case, you can rely on getActivity() not being null. i.e. if (isVisibleToUser && getActivity() != null)
Add ViewPager.OnPageChangeListener to ViewPager by:
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
<method call>
}
}
So whenever user swipes through pages of ViewPager, you will get callback in onPageSelected() method with position of that page and there you can call fragment's method
You can use interfaces here.
Define your interface in Activity that contains view pager
public interface MyInterface{
void callMethod();
}
initialize interface and call like this
viewPager.addOnPageChangeListener(new OnPageChangeListener() {
public void onPageScrollStateChanged(int state) {
}
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
public void onPageSelected(int position) {
myInterface.callMethod();
}
});
And implement that method in your fragment and call your method in callback.
Myfragment extends Fragment implements MyInterface{
//
#Override
void callMethod(){
//call your method here
}
}

setUserVisibleHint() throwing NullPointerException when calling getActivity()

I have create 3 Fragments which are in a ViewPager. I am trying to get data from the server only when the particular Fragment is visible to the user. For that I have called my web service method in setUserVisibleHint(). But I am get NullPointerException when I am using getActivity() inside that method. How can I make sure I that I load data only once and when its visible to the user ? Here the code:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser && !_areLecturesLoaded){
if(getActivity() == null){ //this is always null
Log.e("Fragment1","No Activity");
}else{
getLatLong();
}
_areLecturesLoaded = true;
}
}
private void getLatLong() {
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
// The next two lines tell the new client that “this” current class will handle connection stuff
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
//fourth line adds the LocationServices API endpoint from GooglePlayServices
.addApi(LocationServices.API)
.build();
}
I have verified that onAttach() method is getting called after setUserVisibleHint(). Before Activity is attached setUserVisibleHint() is firing up.
Keep a Activity reference when onAttach() is called and use it everywhere instead of getActivity()
Like this:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
try this :
boolean isVisible=false;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
isVisible=isVisibleToUser;
}
#Override
public void onAttach(Context context) {
if(visible){
// do what you must todo
}else{
//do what you must todo
}
}

setUserVisibleHint called before onCreateView in Fragment

I am working on ViewPager and using Fragment there I found
setUserVisibleHint() called before onCreateView() in Fragment
I am using Fragment from support library android.support.v4.app.Fragment
Is this is a problem with Library ?
How can I get rid of it ?
EDIT
I Override setUserVisibleHint() and not calling super to get rid of it.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
//FIXED: setUserVisibleHint() called before onCreateView() in Fragment causes NullPointerException
//super.setUserVisibleHint(isVisibleToUser);
}
// create boolean for fetching data
private boolean isViewShown = false;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (getView() != null) {
isViewShown = true;
// fetchdata() contains logic to show data when page is selected mostly asynctask to fill the data
fetchData();
} else {
isViewShown = false;
}
}
Use isViewShown instance variable to decide whether to fetch data in onCreateView() or in setUserVisibleHint().
Below code contains logic for onCreateView():
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.main_layout, container, false);
// view initialization steps.......
if (!isViewShown) {
fetchData();
}
// do other stuff
}
This code will solve your problem. As It solved my problem. :)
This trick will fetch data in onCreateView() for direct jumping from one page to another, whereas when you swipe the view it will fetch the data from setUserVisibleHint() method. :)
you can use this logic, also you can turn off viewDidAppear any time by setting isVisible = false
public class MyFragment extends Fragment {
private Boolean isStarted = false;
private Boolean isVisible = false;
#Override
public void onStart() {
super.onStart();
isStarted = true;
if (isVisible && isStarted){
viewDidAppear();
}
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
isVisible = isVisibleToUser;
if (isStarted && isVisible) {
viewDidAppear();
}
}
public void viewDidAppear() {
// your logic
}
}
I found the best solution
private boolean isVisible;
private boolean isStarted;
#Override
public void onStart() {
super.onStart();
isStarted = true;
if (isVisible)
sendRequest(); //your request method
}
#Override
public void onStop() {
super.onStop();
isStarted = false;
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
isVisible = isVisibleToUser;
if (isVisible && isStarted)
sendRequest(); //your request method
}
It's improved version of fareed namrouti's answer. I tested this on many conditions. It's safe.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment, container, false);
if (getUserVisibleHint()) {
sendRequest();
}
return view;
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
if (isResumed()){
sendRequest();
}
}
}
Below Worked for me....
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
//// create class member variable to store view
viewFrag =inflater.inflate(R.layout.fragment_main_favorite, container, false);
// Inflate the layout for this fragment
return viewFrag;
}
and use this
#Override
public void setUserVisibleHint(boolean visible)
{
super.setUserVisibleHint(visible);
if (visible)
{
View v = viewFrag ;
if (v == null) {
Toast.makeText(getActivity(), "ERROR ", Toast.LENGTH_LONG ).show();
return;
}
}
}
My SightFragment.java here, should reset the flags in onDestroyView():
package cc.cubone.turbo.core.app;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.View;
/**
* Fragment for handling view after it has been created and visible to user for the first time.
*
* <p>Specially in {#link android.support.v4.view.ViewPager}, the page will be created beforehand
* but not be visible to user.
*
* <p>Call {#link android.support.v4.view.ViewPager#setOffscreenPageLimit(int)} to set the number of
* pages that should be retained.
*
* Reference:
* <ul>
* <li><a href="http://stackoverflow.com/questions/10024739/how-to-determine-when-fragment-becomes-visible-in-viewpager">
* How to determine when Fragment becomes visible in ViewPager</a>
* </ul>
*/
public class SightFragment extends Fragment {
private boolean mUserSeen = false;
private boolean mViewCreated = false;
public SightFragment() {
}
/*public boolean isUserSeen() {
return mUserSeen;
}
public boolean isViewCreated() {
return mViewCreated;
}*/
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (!mUserSeen && isVisibleToUser) {
mUserSeen = true;
onUserFirstSight();
tryViewCreatedFirstSight();
}
onUserVisibleChanged(isVisibleToUser);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// Override this if you want to get savedInstanceState.
mViewCreated = true;
tryViewCreatedFirstSight();
}
#Override
public void onDestroyView() {
super.onDestroyView();
mViewCreated = false;
mUserSeen = false;
}
private void tryViewCreatedFirstSight() {
if (mUserSeen && mViewCreated) {
onViewCreatedFirstSight(getView());
}
}
/**
* Called when the new created view is visible to user for the first time.
*/
protected void onViewCreatedFirstSight(View view) {
// handling here
}
/**
* Called when the fragment's UI is visible to user for the first time.
*
* <p>However, the view may not be created currently if in {#link android.support.v4.view.ViewPager}.
*/
protected void onUserFirstSight() {
}
/**
* Called when the visible state to user has been changed.
*/
protected void onUserVisibleChanged(boolean visible) {
}
}
While most of this solutions work, you don't even need to track the state by yourself.
With current versions fo the support library there is a isResumed() method which does probably what most of you try to achieve by using an isStarted flag:
Return true if the fragment is in the resumed state. This is true for the duration of onResume() and onPause() as well.
And then it's as easy as:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isResumed()) {
updateUi(isVisibleToUser);
}
}
That simple variant work in my code:
#Override
public void onStart() {
super.onStart();
if (getUserVisibleHint()) {
updateUI(); // your logic
}
}
and
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isResumed() && isVisibleToUser) {
updateUI(); // your logic
}
}
Create this code in your setUserVisibleHint() :
if(isVisibleToUser && getView() != null){
isActive = true;
init();
}else if(isVisibleToUser && getView() == null){
isActive = false;
}else{
isActive = true;
}
In your onCreateView() :
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
if(!isActive){
init();
}
}
BELOW WORKED FOR ME
Please create a global view like this
private View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
//Inflate view layout
view =inflater.inflate(R.layout.your_fragment, container, false);
// return view
return view;
}
and use this
#Override
public void setUserVisibleHint(boolean isUserVisible)
{
super.setUserVisibleHint(isUserVisible);
//When fragment is visible to user and view is not null then enter here.
if (isUserVisible && view != null)
{
// do your stuff here.
}
}
The behavior that you are experiencing is specified in the documentation itself.
Note: This method may be called outside of the fragment lifecycle and thus has no ordering guarantees with regard to fragment lifecycle method calls.
Check it here.
https://developer.android.com/reference/android/app/Fragment#setUserVisibleHint(boolean)
Now, You no longer required to use this Method for FragmentPagerAdapter.
It use send true for onView Screen and false for non-View Screen, while called for ViewPager Everytime.
Since Android have release LifeCycle in AndroidX, all lifecycle methods are called for visible screen. LifeCycle dont run after onCreateView for Non-Visible Screens in PagerAdapter.
This is for:
~Depricated setuservisiblehint
is hard to maintain and listen to a state, I use the hacky method to listen to visibility
For AndroidX Fragment
Initially, I put every show transaction, I added
fragment.userVisibleHint = true
currentFragment?.userVisibleHint = false
transaction.add(fragmentContainer.id, fragment, "tag").commit()
For hide/remove transactions, I added
currentFragment.userVisibleHint = false
In My Fragment/BaseFragment:
class ExampleFragment:Fragment(){
val visibilityStateChanges = MutableLiveData<Boolean>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
visibilityStateChanges.value = true
//return your view
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
Log.e("JeyK", "isVisibleToUser $isVisibleToUser")
visibilityStateChanges.value = isVisibleToUser
}
}
}
in your Fragment, you could observe the liveData to receive visibility states
visibilityStateChanges.observe(this#MapPathFinderFragment) {
Log.e("visibilityStateChanges", "Visibility changed $it")
}
This is the best solution i found.
#Override
public void onCreateView() {
super.onStart();
if (getUserVisibilityHint()){
//do stuff
}
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isResumed() && isVisibleToUser) {
//do stuff
}
}

Categories

Resources