ScrollView+ViewPager,如何动态改变viewPager的高度?
SheWaring
8年前
<p>先看效果图吧</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/1e5ff2a91386c5788155472bfde578bc.gif"></p> <p style="text-align:center">ScrollView+ViewPager,动态改变viewPager的高度</p> <p>在实现的时候,有三点需要注意</p> <h2><strong>1、自定义ViewPager,当滑动到当前页面的时候,重置ViewPager的高度</strong></h2> <pre> <code class="language-java">public class CustomViewPager extends ViewPager { private Map<Integer,Integer> map=new HashMap<>(2); private int currentPage; public CustomViewPager(Context context) { this(context, null); } public CustomViewPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height=0; if(map.size()>currentPage){ height=map.get(currentPage); } //得到ViewPager的MeasureSpec,使用固定值和MeasureSpec.EXACTLY, heightMeasureSpec=MeasureSpec.makeMeasureSpec(height,MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } /** * 在切换tab的时候,重置ViewPager的高度 * @param current */ public void resetHeight(int current){ this.currentPage=current; MarginLayoutParams params= (MarginLayoutParams) getLayoutParams(); if(map.size()>currentPage){ if(params==null){ params=new MarginLayoutParams(LayoutParams.MATCH_PARENT,map.get(current)); }else { params.height=map.get(current); } setLayoutParams(params); } } /** * 获取、存储每一个tab的高度,在需要的时候显示存储的高度 * @param current tab的position * @param height 当前tab的高度 */ public void addHeight(int current,int height){ map.put(current,height); } }</code></pre> <p><strong>注意:</strong></p> <p>MeasureSpec.makeMeasureSpec(),把大小和测量模式打包成一个MeasureSpec,</p> <p>测量模式:</p> <p>①UNSPECIFIED:表示默认值,父控件没有给子view任何限制。------二进制表示:00</p> <p>②EXACTLY:表示父控件给子view一个具体的值,子view要设置成这些值。------二进制表示:01</p> <p>③AT_MOST:表示父控件给子view一个最大的特定值,而子view不能超过这个值的大小。------二进制表示:10</p> <p>为ViewPager添加OnPageChangeListener,实现在滑动的时候改变VIewPager的高度</p> <pre> <code class="language-java">customViewPager.addHeight(position, height); --- customViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { customViewPager.resetHeight(position); } @Override public void onPageScrollStateChanged(int state) { } });</code></pre> <h2><strong>2、获取View的高度</strong></h2> <p>当onCreate方法执行完了,我们定义的控件才会被度量(measure),所以在onCreate方法中去获得控件的高度只能是0 ,可以通过三种方法来获取View的高度</p> <p><strong>1、自己测量</strong></p> <pre> <code class="language-java">private void measureHeight() { int widthMeasureSpace=View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); int heightMeasureSpace=View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); infoText.measure(widthMeasureSpace,heightMeasureSpace); int height=infoText.getMeasuredHeight(); }</code></pre> <p><strong>2、视图树观察者ViewTreeObserver</strong></p> <p>可以通过view.getViewTreeObserver(),获得ViewTreeObserver,使用ViewTreeObserver就可以监听全局事件改变发出的通知,比如整个视图树的布局变化,开始绘制视图,触摸模式改变等等。</p> <ul> <li> <p>添加OnPreDrawListener,在视图树将要绘制时回调</p> <pre> <code class="language-java">private void measureHeight() { textView2.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { //删除监听 textView2.getViewTreeObserver().removeOnPreDrawListener(this); height=textView2.getMeasuredHeight(); listener.changeData(1,height); return true; } }); }</code></pre> <p><strong>注意:</strong></p> <p>getMeasuredHeight()和getHeight()的区别?</p> <p>1、getMeasuredHeight()在measure过程结束之后就可以获取了;getHeight()要在layout过程结束之后才可以获取了。</p> <p>2、getMeasuredHeight()是通过setMeasuredDimension()进行设置的;getHeight()方法中的值则是通过视图下边的坐标减去上边的坐标计算出来的。</p> </li> </ul> <p style="text-align:center"><img src="https://simg.open-open.com/show/e1d898bbfda8f0a5b213467c1578f1da.png"></p> <p style="text-align:center">getHeight</p> <p><img src="https://simg.open-open.com/show/70fd4365a15571913e97bc049942e232.png"></p> <p style="text-align:center">MeasuredHeight</p> <ul> <li>添加OnGlobalLayoutListener,当一个视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变时调用这个回调函数。</li> </ul> <pre> <code class="language-java">private void measureHeight() { textView1.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { textView1.getViewTreeObserver().removeOnGlobalLayoutListener(this); height=textView1.getMeasuredHeight(); listener.changeData(0,height); } }); }</code></pre> <p>使用ViewTreeObserver也可以 <strong>检测软键盘的高度</strong> :</p> <p>在根布局加入GlobalLayoutListener监听,通过getWindowVisibleDisplayFrame方法可以观察可见区域的变化</p> <pre> <code class="language-java">root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { float height=root.getMeasuredHeight(); Rect rect=new Rect(); root.getWindowVisibleDisplayFrame(rect); Log.e("RecycleViewFragment", "bottom:"+rect.bottom + ",height:"+height) ; } });</code></pre> <p style="text-align:center"><img src="https://simg.open-open.com/show/2ea0b3fc4edd073c72e1a8c79bbe685c.gif"></p> <p style="text-align:center">测量软键盘状态和高度</p> <p>参考:</p> <p><a href="/misc/goto?guid=4959714695841158906" rel="nofollow,noindex">Viewtreeobserver解析</a></p> <h2><strong>3、ScrollView+ViewPager,会有手势冲突</strong></h2> <pre> <code class="language-java">public class CustomScrollView extends ScrollView { private float preX; private float preY; private float touchSlop; private boolean isViewPagerDragged; public CustomScrollView(Context context) { this(context, null); } public CustomScrollView(Context context, AttributeSet attrs) { super(context, attrs); touchSlop= ViewConfiguration.get(context).getScaledTouchSlop(); } /** * 在onInterceptTouchEvent()方法里, * 如果水平移动距离大于竖直移动距离,ScrollView不拦截这个事件 * @param ev * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { float currentX=ev.getX(); float currentY=ev.getY(); switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: preX=currentX; preY=currentY; isViewPagerDragged=false; break; case MotionEvent.ACTION_MOVE: if(isViewPagerDragged){ return false; } float dx=Math.abs(preX-currentX); float dy=Math.abs(preY-currentY); if(dx>dy && dx>touchSlop){ isViewPagerDragged=true; return false; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: isViewPagerDragged=false; break; } return super.onInterceptTouchEvent(ev); } }</code></pre> <p>参考:</p> <p><a href="/misc/goto?guid=4959714695942565987" rel="nofollow,noindex">Android嵌套滑动控件的冲突解决和ViewPager适配当前子控件高度不留空白的办法</a></p> <p><a href="/misc/goto?guid=4959714696025589186" rel="nofollow,noindex">最简单也最难——如何获取到Android控件的高度</a></p> <p><a href="/misc/goto?guid=4959714696104480386" rel="nofollow,noindex">关于Android中的ViewTreeObserver</a></p> <p> </p> <p> </p> <p>来自:http://www.jianshu.com/p/a06204e601de</p> <p> </p>