问题描述:

My app works with a long form which I decided to divide in multiple Fragments in a ViewPager. When you press the "save" option, the validation process starts.

Basically the validation is that some EditTexts are not empty. I'm looping through all Fragments in the ViewPager check if all fields has valid values.

// Inside Fragment

public boolean areFieldsValid() {

return !mEditText.getText().toString().isEmpty()

}

public void showErrors() {

mEditText.setError("cannot be blank");

}

If a field inside a Fragment is not valid, then viewPager.setCurrentItem(position, true); and fragment.showErrors() are called to go to that Fragment and show the user the error.

The problem comes when onCreateView() hasn't been called on the Fragment that has the error.

This happens either because you haven't navigated to that Fragment yet (supposing the user's on fragment1, error is on fragment7 and the user pressed "save" while on fragment1) or because the user rotated the device and all views are destroyed on every Fragment.

This problem/issue is not only that mEditText would be null, but also that the Fragment saved its state, so it might not even been blank. In other words, the following code is not an option, because even if the pointer is null, it might not be empty.

// Inside Fragment

public boolean areFieldsValid() {

return mEditText != null && !mEditText.getText().toString()isEmpty();

}

At this point I'm wondering if my architecture is wrong. I decided to go with ViewPager cause the form is really long, and I've been passing data from Fragment to Activity through callbacks.

Given the above settings, how can I validate fields and show the user which field is the one with the error?

网友答案:

You can't just assume that UI components will be there anytime you want. That fragment might be gone, killed or worse, destroyed without saving it's instance state.

What I offer is to save data on database and check if everything is set on save button click event. This can be done using ContentProviders and SQLiteDatabase. As Virgil Said in here "Persist more, persist often."

网友答案:

I have implemented a similar thing, but my approach is to go fragment by fragment. Hope this helps.

I add an interface,

public interface AddActionInterface {
    public void onAddButtonClicked(); 
}

I created a base fragment which implements this interface as,

public abstract class BaseFragment extends Fragment implements AddActionInterface {

  @Override
  public void onAddButtonClicked() {
    if (isAdded() && isVisible()) {
        executeAction();
    }
  }

  protected abstract void executeAction();

}

Then we will call our Interface object like this in the activity. Create a List like below,

List<AddActionInterface> listeners = new ArrayList<AddActionInterface>();

and add your fragment to the list inside the view pager as,

listeners.add(fragment);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment, tag).commit();

Simply call the below in the onOptionsItemSelected method.

if (item.getItemId() == R.id.action_add) {          
for (AddActionInterface listener : listeners) {             
   listener.onAddButtonClicked();
}
}

What the above method does is calls the onAddButtonClicked() method which is implemented in the BaseFragment.

Trick here is that every time the button in the action bar is clicked it will pass the control to the BaseFragment which checks if the current fragment is still attached then will call the executeAction() method of the respective fragment which being abstract every fragment can have their own implementation.

So say for FragmentA you will simply have to extend it from BaseFragment and override executeAction() method. You can write fragment specific implementations.

This process is called dependency inversion principle. See if you can put all these pieces in right place else let me know. :) Wow this is huge. :)

网友答案:

On the viewpager class:

  public  void validate() {
    for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
      Fragment fragment = mSectionsPagerAdapter.getItem(i);
      if(!(fragment instanceof Validetable)) {
        return;
       }
       Validetable validetable = (Validetable) mSectionsPagerAdapter.getItem(i);
       Fragment invalidFragment = validetable.validate();
       if (invalidFragment == null) {
         Toast.makeText(getActivity(), "valido", Toast.LENGTH_LONG).show();
       }
       else {
     mViewPager.setCurrentItem(i);
         break;
       }
}

On each fragment you do:

public static boolean isValid = true;

@Override
public Fragment validate() {
  if ( StringUtils.isBlank(ColetaLocal.getInstance().getNivel())) {
    isValid = false;
    return this; 
  }
  isValid = true;
  return null;
}


@Override
public void onResume () {
    super.onResume();
    treatErrorsShowing();
}

private void treatErrorsShowing() {
    if (!isValid) {
        showErrors();
    }
    else {
        clearErrors();
    }
}
网友答案:

I ended up validating each Fragment before moving to the next one.

Reason: The initial idea was to validate on save, and if there was an Fragment with invalid data, move to that fragment and show the errors. But since there is no way to determine the state of Views inside a Fragment if it is not visible, you cannot validate input.

相关阅读:
Top