解決ViewPager與GridView巢狀的滑動不流暢和高度無法自適應
最近產品提了一個需求,要求把首頁的導航按鈕改成和美團類似的可翻頁的GridView。
乍一看,這不就是ViewPager巢狀兩個GridView嗎,簡單簡單。簡單的答應後,我就掉進了控制元件巢狀的大坑中。
簡單的寫好程式碼後,滿心歡喜的執行程式,咦???我的ViewPager去哪了?GridView呢?(黑人問號)在一番努力之後,我終於找到了問題的原因,當Viewpager和GridView巢狀時,如果ViewPager的高度設定成warp_content,會導致控制元件不顯示。解決這個問題有兩個方案。
一、既然warp_content不行,那麼給ViewPager直接設定一個高度!搞定,簡單省事,不過這樣有一個缺點,就是沒法保證所有機型適配,而且有時候GridView內容多,有兩行並分頁,有時候內容少,只有一行,那麼就會空出一部分的高度,導致介面不美觀。
二、上面的方法有點太簡單粗暴了,這裡就介紹一下我現在用的方法,自定義一個ViewPager,在onMeasure中計算他的子View的高度,取最高的來作為自己的高度。程式碼如下
/** * 根據測量的子view設定pager高度 * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = 0; for (int i = 0; i < getChildCount();i++) { View child = getChildAt(i); child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); int h = child.getMeasuredHeight(); if (h > height) height = h; } heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
當然,用了方法二之後,如果你用的是普通的GridView和我們自定義的ViewPager巢狀的話,會發現一個事情,那就是我們的GridView的第二行不見了!只剩下了一行,又是巢狀的鬼問題,GridView顯示不全了,這裡我們的GridView也需要自定義一下,這個就比較普遍了,大家一般都是用的這個,重寫一下GridView的onMeasure。
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); }
好了!大功告成!我們的ViewPager可以和GridView巢狀並且能自適應高度啦!開心的趕緊上去滑一滑,可是一碰,感覺有點不對勁。滑動的時候總感覺沒那麼靈敏,很難滑到第二頁,這也是因為控制元件巢狀導致手勢衝突。沒關係,我們接著回到我們自定義的ViewPager中,重寫他的dispachTouchEvent方法
private float mPointX; private float mPointY; /** * 解決巢狀後滑動不靈敏問題 * @param ev * @return */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mPointX = ev.getX(); mPointY = ev.getY(); getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: if (Math.abs(ev.getX() - mPointX) > Math.abs(ev.getY() - mPointY)) { getParent().requestDisallowInterceptTouchEvent(true); } else { getParent().requestDisallowInterceptTouchEvent(false); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: getParent().requestDisallowInterceptTouchEvent(false); break; default: break; } return super.dispatchTouchEvent(ev); }
記錄一下手指按下時的X,Y座標,然後在移動通過計算,如果左右移動的話就阻止父控制元件攔截事件,這樣就能在左右滑動的時候靈活滑動了。
好了,這個問題解決以後,我們的控制元件應該就比較完整了,其他都是GridView和ViewPager的老套路。終於跳出了GridView和ViewPager巢狀的大坑,希望能夠幫助到有同樣需求的同學。