android android-viewpager android-custom-view android-scrollview

android - Viewpager de altura dinámico



android-viewpager android-custom-view (4)

Estoy intentando crear un viewpager personalizado dentro de la vista de desplazamiento personalizado que envuelve dinámicamente la altura del niño actual.

package com.example.vihaan.dynamicviewpager; import android.content.Context; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.widget.ScrollView; /** * Created by vihaan on 1/9/15. */ public class CustomScrollView extends ScrollView { private GestureDetector mGestureDetector; public CustomScrollView(Context context, AttributeSet attrs) { super(context, attrs); mGestureDetector = new GestureDetector(context, new YScrollDetector()); setFadingEdgeLength(0); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return super.onInterceptTouchEvent(ev) && mGestureDetector.onTouchEvent(ev); } // Return false if we''re scrolling in the x direction class YScrollDetector extends GestureDetector.SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return (Math.abs(distanceY) > Math.abs(distanceX)); } } }

CustomPager

/** * Created by vihaan on 1/9/15. */ public class CustomPager extends ViewPager { public CustomPager (Context context) { super(context); } public CustomPager (Context context, AttributeSet attrs) { super(context, attrs); } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST; final View tab = getChildAt(0); int width = getMeasuredWidth(); int tabHeight = tab.getMeasuredHeight(); if (wrapHeight) { // Keep the current measured width. widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); } int fragmentHeight = measureFragment(((Fragment) getAdapter().instantiateItem(this, getCurrentItem())).getView()); heightMeasureSpec = MeasureSpec.makeMeasureSpec(tabHeight + fragmentHeight + (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()), MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } public int measureFragment(View view) { if (view == null) return 0; view.measure(0, 0); return view.getMeasuredHeight(); } }

MyPagerAdapter

public class MyPagerAdapter extends FragmentPagerAdapter { private List<Fragment> fragments; public MyPagerAdapter(FragmentManager fm) { super(fm); this.fragments = new ArrayList<Fragment>(); fragments.add(new FirstFragment()); fragments.add(new SecondFragment()); fragments.add(new ThirdFragment()); fragments.add(new FourthFragment()); } @Override public Fragment getItem(int position) { return fragments.get(position); } @Override public int getCount() { return fragments.size(); } }

Esperaba que esto envolviera la altura de los fragmentos actuales, pero solo está considerando la altura del primer hijo.

Ejemplo de proyecto github: https://github.com/VihaanVerma89/DynamicViewPager


Agregando a la solución de @ vihaan, si tiene un PagerTitleStrip o PagetTabStrip, puede agregar este

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

Justo antes de comenzar la animación (antes del comentario.

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

Para que se tenga en cuenta la altura de la cinta.


Hice algunos ajustes en su código y ahora está funcionando bien.

1] onMeasure función onMeasure no era adecuada. Usa la lógica inferior

@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 debe volver a medirse cada vez que se cambia una página. Un buen lugar para hacer esto es la función PagerAdapter de 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()); } } }

Aquí está el enlace al proyecto GitHub con estos ajustes: https://github.com/vabhishek/WrapContentViewPagerDemo


Los ans de @abhishek hacen lo que se requiere, pero el siguiente código también agrega animación durante el cambio de altura

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


Por si alguien más encuentra este post como yo. Versión trabajada sin error de altura inicialmente cero:

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(); } }

Y lo usé en FragmentPagerAdapter personalizado, como este

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()); } } } }