I'm working on a flashcard like app and i've been wrestling with the flip animation. I've based my code on the AnimationsDemo from android with the difference being that it is done inside a fragment instead of an activity. It works fine, not entirely as the preview of the aforementioned demo, but decent enough. Except... after changing orientation. If the app is started in landscape, it works fine in landscape, but freaks in portrait and if the app is launched in portrait it works in portrait and freaks in landscape.
This is my code ( i skimmed the datamodel obviously)
public class MyFlashCardFragment extends Fragment {
private MyDataModel model;
private Handler mHandler = new Handler();
private boolean mShowingBack = false;
FrameLayout frameLayout;
public static MyFlashCardFragment create(MyDataModel m) {
MyFlashCardFragment fragment = new MyFlashCardFragment();
Bundle bundle = new Bundle();
bundle.putString(AppKeys.FRONT_KEY,m.getFrontText());
bundle.putString(AppKeys.BACK_KEY,m.getBackText());
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!getArguments().isEmpty()) {
model = new MyDataModel();
model.setFrontText(getArguments().getString(AppKeys.FRONT_KEY));
model.setBackText(getArguments().getString(AppKeys.BACK_KEY));
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
if (model == null) {
return super.onCreateView(inflater, container, savedInstanceState);
} else {
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_flashcard, container, false);
frameLayout = (FrameLayout) root.findViewById(R.id.chilfd_frame);
Button btn_overlay = (Button) root.findViewById(R.id.overlaybutton);
btn_overlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
flipCard();
}
});
return root;
}
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if(savedInstanceState == null) {
getChildFragmentManager()
.beginTransaction()
.add(R.id.chilfd_frame, CardFrontFragment.create(model.getFrontText()))
.commit();
} else {
mShowingBack = (getChildFragmentManager().getBackStackEntryCount() > 0);
}
getChildFragmentManager().addOnBackStackChangedListener(new android.app.FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
mShowingBack = (getChildFragmentManager().getBackStackEntryCount() > 0);
getActivity().invalidateOptionsMenu();
}
});
}
private void flipCard() {
if (mShowingBack) {
getChildFragmentManager().popBackStack();
return;
}
mShowingBack = true;
getChildFragmentManager()
.beginTransaction()
.setCustomAnimations(
R.animator.card_flip_right_in, R.animator.card_flip_right_out,
R.animator.card_flip_left_in, R.animator.card_flip_left_out)
.replace(frameLayout.getId(), CardBackFragment.create(model.getBackText()))
.addToBackStack(null)
.commit();
mHandler.post(new Runnable() {
@Override
public void run() {
getActivity().invalidateOptionsMenu();
}
});
}
/**
* A fragment representing the front of the card.
*/
public static class CardFrontFragment extends Fragment {
String frontText;
public static CardFrontFragment create(String frontText) {
CardFrontFragment fragment = new CardFrontFragment();
Bundle bundle = new Bundle();
bundle.putString(AppKeys.FRONT_KEY,frontText);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!getArguments().isEmpty()) {
frontText = getArguments().getString(AppKeys.FRONT_KEY);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (TextUtils.isEmpty(frontText)) {
return super.onCreateView(inflater, container, savedInstanceState);
} else {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_flashcard_front, container, false);
((TextView) rootView.findViewById(R.id.tv_name)).setText(frontText);
return rootView;
}
}
}
/**
* A fragment representing the back of the card.
*/
public static class CardBackFragment extends Fragment {
String backText;
public static CardBackFragment create(String backText) {
CardBackFragment fragment = new CardBackFragment();
Bundle bundle = new Bundle();
bundle.putString(AppKeys.BACK_KEY,backText);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!getArguments().isEmpty()) {
backText = getArguments().getString(AppKeys.BACK_KEY);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (TextUtils.isEmpty(backText)) {
return super.onCreateView(inflater, container, savedInstanceState);
} else {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_flashcard_back, container, false);
((TextView) rootView.findViewById(R.id.tv_name)).setText(backText);
return rootView;
}
}
}
}
The parentfragment is loaded form a viewpager:
pager = (ViewPager) findViewById(R.id.pager);
adapter = new FlashCardPageAdapter(getFragmentManager(), getAllFrom(ref));
pager.setAdapter(adapter);
pager.setCurrentItem(position,true);
pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
invalidateOptionsMenu();
}
});
and the adapter used is:
public class FlashCardPageAdapter extends FragmentStatePagerAdapter {
private ArrayList<MyDataModel> Models;
public FlashCardPageAdapter(FragmentManager fm, ArrayList<MyDataModel> list) {
super(fm);
this.Models = list;
}
@Override
public Fragment getItem(int position) {
return FlashCardFragment.create(Models.get(position));
}
@Override
public int getCount() {
return Models.size();
}
}
I also tried to create the animations form an Animator object and add that to a transparent button and animating views in and out and toggling the visibility and the result is the same.
Any ideas/suggestions would be welcome.
It turns out there's nothing missing in my code and the problem is emulator related. Well at least it seems that way. I tried it on 2 real devices (1 phone/1 tablet) and the problem didn't manifest on them, so might be an AVD bug or simple shortcoming of the simulated orientation change.