Load data when user stops swiping in fragmentviewpager in android? - android

Load fragment when user stops swiping instead of previous,current,next pattern in fragmentviewpager in android?
Load single fragment at a time in fragmentviewpager instead of multiple.

On Fragment Load FragmentViewPager sets setUserVisibleHint to true of current fragment. Through that we can put delay in loading fragment in FragmentViewPager. In Below code i have put delay of 1000 ms.
#Override
public boolean getUserVisibleHint() {
boolean isVisible = super.getUserVisibleHint();
return isVisible;
}
public Runnable handleDelays = new Runnable() {
#Override
public void run() {
if (getUserVisibleHint()) {
// Application Logic
}
}
};
private Handler pagerHandler;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (this.isVisible()) {
if (!isVisibleToUser) {
pagerHandler.removeCallbacks(handleDelays);
} else {
pagerHandler.removeCallbacks(handleDelays);
pagerHandler.postDelayed(handleDelays, 1000);
}
} else {
if (pagerHandler != null) {
if (!isVisibleToUser) {
pagerHandler.removeCallbacks(handleDelays);
} else {
pagerHandler.removeCallbacks(handleDelays);
pagerHandler.postDelayed(handleDelays, 1000);
}
} else {
pagerHandler = new Handler();
}
}
}

Related

How to use backbutton to unselect all item in gridview?

i'm using backbutton as interface from activity but it's not working properly for me because on backpress showing 0 size of arraylist
// here is the activity class from where i'm getting backbutton interface..
public class Multiple_Images extends AppCompatActivity {
#Override
public void onBackPressed() {
if(twice ==true){
Intent intent =new Intent(this,MainActivity.class);
startActivity(intent);
}ImageAdapter imageAdapter =new ImageAdapter(this);
imageAdapter.onBackPress();
Toast.makeText(this, "Press twice", Toast.LENGTH_SHORT).show();
twice =true;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
twice =false; } }, 2000); }}
//here is the adapter class here i'm using backbutton
public class ImageAdapter extends BaseAdapter implements onBackPressListener {
ArrayList<String> selectedArraylist ;
#Override
public boolean onBackPress() {
selectedArraylist.clear();
Toast.makeText(context, "All values unselected", Toast.LENGTH_SHORT).show();
return true;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
urimodel=new ArrayList<>();
final ImageView imageGrid ;
Activity activity = (Activity) context;
actionMode = activity.startActionMode(new Actionmode());
final GridModel gridModel=(GridModel) this.getItem(i);
if(view==null) {
view = LayoutInflater.from(context).inflate(R.layout.model, null);
selectedArraylist =new ArrayList<>();
}
final CardView cardView= (CardView)view.findViewById(R.id.cardview_image);
imageGrid = (ImageView) view.findViewById(R.id.grid_image);
// gridText = (TextView) view.findViewById(R.id.grid_text);
imageGrid.setScaleType(ImageView.ScaleType.CENTER_CROP);
// imageGrid.setScaleType(ImageView.ScaleType.CENTER_CROP);
Picasso.get().load(gridModel.getImage()).resize(200,200).into(imageGrid);
if (selectedArraylist.contains(gridModel.getImage_text())) {
cardView.setCardBackgroundColor(CARD_SELECTED_COLOR);
}else {
cardView.setCardBackgroundColor(Color.WHITE);
}
return view;
}
}
Simply you can do this inside onBackPressed
#Override
public void onBackPressed() {
if (twice == true) {
super.onBackPressed(); //this backs to the previous activity, if you want to stay with Intent, add finish() after startActivity()
return;
} else {
for (int i = 0; i < list.size(); i++) {
if (gridView.isItemChecked(i)) {
gridView.setItemChecked(i, false);
}
}
//selectedArraylist.clear(); this is clearing your array of selected items
}
twice = true;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
twice = false;
}
}, 2000);
}
I don't know, why did you put selectedArraylist =new ArrayList<>(); in adapter getView() method. getView() is fired every time, when a new list item is inflated, that mean every time, when you are changing adapters source, scrolling list this method is called, and every time you are initialize you array, and all data inside lost. You should treat an adapter class just like a tool for displaying items, and all actions like above make outside adapter.
pretty much easy,
I give you my own project code, hope it help you.
StudentFragment.java:
private void MultiSelected_Student(int position) {
Student data = adapter_class.getItem(position);
if (data != null) {
if (selectedIds.contains(data)) selectedIds.remove(data);
else selectedIds.add(data);
}
}
private void Remove_MultiSelected() {
try {
selectedIds.clear();
} catch (Exception e) {
e.printStackTrace();
}
}
public void Group_UnSelect() {
Remove_MultiSelected();
MultiSelected = false;
fab.setVisibility(View.GONE);
homeeActivity.studentsMultiSelect = false;
notifyy();
}
private void notifyy() {
adapter_class.notifyDataSetChanged();
}
HomeActivity.java:
public boolean studentsMultiSelect = false;
#Override
public void onBackPressed() {
if (studentsMultiSelect) {
studentFragment.Group_UnSelect();
} else {
super.onBackPressed();
}
}

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
}
}

Que multiple dialogFragments to display one after the other

If I have an ArrayList<DialogFragment> containing DialogFragments of unknown size, how can I programmatically cue up each one so that once the first one is dismissed, the next one is shown, and so on?
for (int i = 0; i < tutorialViews.size(); i++) {
final int current = i;
DialogFragment someDialogFragment = dialogFragmentList.get(i);
if (i == 0) {
someDialogFragment .show(activity.get().getSupportFragmentManager(), "some_dialog_fragment");
}
if (i + 1 != dialogFragmentList.size() - 1) {
someDialogFragment.getDialog().setOnCancelListener(new OnCancelListener() {
#Override
public void onCancel(DialogInterface arg0) {
dialogFragmentList.get(current + 1).show(activity.get().getSupportFragmentManager(), "more_dialog_fragments");
}
});
}
}
Unforunately this doesn't work since the dialog object within a dialogFragment isn't instantiated yet, giving a nullPointerException for the getDialog() call
Create your own interface to callback when the fragmentdialog is closed.
OnDialogFragHide mListener;
public interface OnDialogFragHide {
public void onFragmentDismissed();
}
public void setOnFragDismissedListener(OnDialogFragHide listener) {
mListener = listener;
}
Register the interface in the for loop
if (i == 0) {
tutorial.show(activity.get().getSupportFragmentManager(), "smoking_hawt");
}
if (i != tutorialViews.size() - 1) {
tutorial.setOnFragDismissedListener(new OnDialogFragHide() {
#Override
public void onFragmentDismissed() {
tutorialViews.get(current + 1).show(activity.get().getSupportFragmentManager(), "some_tag");
}
});
}
Call upon the listener whenever the fragment is closed (i.e. in the FragmentDialog's onDismiss() and onCancel() methods, NOT the DIALOG's onDismiss / onCancel listeners.
#Override
public void onDismiss(DialogInterface dialog) {
if (mListener != null && !dismissed) {
dismissed = true;
mListener.onFragmentDismissed();
} else {
Log.e(TAG, "DialogFragmentDismissed not set");
}
super.onDismiss(dialog);
}
#Override
public void onCancel(DialogInterface dialog) {
if (mListener != null && dismissed) {
dismissed = true;
mListener.onFragmentDismissed();
} else {
Log.e(TAG, "DialogFragmentDismissed not set");
}
super.onCancel(dialog);
}
the dismissed boolean is for good measure to make the listener isn't called twice.

Android app using cpu when app closed with back or home - animations in ViewPager

I have an app that has animations in fragments in a ViewPager. I have the ViewPager displaying on the empty option of a list fragment. The animations are NineOldAndroids ObjectAnimators combined in AnimatorSets some of which animate SVGs shown with svg-android.
When I change page on the ViewPager the animations stop using this code in the fragment:
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
if (myAnimationView != null) {
myAnimationView.restartAnimation();
}
} else {
if (myAnimationView != null) {
myAnimationView.stopAnimation();
}
}
}
And this code in the View implements ValueAnimator.AnimatorUpdateListener:
public void stopAnimation() {
endAnimCalled = true;
myAnimatorSet.end();
}
public void restartAnimation() {
endAnimCalled = false;
if (!myAnimatorSet.isStarted()) {
myAnimatorSet.start();
}
}
If pressing back to exit when not on the animation page, the app uses 1-5% CPU even after several hours. If back is pressed on the page when animating, the app uses 10-30% CPU when running in the background.
Is there a good way to pass through the fragments that onPause has been called? Any ideas why the app still uses 1-5% CPU when the animations have stopped?
I have found this impossible to replicate in an app small enough to be reasonable to post on Stack Exchange.
Making sure that the animation ends is the only solution I have found for the CPU usage. I have set it to only repeat the animation 10 times. I also considered using a BroadcastReceiver to pass a broadcast from the onPause() of the activity to call endAnimation() in the fragments.
public void startAnimation() {
createAnimation();
animRepeats = 0;
if (!myAnimatorSet.isStarted()) {
myAnimatorSet.start();
}
AnimatorListener myAnimListen = new AnimatorListener() {
#Override
public void onAnimationEnd(Animator animator) {
if (!endAnimCalled && animRepeats < 10) {
myAnimatorSet.start();
animRepeats++;
}
}
#Override public void onAnimationRepeat(Animator animator) {}
#Override public void onAnimationStart(Animator animator) {}
#Override public void onAnimationCancel(Animator animation) {}
};
myAnimatorSet.addListener(myAnimListen);
}
public void stopAnimation() {
endAnimCalled = true;
myAnimatorSet.end();
}
public void restartAnimation() {
endAnimCalled = false;
if (!myAnimatorSet.isStarted()) {
animRepeats = 0;
myAnimatorSet.start();
}
}

ListFragment: prevent race condition between getListView() and onDestroyView()

I have a ListFragment that I want to update regulary. The update process itselfs is quite complicated and could take some time. That's why I made a thread that executes a new update 5 seconds after the previous update has been done. Then I create a handler to update the list, while keeping track of the position in the list.
The problem is that by quickly sliding between fragments in my ViewPager I can force a race condition: onDestroyView() can be called before the handler calls getListView(), resulting in the following error:
java.lang.IllegalStateException: Content view not yet created
My question is: how can I prevent this race condition? Is there any way to check if the view is still there? Checking if the updateThread has been interrupted in the code below is unfortunately not enough.
public class MyFragment extends ListFragment {
private Thread updateThread = null;
public void startUpdate() {
/* Kill old thread */
if (updateThread != null) {
updateThread.interrupt();
}
final Handler handler = new Handler();
Runnable r = new Runnable() {
#Override
public void run() {
while (true) {
// ... collect data in `adapter`
final ArrayAdapter<String[]> ada = adapter;
handler.post(new Runnable() {
#Override
public void run() {
if (ada != null) {
// restore view position
int index = getListView().getFirstVisiblePosition(); // CRASH here
View v = getListView().getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
setListAdapter(ada);
ada.notifyDataSetChanged();
getListView().setSelectionFromTop(index, top);
}
}
});
//... sleep for some second
}
}
};
updateThread = new Thread(r);
updateThread.start(); // start updating
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
startUpdate();
} else {
if (updateThread != null) {
updateThread.interrupt();
}
}
}
}
Try to use isAdded to check if you fragment is attached to activity

Categories

Resources