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