1. 程式人生 > >解決ViewPager與GridView巢狀的滑動不流暢和高度無法自適應

解決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巢狀的大坑,希望能夠幫助到有同樣需求的同學。