Android ViewPager with PageTransformer and SceneTransition - android

and thank you for stopping by.
I've been facing a weird behavior using the combination of both PageTransformer and TransitionManager.
The hierarchy I'm using is fairly easy : A viewPager with each page being a fragment. The fragment has two different layouts, changing with the TransitionManager.go().
The issue :
If I just scroll through the viewPager, everything is fine, and my pageTransformer applies the right values, creating the desired parallax effect.
If I just click back and forth to change scenes inside a page, I also get the desired output.
However, whenever I use the TransitionManager.go() (let's say twice, to go back to the first layout) and then start scrolling through my viewPager, the parallax effect doesn't occur anymore.
My question :
Is there any known issue I haven't been able to find with using both a PageTransformer and a TransitionManager at the same time?
My code :
Fragment1.java
public class Fragment1 extends Fragment {
private Scene mStartScene;
private Scene mInfoScene;
private Transition mTransition;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.view_pager_item_default, container, false);
RelativeLayout rootLayout = (RelativeLayout) v.findViewById(R.id.p1);
mTransition = new ChangeBounds();
mTransition.setDuration(400);
mStartScene = Scene.getSceneForLayout(rootLayout, R.layout.view_pager_item_default, getContext());
mInfoScene = Scene.getSceneForLayout(rootLayout, R.layout.view_pager_item_details, getContext());
return (v);
}
public void changeScene(View v) {
Scene tmp = mInfoScene;
mInfoScene = mStartScene;
mStartScene = tmp;
TransitionManager.go(mStartScene, mTransition);
}
}
view_pager_item_default.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/p1"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView"
android:elevation="20dp"
android:text="Item 1"
android:onClick="changeScene"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="60dp"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:padding="20dp"
android:layout_height="wrap_content"
android:background="#drawable/white_shape"
android:textSize="40sp"
android:gravity="center"
android:textColor="#000000"/>
<ImageView
android:id="#+id/imageView"
android:elevation="19dp"
android:scaleType="centerCrop"
android:layout_marginBottom="-10dp"
android:layout_centerHorizontal="true"
android:src="#mipmap/big_image"
android:background="#android:color/transparent"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
view_pager_item_details.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/p1"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView"
android:elevation="20dp"
android:text="Item 1 description"
android:onClick="changeScene"
android:layout_marginTop="150dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="20dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/white_shape"
android:clickable="true"
android:textSize="30sp"
android:gravity="center"
android:textColor="#000000"/>
<ImageView
android:id="#+id/imageView"
android:elevation="21dp"
android:layout_marginTop="50dp"
android:layout_centerHorizontal="true"
android:src="#mipmap/small_image"
android:background="#android:color/transparent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
My adapter is fairly simple :
public class MyPagerAdapter extends FragmentPagerAdapter {
private final List fragments;
public MyPagerAdapter(FragmentManager fm, List fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return (Fragment) this.fragments.get(position);
}
#Override
public int getCount() {
return this.fragments.size();
}
}
Then is my PageTransformer which moves each children of the view independently
public class MyPagerTransformer implements ViewPager.PageTransformer {
private float mParallaxCoeff;
private float mDistanceCoeff;
public MyPagerTransformer(float parallax, float distance) {
mParallaxCoeff = parallax;
mDistanceCoeff = distance;
}
#Override
public void transformPage(View page, float position) {
float coefficient = page.getWidth() * mParallaxCoeff;
ViewGroup vG = (ViewGroup) page;
for (int i = vG.getChildCount() - 1; i >= 0; --i) {
View v = vG.getChildAt(i);
if (v != null) {
v.setTranslationX(coefficient * (position * position * position));
}
coefficient *= mDistanceCoeff;
}
}
}
And lastly my Activity :
public class MainActivity extends AppCompatActivity {
private Fragment1 mFrag1;
private Fragment1 mFrag2;
private Fragment1 mFrag3;
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
mViewPager = (ViewPager) findViewById(R.id.main_view_pager);
CirclePageIndicator indicator = (CirclePageIndicator) findViewById(R.id.indicator);
if (mViewPager != null) {
mViewPager.setPageTransformer(true, new MyPagerTransformer(8.0f, 8.0f));
//vp.addOnPageChangeListener(listener);
List<Fragment> fragments = new ArrayList<>();
mFrag1 = new Fragment1();
mFrag2 = new Fragment1();
mFrag3 = new Fragment1();
fragments.add(mFrag1);
fragments.add(mFrag2);
fragments.add(mFrag3);
PagerAdapter realViewPagerAdapter = new MyPagerAdapter(super.getSupportFragmentManager(), fragments);
mViewPager.setAdapter(realViewPagerAdapter);
indicator.setViewPager(mViewPager);
}
}
public void changeScene(View v) {
switch (mViewPager.getCurrentItem()) {
case 0:
mFrag1.changeScene(v);
break;
case 1:
mFrag2.changeScene(v);
break;
case 2:
mFrag3.changeScene(v);
break;
default:
break;
}
}
}
Lastly, here's a gif showing what happens. As you can see, at the beginning "Item 1" has the parallax effect. After switching scenes back and forth, the PageTransformer won't apply anymore.
Thanks in advance !

I will answer my own question in case anyone would bump into the same issue as I did.
The problem came from the rootLayout I was using in the fragment not being "the right one", therefore the TransitionManager was adding an extra layer when going back to the first scene.
Here's what I changed :
Fragment1.java
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//view_pager_root.xml is a simple empty FrameLayout
View v = inflater.inflate(R.layout.view_pager_root, container, false);
RelativeLayout rootLayout = (RelativeLayout) v.findViewById(R.id.p1);
mTransition = new ChangeBounds();
mTransition.setDuration(400);
mStartScene = Scene.getSceneForLayout(rootLayout, R.layout.view_pager_item_default, getContext());
mInfoScene = Scene.getSceneForLayout(rootLayout, R.layout.view_pager_item_details, getContext());
return (v);
}
Since I added another layer into my hierarchy, I also had to change slightly my PageTransformer :
#Override
public void transformPage(View page, float position) {
float coefficient = page.getWidth() * mParallaxCoeff;
//vG is the FrameLayout
ViewGroup vG = (ViewGroup) page;
if (vG.getChildAt(0) instanceof ViewGroup) {
//vG is now the RelativeLayout from the scene
vG = (ViewGroup) vG.getChildAt(0);
for (int i = vG.getChildCount() - 1; i >= 0; --i) {
View v = vG.getChildAt(i);
if (v != null) {
v.setTranslationX(coefficient * (position * position * position));
}
coefficient *= mDistanceCoeff;
}
}
}

Related

How to Scroll Vertically Viewpager

I am unable to scroll view pager vertically. when I use NestedScrollView in the Child layout, pages become blank. I tried many solutions.but failed.
This is my main fragment nav_swipe.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" >
<android.support.v4.view.PagerTitleStrip
android:id="#+id/pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#33b5e5"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="#fff" />
</android.support.v4.view.ViewPager>
</android.support.design.widget.CoordinatorLayout>
This is Child fragment layout : fragment_a.xml. where I used nestedscrollview but get no solution.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/text1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="#color/colorPrimaryDark"
android:layout_marginBottom="#dimen/activity_vertical_margin"
android:layout_marginRight="#dimen/activity_horizontal_margin"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
android:layout_marginTop="#dimen/activity_vertical_margin"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="#+id/constraintLayout"
tools:layout_constraintLeft_creator="1"
tools:layout_constraintTop_creator="1" />
</android.support.constraint.ConstraintLayout>
This is SwipeNav.java
public class SwipeNav extends Fragment {
private MyPagerAdapter myPagerAdapter;
private ViewPager viewPager;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.nav_swipe, container, false);
myPagerAdapter = new MyPagerAdapter(getChildFragmentManager(),getContext());
viewPager = (ViewPager) rootView.findViewById(R.id.pager);
viewPager.setAdapter(myPagerAdapter);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
This is MyPagerAdapter.java
public class MyPagerAdapter extends FragmentStatePagerAdapter {
private Context context;
private String[] tabTitlesArray = null;
public MyPagerAdapter(FragmentManager fm, Context context) {
super(fm);
tabTitlesArray = context.getResources().getStringArray(R.array.tab_titles);
this.context= context;
}
#Override
public Fragment getItem(int i) {
Fragment fragment = new AFragment();
Bundle args = new Bundle();
args.putString(AFragment.ARG_OBJECT, tabTitlesArray[i]);
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
return tabTitlesArray.length;
}
#Override
public CharSequence getPageTitle(int position) {
//return tabTitleArray[position];
return "OBJECT " + (position + 1);
}
}
This is AFragment.java
public class AFragment extends Fragment {
public static final String ARG_OBJECT = "object";
public AFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_a, container, false);
Bundle args = getArguments();
((TextView) rootView.findViewById(R.id.text1)).setText(args.getString(ARG_OBJECT));
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
kindly use ViewPager.PageTransformer to give the illusion of a vertical ViewPager. To achieve scrolling with a vertical instead of a horizontal.
/**
* Uses a combination of a PageTransformer and swapping X & Y coordinates
* of touch events to create the illusion of a vertically scrolling ViewPager.
*
* Requires API 11+
*
*/
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
initiate();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
initiate();
}
private void initiate() {
// The majority of the magic happens here
setPageTransformer(true, new VerticalPageTransformer());
// The easiest way to get rid of the overscroll drawing that happens on the left and right
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements ViewPager.PageTransformer {
#Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 1) { // [-1,1]
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
//set Y position to swipe in from top
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
/**
* Swaps the X and Y coordinates of your touch event.
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev){
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev); // return touch coordinates to original reference frame for any child views
return intercepted;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
Try this code :
EDIT LAYOUT
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"/>
</LinearLayout>
</ScrollView>
EDIT : For the main try this code
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="#+id/main_layout"
android:layout_height="match_parent"
tools:context=".com.example.thomas.activity.MainActivity">
<android.support.design.widget.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TabLayout
android:id="#+id/main_tabs"
android:layout_width="match_parent"
app:tabIndicatorColor="#android:color/white"
app:tabSelectedTextColor="#android:color/white"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
</android.support.design.widget.TabLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/main_tabs_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/appBarLayout">
</android.support.v4.view.ViewPager>
If not working, please show some java code
EDIT
I updated layout for fragment
Now here is code for fragment adapter :
public class FragmentAdaptater extends FragmentPagerAdapter
{
private final List<Fragment> lstFragment = new ArrayList<>();
private final List<String> lstTitles = new ArrayList<>();
public FragmentAdaptater(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
return lstFragment.get(i);
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return lstTitles.get(position);
}
#Override
public int getCount() {
return lstTitles.size();
}
public void AddFragment (Fragment fragment , String title)
{
lstFragment.add(fragment);
lstTitles.add(title);
}
}
And code in main :
public class MainActivity extends AppCompatActivity {
private Toolbar mToolbar;
private FragmentPagerAdapter fragmentadaptater2;
private ViewPager mviewpager;
private TabLayout mtablayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_2);
mviewpager = (ViewPager) findViewById(R.id.main_tabs_pager);
mToolbar = (Toolbar) findViewById(R.id.main_page_toolbar);
mtablayout = (TabLayout) findViewById(R.id.main_tabs);
setSupportActionBar(mToolbar);
fragmentadaptater2 = new FragmentAdaptater(getSupportFragmentManager());
((FragmentAdaptater) fragmentadaptater2).AddFragment(new YOURFRAGMENT(),"YOUR FRAGMENT NAME");
((FragmentAdaptater) fragmentadaptater2).AddFragment(new YOURFRAGMENT(),"YOUR FRAGMENT NAME");
((FragmentAdaptater) fragmentadaptater2).AddFragment(new YOURFRAGMENT(),"YOUR FRAGMENT NAME");
mviewpager.setAdapter(fragmentadaptater2);
mtablayout = (TabLayout) findViewById(R.id.main_tabs);
mtablayout.setupWithViewPager(mviewpager);
}
One should use ViewPager2 and in its XML set android:orientation="vertical" property and your ViewPager will scroll vertically.
The complete code will be like this.
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
/>

Fill linearlayout with cardviews and viewpager

I have an issue trying to fill a linear layout with cardviews inside, and inside of these, a view pager that is filled with two fragments. the fragments are filled with data stored in an SQLite database in a loop.
The issue is that only the last viewpager adapter calls the getItem function, making that all cards are empty except the last one.
This is the element layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="vertical">
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginBottom="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="10dp"
card_view:cardCornerRadius="4dp">
<com.jaimedediego.cubemaster.view.CustomViews.CustomViewPager
android:id="#+id/detail_container"
android:layout_width="match_parent"
android:layout_height="100dp" />
</android.support.v7.widget.CardView>
</LinearLayout>
The CustomViewPager works exactly like a normal viewpager.
This is the function where a external linear layout is filled with elements with the previous layout. Where timesDetail is filled with the data
for (int i = 0; i < timesDetail.size(); i++) {
final Detail detail = timesDetail.get(i);
final View v = getLayoutInflater().inflate(R.layout.element_timesdetail_list, null);
final CustomViewPager viewPager = v.findViewById(R.id.detail_container);
viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
#Override
public Fragment getItem(int position) {
return PlaceholderFragmentDetail.newInstance(position, detail);
}
#Override
public int getCount() {
return 2;
}
});
timesLayout.addView(v);
}
}
The getItem should be called for each loop to call the onCreateView of the PlaceholderFragmentDetail. but it doesn't (I think the issue is here). And it makes all cardview excepting the last one are empty.
The PlaceholderFragmentDetail call is
public static class PlaceholderFragmentDetail extends Fragment {
private static final String ARG_SECTION_NUMBER = "section_number";
private static final String DETAIL = "detail";
public PlaceholderFragmentDetail() {/*Do nothing*/}
public static PlaceholderFragmentDetail newInstance(int sectionNumber, Detail detail) {
PlaceholderFragmentDetail fragment = new PlaceholderFragmentDetail();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
args.putSerializable(DETAIL, detail);
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v;
Detail detail = (Detail) getArguments().getSerializable(DETAIL);
switch (getArguments().getInt(ARG_SECTION_NUMBER)) {
case 0:
v = inflater.inflate(R.layout.fragment_detail, container, false);
break;
case 1:
v = inflater.inflate(R.layout.layout_dialog_areyousure, container, false);
break;
default:
v = null;
break;
}
return v;
}
}
I really think that the issue is the getItem call of the FragmentPagerAdapter, but i don't know what i'm doing wrong.

ViewPager displays shadows on Fragments after notifyDataSetChanged

I'm making a simple flashcard app using a ViewPager, FragmentStatePagerAdapter, and some card flipping animations found on the offical Android tutorials (https://developer.android.com/training/animation/cardflip.html). I've created a shuffle button which shuffles the cards in the deck and then calls notifyDataSetChanged on the adapter to update the card views. Once called, it successfully updates the views, but it also creates a shadow/border around each card that doesn't clear until the card is touched and flipped.
When initially launched, the cards are basically have no border.
Here is what the cards look like after I spam the shuffle button. The border darkens on every press:
Here are some snippets of the relevant parts of my code:
MainAcivity.java
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.shuffleButton:
pager.removeAllViews();
pagerAdapter.shuffle();
pager.setAdapter(pagerAdapter);
}
return true;
CardPagerAdapter.java
public class CardPagerAdapter extends FragmentStatePagerAdapter {
public static final String CARD = "CARD";
private static final float WIDTH_SCALE = 0.95f;
private int count;
private Deck deck;
private ArrayList<Card> cards;
private boolean isShuffled;
public CardPagerAdapter(FragmentManager fragmentManager, Deck deck) {
super(fragmentManager);
this.deck = deck;
this.count = deck.getSize();
this.cards = deck.getCards();
this.isShuffled = false;
}
#Override
public Fragment getItem(int position) {
CardContainerFragment cardContainerFragment = new
CardContainerFragment();
Bundle bundle = new Bundle();
bundle.putParcelable(CARD, cards.get(position));
cardContainerFragment.setArguments(bundle);
return cardContainerFragment;
}
#Override
public int getItemPosition(Object item) {
return POSITION_NONE;
}
#Override
public int getCount() {
return count;
}
/**
* Override the pageWidth in order to be able
* to see other cards on the side of the current card.
* #param position The position of the current card
* #return The multiplier by which to change the width by
*/
#Override
public float getPageWidth(int position) {
return WIDTH_SCALE;
}
public void shuffle() {
if (isShuffled) {
cards = deck.getCards();
} else {
Collections.shuffle(cards);
}
isShuffled = !isShuffled;
notifyDataSetChanged();
}
}
CardContainerFragment.java
public class CardContainerFragment extends Fragment {
public static final String FLIPPED = "FLIPPED";
private static final float DISTANCE = 8000;
private CardFragment frontCardFragment;
private CardFragment backCardFragment;
private boolean cardFlipped;
public CardContainerFragment() {
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
// Create the card fragment view
final View rootView = inflater.inflate(
R.layout.card_fragment, container, false);
this.cardFlipped = false;
Bundle bundle = this.getArguments();
// Make the front and back cards
Bundle frontBundle = (Bundle) bundle.clone();
Bundle backBundle = (Bundle) bundle.clone() ;
frontCardFragment = new CardFragment();
frontBundle.putByte(FLIPPED, (byte) 0);
frontCardFragment.setArguments(frontBundle);
backCardFragment = new CardFragment();
backBundle.putByte(FLIPPED, (byte) 1);
backCardFragment.setArguments(backBundle);
// Set the first viewed fragment to be the the front of the card
getChildFragmentManager()
.beginTransaction()
.add(R.id.container, frontCardFragment)
.commit();
// Flip the card once touched
rootView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
flipCard();
}
});
return rootView;
}
/**
* Swaps the visible fragment with the other fragment (the other side of
the card)
* Animates the transition between the change with a flip animation.
*/
public void flipCard() {
Fragment fragment;
if (cardFlipped) {
fragment = frontCardFragment;
} else {
fragment = backCardFragment;
}
getChildFragmentManager()
.beginTransaction()
.setCustomAnimations(
R.animator.card_flip_left_in,
R.animator.card_flip_left_out,
R.animator.card_flip_right_in,
R.animator.card_flip_right_out)
.replace(R.id.container, fragment)
.commit();
cardFlipped = !cardFlipped;
}
public static class CardFragment extends Fragment {
private TextView cardText;
private ImageButton starButton;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup
container,
Bundle savedInstanceState) {
// Create the card fragment view and get the card object
View view = inflater.inflate(R.layout.card_view, container,
false);
Bundle bundle = this.getArguments();
final Card card = bundle.getParcelable(CardPagerAdapter.CARD);
boolean cardFlipped = bundle.getByte(FLIPPED) != 0;
// Change camera perspective to not have the flip animation be
// distorted
float scale = getResources().getDisplayMetrics().density;
view.setCameraDistance(DISTANCE * scale);
// Set the text from the card to the fragment's textview
cardText = (TextView) view.findViewById(R.id.cardText);
if (cardFlipped) {
cardText.setText(card.getBackText());
} else {
cardText.setText(card.getFrontText());
}
// Make the star button and set it based on if the card is
// starred
starButton = (ImageButton) view.findViewById(R.id.starButton);
if (card.isStarred()) {
starButton.setImageResource(R.mipmap.ic_star_white_48dp);
} else {
starButton.setImageResource(R.mipmap.ic_star_border_black_48dp);
}
starButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!card.isStarred()) {
starButton.setImageResource(R.mipmap.ic_star_white_48dp);
} else {
starButton.setImageResource(R.mipmap.ic_star_border_black_48dp);
}
card.toggleStarred();
}
});
return view;
}
}
}
Any help at all would be appreciated.
*EDIT
As requested here is the XML
card_fragment.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:stateListAnimator="#null">
</FrameLayout>
card_view.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:stateListAnimator="#null">
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:cardUseCompatPadding="true"
android:stateListAnimator="#null">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/cardText"
android:layout_width="350dp"
android:layout_height="500dp"
android:gravity="center"
android:text="This is the card contents."
android:textAlignment="center"
android:textSize="24sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintBottom_toBottomOf="parent" />
<ImageButton
android:id="#+id/starButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:background="#android:color/transparent"
android:tint="#android:color/darker_gray"
app:layout_constraintRight_toRightOf="#+id/cardText"
app:layout_constraintTop_toTopOf="#+id/cardText"
app:srcCompat="#mipmap/ic_star_border_black_48dp" />
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>
</FrameLayout>
main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#d3d3d3"
tools:context="com.benvo.viewpager.activities.MainActivity">
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<ProgressBar
android:id="#+id/deckProgressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

ViewPager setCurrentItem not smooth

I have an activity that contains one ViewPager (nothing else). The fragments I use are extremely simple. They consist of a flat background with two buttons (back and forward).
When the ViewPager scrolls with user input it works smoothly. However, when I program the buttons to use setCurrentItem to the next page the animation is really choppy.
The "choppyness" is only the first time the animation executes. After that, if I go back and forth using the buttons the animation is smooth.
Because of this behavior, I imagine that it has something to do with the way that PageViewer anticipates user behavior. If anyone can shed some light on this matter that would be of great help. Thanks!
I read about similar issues with PageViewer animations being choppy (all of them more than 3 years old). I tried their suggestions and could not get it to work; so I decided to create a new question.
Here is the code I am using:
MainActivity.java
public class MainActivity extends Activity implements MyFragment.Listener {
private ViewPager _pager;
private PagerAdapter _adapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
_pager = (ViewPager)findViewById(R.id.pager);
_adapter = new MyAdapter(this, getFragmentManager());
_pager.setAdapter(_adapter);
}
public void next() {
if(_pager.getCurrentItem() + 1 >= _adapter.getCount()) return;
_pager.setCurrentItem(_pager.getCurrentItem() + 1, true);
}
public void previous() {
if(_pager.getCurrentItem() - 1 < 0) return;
_pager.setCurrentItem(_pager.getCurrentItem() - 1, true);
}
}
MyAdapter.java
public class MyAdapter extends FragmentStatePagerAdapter {
private static final int PAGES = 3;
private ArrayList<Fragment> _slides;
public MyAdapter(MainActivity l, FragmentManager fm) {
super(fm);
_slides = new ArrayList<>();
for(int i = 0; i < PAGES; i++) {
MyFragment f = new MyFragment();
f.addListener(l);
_slides.add(f);
}
}
public Fragment getItem(int position) { return _slides.get(position); }
public int getCount() { return _slides.size(); }
}
MyFragment.java
public class MyFragment extends Fragment {
public interface Listener {
void next();
void previous();
}
private ArrayList<Listener> ls = new ArrayList<>();
public void addListener(Listener l) { ls.add(l); }
protected void next() { for(Listener l : ls) l.next(); }
protected void previous() { for(Listener l : ls) l.previous(); }
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.my_fragment, container, false);
view.setBackgroundColor(Color.argb(255, (int)(Math.random() * 255), (int)(Math.random() * 255), (int)(Math.random() * 255)));
Button bNext = (Button)view.findViewById(R.id.button_next);
bNext.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
next();
}
});
Button bPrevious = (Button)view.findViewById(R.id.button_previous);
bPrevious.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
previous();
}
});
return view;
}
}
main_activity.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
my_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<Button
android:id="#+id/button_previous"
android:layout_width="100dp"
android:layout_height="100dp"
android:text="previous" />
<Button
android:id="#+id/button_next"
android:layout_width="100dp"
android:layout_height="100dp"
android:text="next" />
</LinearLayout>
Create a custom view pager class by extending ViewPager
Override the smoothScrollTo(...) method
void smoothScrollTo(int x, int y, int v) {
super.smoothScrollTo(x, y, 1);
}
The default value used is 0. When you pass 1, scrolling is smooth.

ViewPagers and PagerAdapters

I am new to Android and am trying a sample application for showing ViewPagers in a Master-Detail Flow using custom PagerAdapters and FragmentStatePagerAdapters. My application has a list of dummy items managed by a SQLiteDatabase which contain a title String, a description String, a Boolean like status, and a list of images (I plan to implement them as downloading from String urls but presently I'm just trying with a single image resource). I am having two problems in the Detail View.
My intention is to use a ViewPager with a FragmentStatePagerAdapter to show the detail view, which consists of a ViewPager with a custom PagerAdapter for showing the list of images, TextView for title and description, a ToggleButton for the like status and a delete button for deleting items from the list.
Issues:
The ViewPager with the custom PagerAdapter does not display the image. It occupies the expected space and swipes performed on it also behave as expected. Only the image is not visible.
[RESOLVED] On using the delete button, I am able to delete the item from the database, and also update the Master View accordingly, but I am not able to update the Detail View, and the app crashes.
Here is my code:
Code that calls ItemDetailActivity.java
#Override
public void onClick(View v) {
Intent detailIntent = new Intent(getContext(), ItemDetailActivity.class);
detailIntent.putExtra(ItemDetailFragment.ARG_LIST_POSITION, holder.position);
getContext().startActivity(detailIntent);
}
ItemDetailActivity.java
public class ItemDetailActivity extends FragmentActivity {
static ItemDetailPagerAdapter idpa;
static ViewPager detailPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_item_detail);
idpa = new ItemDetailPagerAdapter(getSupportFragmentManager());
// Show the Up button in the action bar.
getActionBar().setDisplayHomeAsUpEnabled(true);
detailPager = (ViewPager) findViewById(R.id.item_detail_container);
detailPager.setAdapter(idpa);
detailPager.setCurrentItem(getIntent().getIntExtra(ItemDetailFragment.ARG_LIST_POSITION, 0));
}
}
activity_item_detail.xml
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/item_detail_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.trial.piclist.ItemDetailActivity"
tools:ignore="MergeRootFrame" />
ItemDetailFragment.java
public class ItemDetailFragment extends Fragment {
public static final String ARG_ITEM_ID = "item_id";
public static final String ARG_LIST_POSITION = "list_index";
public static final String ARG_TWO_PANE = "is_two_pane";
int position = -1;
long id = -1;
boolean twoPane = false;
ViewPager pager;
private PicItem mItem;
public ItemDetailFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
twoPane = getArguments().getBoolean(ARG_TWO_PANE, false);
position = getArguments().getInt(ARG_LIST_POSITION, -1);
id = getArguments().getLong(ARG_ITEM_ID, -1);
if (id == -1)
id = ItemListFragment.getIdByPosition(position);
setmItem(id);
}
public void setmItem(long id) {
if (id >= 0) {
try {
ItemListActivity.lds.open();
mItem = ItemListActivity.lds.getById(id);
ItemListActivity.lds.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
if (mItem != null) {
List<String> pics = new ArrayList<String>();
pics.add("1");
pics.add("2");
pics.add("3");
pics.add("4");
pics.add("5");
mItem.setPics(pics);
}
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_item_detail,
container, false);
DetailViewHolder holder = new DetailViewHolder();
pager = (ViewPager) rootView.findViewById(R.id.pager);
ImagePagerAdapter adapter = new ImagePagerAdapter(mItem, getActivity(),
inflater, position);
pager.setAdapter(adapter);
holder.position = getArguments().getInt(ARG_LIST_POSITION);
holder.ttv = (TextView) rootView.findViewById(R.id.item_title);
holder.dtv = (TextView) rootView.findViewById(R.id.item_detail);
holder.likeButton = (ToggleButton) rootView
.findViewById(R.id.item_like);
holder.deleteButton = (Button) rootView.findViewById(R.id.item_delete);
rootView.setTag(holder);
if (mItem != null) {
holder.ttv.setText(mItem.getTitle());
holder.dtv.setText(mItem.getDescription());
holder.likeButton.setChecked(mItem.getIsLiked());
holder.likeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ItemListActivity.lds.open();
ItemListActivity.lds.toggleLike(mItem.getId());
mItem.toggleIsLiked();
ItemListActivity.lds.close();
ItemListFragment.listDisplayHelper.toggleLiked(position);
}
});
holder.deleteButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ItemListActivity.lds.open();
ItemListActivity.lds.removeItem(mItem.getId());
ItemListActivity.lds.close();
ItemListFragment.listDisplayHelper.remove(position);
ItemListActivity.idpa.notifyDataSetChanged();
// What do I do so that the FragmentStatePagerAdapter is
// updated and the viewpager shows the next item.
}
});
}
return rootView;
}
static private class DetailViewHolder {
TextView ttv;
TextView dtv;
ToggleButton likeButton;
Button deleteButton;
int position;
}
}
fragment_item_detail.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context="com.trial.piclist.ItemDetailFragment" >
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="200dip">
</android.support.v4.view.ViewPager>
<TableRow
android:id="#+id/tableRow1"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/item_title"
style="?android:attr/textAppearanceLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello"
android:textIsSelectable="true" />
<Space
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1" />
<include
android:layout_width="wrap_content"
android:layout_height="wrap_content"
layout="#layout/controls_layout" />
</TableRow>
<ScrollView
android:id="#+id/descScrollView"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/item_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello" />
</LinearLayout>
</ScrollView>
</LinearLayout>
controls_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<ToggleButton
android:id="#+id/item_like"
android:layout_width="30dip"
android:layout_height="30dip"
android:layout_gravity="right"
android:background="#android:drawable/btn_star"
android:gravity="center"
android:text="#string/like_list_item"
android:textOff="#string/empty_text"
android:textOn="#string/empty_text" />
<Button
android:id="#+id/item_delete"
style="?android:attr/buttonStyleSmall"
android:layout_width="30dip"
android:layout_height="30dip"
android:background="#android:drawable/ic_menu_delete"
android:text="#string/empty_text" />
</LinearLayout>
Custom PagerAdapter
ImagePagerAdapter.java
public class ImagePagerAdapter extends PagerAdapter {
LayoutInflater inflater;
List<View> layouts = new ArrayList<>(5);
// Constructors.
#Override
public Object instantiateItem(ViewGroup container, int position) {
if (layouts.get(position) != null) {
return layouts.get(position);
}
View layout = inflater.inflate(R.layout.detail_image,
((ViewPager) container), true);
try {
ImageView loadSpace = (ImageView) layout
.findViewById(R.id.detail_image_view);
loadSpace.setBackgroundColor(0x000000);
loadSpace.setImageResource(R.drawable.light_grey_background);
loadSpace.setAdjustViewBounds(true);
} catch (Exception e) {
System.out.println(e.getMessage());
}
layout.setTag(images.get(position));
layouts.set(position, layout);
return layout;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
#Override
public int getCount() {
return 5;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return (((View) object).findViewById((view.getId())) != null);
}
}
FragmentPagerAdapter
ItemDetailPagerAdapter.java
public class ItemDetailPagerAdapter extends FragmentStatePagerAdapter {
public ItemDetailPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment = new ItemDetailFragment();
Bundle args = new Bundle();
args.putLong(ItemDetailFragment.ARG_ITEM_ID, ItemListFragment.getIdByPosition(position));
args.putInt(ItemDetailFragment.ARG_LIST_POSITION, position);
args.putBoolean(ItemDetailFragment.ARG_TWO_PANE, ItemListActivity.mTwoPane);
fragment.setArguments(args);
return fragment;
}
#Override
public int getCount() {
openDatabase();
int c = database.getCount();
closeDatabase();
return c;
}
#Override
public int getItemPosition(Object object) {
long mId = ((ItemDetailFragment) object).getmId();
int pos = POSITION_NONE;
openDatabase();
if (database.contains(mId)) {
pos = database.getPositionById(mId);
}
closeDatabase();
return pos;
}
}
Any help is much appreciated. Thanks :)
In your ItemDetailFragment, remove the viewpager from the holder, it should be directly into the returned view, something like this:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_item_detail,
container, false);
pager = (ViewPager) rootView.findViewById(R.id.pager);
ImagePagerAdapter adapter = new ImagePagerAdapter(mItem, getActivity(),inflater, position);
pager.setAdapter(adapter);
return rootView;
}
and the ViewHolder pattern should be applied inside your PagerAdapter.
In ImagePagerAdapter.java, correct the isViewFromObject method -
#Override
public boolean isViewFromObject(View view, Object object) {
return (view == (View) object);
}
This will correct the issue of the ImageView.
In ItemDetailPagerAdapter.java, override the getItemPosition method -
#Override
public int getItemPosition(Object object) {
int ret = POSITION_NONE;
long id = ((ItemDetailFragment) object).getId();
openDatabase();
if (databaseContains(id)) {
ret = positionInDatabase(id);
}
closeDatabase();
return ret;
}
On deleting call the FragmentStatePagerAdapter.NotifyDataSetChanged() method. This will make the Adapter update itself on deleting.
Although, the FragmentStatePagerAdapter uses a list of Fragments and of stored states to implement the adapter. That is also causing trouble. To remove that, implement your own list of Fragments.

Categories

Resources