Dynamic height viewpager

后端 未结 6 1334
無奈伤痛
無奈伤痛 2020-11-27 13:06

I\'m trying to create a custom viewpager inside custom scroll viewthat dynamically wraps the current child\'s height.

package com.example.vihaan.dynamicviewp         


        
相关标签:
6条回答
  • 2020-11-27 13:38

    Adding to @vihaan's solution, if you have a PagerTitleStrip or PagetTabStrip, you can add this

    // Account for pagerTitleStrip or pagerTabStrip
    View tabStrip = getChildAt(0);
    if (tabStrip instanceof PagerTitleStrip) {
        tabStrip.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.UNSPECIFIED));
        height += tabStrip.getMeasuredHeight();
    }
    

    just before starting the animation (before the comment

     // Not the best place to put this animation, but it works pretty good.
    

    so that the height of the strip is taken into account.

    0 讨论(0)
  • 2020-11-27 13:40

    Just in case someone else find this post like me. Worked version without bug of initially zero height:

    public class DynamicHeightViewPager extends ViewPager {
        private View mCurrentView;
    
        public DynamicHeightViewPager(Context context) {
            super(context);
        }
    
        public DynamicHeightViewPager(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            if (mCurrentView != null) {
                mCurrentView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    
                int height = Math.max(0, mCurrentView.getMeasuredHeight());
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
        }
    
        public void measureCurrentView(View currentView) {
            mCurrentView = currentView;
            requestLayout();
        }
    }
    

    And used it in custom FragmentPagerAdapter, like this

    public abstract class AutoheightFragmentPagerAdapter extends FragmentPagerAdapter {
        private int mCurrentPosition = -1;
    
        public AutoheightFragmentPagerAdapter(FragmentManager fm) {
            super(fm);
        }
    
        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            super.setPrimaryItem(container, position, object);
    
            if (position != mCurrentPosition && container instanceof DynamicHeightViewPager) {
                Fragment fragment = (Fragment) object;
                DynamicHeightViewPager pager = (DynamicHeightViewPager) container;
    
                if (fragment != null && fragment.getView() != null) {
                    mCurrentPosition = position;
                    pager.measureCurrentView(fragment.getView());
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-27 13:45

    @abhishek's ans does what is required but the code below also adds animation during height change

    public class WrappingViewPager extends ViewPager {
    
        private Boolean mAnimStarted = false;
    
        public WrappingViewPager(Context context) {
            super(context);
        }
    
        public WrappingViewPager(Context context, AttributeSet attrs){
            super(context, attrs);
        }
    
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            if(!mAnimStarted && null != getAdapter()) {
                int height = 0;
                View child = ((FragmentPagerAdapter) getAdapter()).getItem(getCurrentItem()).getView();
                if (child != null) {
                    child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                    height = child.getMeasuredHeight();
                    if (VersionUtils.isJellyBean() && height < getMinimumHeight()) {
                        height = getMinimumHeight();
                    }
                }
    
                // Not the best place to put this animation, but it works pretty good.
                int newHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
                if (getLayoutParams().height != 0 && heightMeasureSpec != newHeight) {
                        final int targetHeight = height;
                        final int currentHeight = getLayoutParams().height;
                        final int heightChange = targetHeight - currentHeight;
    
                        Animation a = new Animation() {
                            @Override
                            protected void applyTransformation(float interpolatedTime, Transformation t) {
                                if (interpolatedTime >= 1) {
                                    getLayoutParams().height = targetHeight;
                                } else {
                                    int stepHeight = (int) (heightChange * interpolatedTime);
                                    getLayoutParams().height = currentHeight + stepHeight;
                                }
                                requestLayout();
                            }
    
                            @Override
                            public boolean willChangeBounds() {
                                return true;
                            }
                        };
    
                        a.setAnimationListener(new Animation.AnimationListener() {
                            @Override
                            public void onAnimationStart(Animation animation) {
                                mAnimStarted = true;
                            }
    
                            @Override
                            public void onAnimationEnd(Animation animation) {
                                mAnimStarted = false;
                            }
    
                            @Override
                            public void onAnimationRepeat(Animation animation) {
                            }
                        });
    
                        a.setDuration(1000);
                        startAnimation(a);
                        mAnimStarted = true;
                } else {
                    heightMeasureSpec = newHeight;
                }
            }
    
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
    
    0 讨论(0)
  • 2020-11-27 13:52

    This few lines of code will solve the problem.

    Create a custome widget for viewpager class. and in xml use it for viewpager.

    public class DynamicHeightViewPager extends ViewPager {
    private View mCurrentView;
    
    public DynamicHeightViewPager(Context context) {
        super(context);
    }
    
    public DynamicHeightViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    
    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
        if (mCurrentView != null) {
            mCurrentView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    
            int height = Math.max(0, mCurrentView.getMeasuredHeight());
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
    
    public void measureCurrentView(View currentView) {
        mCurrentView = currentView;
        requestLayout();
    }
    }
    

    Then in the view pager adapter override the below method and add the code.

      @Override
    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        super.setPrimaryItem(container, position, object);
        if (container instanceof DynamicHeightViewPager) {
    // instead of card view give your root view from your item.xml file.
            CardView cardView = (CardView) object;
            ((DynamicHeightViewPager) container).measureCurrentView(cardView);
        }
    }
    

    Make your view pager height wrap_content inside xml file so you can check the result.

    0 讨论(0)
  • 2020-11-27 13:53

    Made a few tweaks in your code and it is working fine now.

    1] onMeasure function wasn't proper. Use below logic

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mCurrentView == null) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }
        int height = 0;
        mCurrentView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        int h = mCurrentView.getMeasuredHeight();
        if (h > height) height = h;
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    

    2] ViewPager needs to be re-measured each time a page is changed. Good place to do this is setPrimaryItem function of PagerAdapter

    @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            super.setPrimaryItem(container, position, object);
            if (position != mCurrentPosition) {
                Fragment fragment = (Fragment) object;
                CustomPager pager = (CustomPager) container;
                if (fragment != null && fragment.getView() != null) {
                    mCurrentPosition = position;
                    pager.measureCurrentView(fragment.getView());
                }
            }
        }
    

    Here is the link to GitHub project with these tweaks: https://github.com/vabhishek/WrapContentViewPagerDemo

    0 讨论(0)
  • 2020-11-27 13:53
    public class WrapContentViewPager extends ViewPager {
    private Boolean mAnimStarted = false;
    
    public WrapContentViewPager(Context context) {
        super(context);
    }
    
    public WrapContentViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
        if (!mAnimStarted && null != getAdapter()) {
            int height = 0;
            View child = ((CommonViewPagerAdapter) getAdapter()).getItem(getCurrentItem()).getView();
            if (child != null) {
                child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                height = child.getMeasuredHeight();
                if (height < getMinimumHeight()) {
                    height = getMinimumHeight();
                }
            }
    
            // Not the best place to put this animation, but it works pretty good.
            int newHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
            if (getLayoutParams().height != 0 && heightMeasureSpec != newHeight) {
                final int targetHeight = height;
                final int currentHeight = getLayoutParams().height;
                final int heightChange = targetHeight - currentHeight;
    
                Animation a = new Animation() {
                    @Override
                    protected void applyTransformation(float interpolatedTime, Transformation t) {
                        if (interpolatedTime >= 1) {
                            getLayoutParams().height = targetHeight;
                        } else {
                            int stepHeight = (int) (heightChange * interpolatedTime);
                            getLayoutParams().height = currentHeight + stepHeight;
                        }
                        requestLayout();
                    }
    
                    @Override
                    public boolean willChangeBounds() {
                        return true;
                    }
                };
    
                a.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
                        mAnimStarted = true;
                    }
    
                    @Override
                    public void onAnimationEnd(Animation animation) {
                        mAnimStarted = false;
                    }
    
                    @Override
                    public void onAnimationRepeat(Animation animation) {
                    }
                });
    
                a.setDuration(100);
                startAnimation(a);
                mAnimStarted = true;
            } else {
                heightMeasureSpec = newHeight;
            }
        }
    
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    }
    

    ==============================

    wrapContentViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }
    
            @Override
            public void onPageSelected(int position) {
                wrapContentViewPager.measure(wrapContentViewPager.getMeasuredWidth(), wrapContentViewPager.getMeasuredHeight());
            }
    
            @Override
            public void onPageScrollStateChanged(int state) {
    
            }
        });
    
    0 讨论(0)
提交回复
热议问题