21

Я пытаюсь создать пользовательский viewpager внутри пользовательского прокрутки, который динамически обертывает высоту текущего ребенка.Dynamic view viewpager

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

Я надеялся, что это будет обернуть вокруг высоты текущих фрагментов, но она принимает только высоту первого ребенка во внимание.

Пример проекта GitHub: https://github.com/VihaanVerma89/DynamicViewPager

ответ

16

@ Абхишек х анс делает то, что требуется, но ниже код также добавляет анимацию при высоте изменить

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

Разве это не ужасно laggy/not-smooth? Это для меня, по крайней мере (на эмуляторе). – Timmiej93

+0

@ Timmiej93 Вы можете che ck это жить в моем приложении https://play.google.com/store/apps/details?id=com.mirraw.android. Перейдите на страницу сведений о продукте. Он работает безупречно :) –

+0

Прошу прощения, я, вероятно (скорее всего), очень глуп, но где я найду ваше приложение? Я мог бы вытащить его из GitHub, но эта версия не имеет изменений в нем (насколько я могу видеть). EDIT: Вы только что добавили ссылку, когда я набрал это, спасибо! – Timmiej93

42

Сделано несколько настроек в вашем коде, и теперь он работает нормально.

1] onMeasure функция не соответствует. Используйте ниже логики

@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 должна быть измерена повторно каждый раз, когда страница изменена. Хорошее место, чтобы сделать это setPrimaryItem функция 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()); 
      } 
     } 
    } 

Вот ссылка на проект GitHub с этих ухищрений: https://github.com/vabhishek/WrapContentViewPagerDemo

+1

Hi! Я использовал ваше решение, чтобы вставить свой ViewPager в ScrollView. Первый фрагмент ViewPager всегда получает высоту 0, хотя у меня есть контент. Другие фрагменты работают правильно. Когда я перехожу к первому фрагменту, я вижу содержимое там, но как только я полностью на этой вкладке, фрагмент обрушивается. Я не могу понять, что происходит не так. –

+0

@NarayanAcharya Можете ли вы разместить свой код? желательно в качестве нового вопроса и поставьте ссылку на этот вопрос здесь. –

+0

Я добавил код в этот вопрос http://stackoverflow.com/questions/38349809/android-viewpager-with-a-dynamic-height-within-a-scrollview. Пожалуйста, смотрите. Благодаря! –

2

Добавление к @ vihaan, решение которого, если у вас есть PagerTitleStrip или PagetTabStrip, вы можете добавить

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

непосредственно перед запуском анимации (перед комментарием

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

так, что высота полосы принимается во внимание.

0

На всякий случай кто-то найдет это сообщение, как я. Рабочая версия без ошибки изначально нулевой высоты:

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