GridView & ListView (Android盒子開發)

一.GridView在Android4.4下關於焦點的bug的解決方式

Android4.4下,關於GridView有一個關於焦點的bug。這個bug並不容易被發現,可是在電視盒子的開發過程當中就很明顯。具體表現是,Gridview會一直持有一個焦點,只要有數據,就一直會有一個子View是Selected狀態。並且,當數據從無到有的時候,GridView還會搶焦點。 android

最後解決這個bug的方式是這樣的,創建一個自定義的GridView,重寫一個方法, 數組

@Override
public boolean isInTouchMode() {
    return !(hasFocus() && !super.isInTouchMode());
}

我並無弄懂爲啥,可是加上這句話確實就解決了這個bug。 ide

二.選中項有放大效果的GirdView的實現

先在AnimaFactory類中包裝靜態縮放動畫。 佈局

/**
 * 縮放動畫,用於縮放控件
 * @param startScale 控件的起始尺寸倍率
 * @param endScale 控件的終點尺寸倍率
 * @return
 */
public static Animation zoomAnimation(float startScale, float endScale, int duration){
    ScaleAnimation anim = new ScaleAnimation(startScale, endScale, startScale, endScale,
            Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
    anim.setFillAfter(true);
    anim.setDuration(duration);
    return anim;
}

而後在自定義GridView中加入以下方法。 動畫

/**
 * 實現本類onFocusChangeListener時調用此方法
 * @param v
 * @param hasFocus
 */
public void onFocusChange(View v, boolean hasFocus) {
    if (hasFocus) {
        zoomInView(lastView);
    } else {
        zoomOutView(lastView);
    }
}

/**
 * 外面調用OnItemSelectedListener的時候,調用這個方法
 * @param parent
 * @param view
 * @param position
 * @param id
 */
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    zoomInView(view);
    if (view != lastView) {
        zoomOutView(lastView);
    }
    lastView=view;
}

/**
 * 放大
 * @param v
 */
private void zoomInView(View v) {
    if (v != null) {
        v.startAnimation(AnimaFactory.zoomAnimation(1.0f, 1.1f, 100));
    }
}

/**
 * 縮小
 * @param v
 */
private void zoomOutView(View v) {
    if (v != null) {
        v.startAnimation(AnimaFactory.zoomAnimation(1.1f, 1.0f, 100));
    }
}

其中,onSelected方法的調用效果是,把正在選中的View放大,把上一個選中的View即lastView縮小。而後lastView更新爲當前選中的View。 this

這個方法在外面給GridView設置OnSelectedListener的時候調用。 spa

同理,設置OnFocusChangeListener中要調用的onFucusChange方法。 操作系統

這裏還有一點要說的,雖然選中項放大了,可是它並不必定被畫在最前面。爲了保證不會被擋住,咱們還要加一個方法。 code

/**
 * @description: <修改默認加載順序(核心代碼)>
 * @see android.view.ViewGroup#getChildDrawingOrder(int, int)
 * @param childCount 當前屏幕中顯示的item個數
 * @param i 在數組中的位置,從0開始到childCount-1
 * @return 加載順序
 */
@Override
protected int getChildDrawingOrder(int childCount, int i) {
    if (this.getSelectedItemPosition() != -1) {
        if (i + this.getFirstVisiblePosition() == this.getSelectedItemPosition()) {// 這是本來要在最後一個刷新的item
            return childCount - 1;
        }
        if (i == childCount - 1) {// 這是最後一個須要刷新的item
            return this.getSelectedItemPosition() - this.getFirstVisiblePosition();
        }
    }
    return i;
}

這段代碼加上後,要在構造方法中加一句, ip

setChildrenDrawingOrderEnabled(true);

來保證加載順序正確。

三.重置數據setList()方法中要注意的

有兩種重置數據的方法,若是在Adapter中傳入的數據,那麼直接給gridView調用setAdapter即能重置數據。

另外一種,是在Adapter中,寫setList方法來傳入數據。好比:

public void setList(List<VideoObject> list){
    notifyDataSetInvalidated();
    mList=list;
    notifyDataSetChanged();
}

notifiDataSetChange()方法比較好理解,由於重置數據後要刷新數據。那notifyDataSetInvalidated()起什麼做用呢?它的做用是通知Adatper舊數據無效。若是不寫這行代碼,在重置數據的時候,可能會出現數組越界異常。

四.關於分頁加載數據

由於電視盒子沒有觸屏,是經過遙控控制焦點來操做系統的。因此分頁加載不是經過「下拉刷新」,而是經過焦點判斷。而判斷語句,在OnSelectedChangeListener中實現。

首先,要寫代碼來肯定要須要請求數據的那一行的行數。這裏我設爲lastLineNum。

lastLineNum = (list.size() - 1) / gridView.getNumColumns();

這裏的list是獲取的數據,接下來設置監聽。

gridView.setOnItemSelectedListener(new OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        gridView.onItemSelected(parent, view, position, id);
        // 若是是分頁加載最後一排,則要求分頁數據
        if (lastLineNum != 0 && position / gridView.getNumColumns() == lastLineNum) {
            //在這裏請求數據並更新視圖
        }
        @Override
        public void onNothingSelected(AdapterView<?> parent) {

        }
});

請求數據更新視圖,就在Adapter中addList,而後notifyDataSetChange就能夠了。

五.獲取正在顯示的數據位置以及出現bug的修正方式

GridView和ListView都有一個方法是getFirstVisiblePosition(),是獲取第一個正在顯示的數據的位置。可是這個方法有個缺陷,就是當數據,只顯示一半甚至更少的時候,這個方法判斷它已經顯示了。然而它並無徹底顯示。

我當時要實現的功能是當ListView上側有數據沒有顯示的時候,上側有一個箭頭,而ListView下側有數據沒有顯示的時候,下側有一個箭頭。這兩個箭頭都是ImageView。

後來解決這個缺陷是用下面的這種方式。

lvChildCategory.setOnScrollListener(new OnScrollListener() {
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (totalItemCount == 0 || visibleItemCount == 0) {
            //當無數據顯示的時候,上下側兩個箭頭均不顯示
            ivArrayUp.setVisibility(View.INVISIBLE);
            ivArrayDown.setVisibility(View.INVISIBLE);
            return;
        } else {
            //當有數據顯示的時候
            if (firstVisibleItem > 0) {
                //當第一個顯示的數據不是第一個數據時,箭頭顯示
                ivArrayUp.setVisibility(View.VISIBLE);
            } else {
                //當第一個顯示的數據是第一個數據的時候,加判斷
                //第一個數據的上座標
                int firstTop = view.getChildAt(0).getTop();
                //ListView的上座標
                int paddingTop = view.getListPaddingTop();
                //若是第一個數據的上座標沒超過了ListView的邊界,那麼上箭頭不顯示
                int spaceAbove = paddingTop - firstTop;
                if (spaceAbove <= 0)
                    ivArrayUp.setVisibility(View.INVISIBLE);
            }
            //下箭頭與上箭頭同理
            if (firstVisibleItem + visibleItemCount < totalItemCount) {
                ivArrayDown.setVisibility(View.VISIBLE);
            } else {
                int lastBottom = view.getChildAt(view.getChildCount() - 1).getBottom();
                int paddingBottom = view.getListPaddingBottom();
                if (lastBottom <= (view.getHeight() - paddingBottom))
                    ivArrayDown.setVisibility(View.INVISIBLE);
            }
        }
    }
});

即獲取子View的上座標和ListView的上座標來判斷。解決此缺陷。

六.GridView的視圖小細節

GridView中,selector會影響子佈局的排版。通常並不明顯,可是若是selector是一層比較厚的陰影的話,子佈局加上selector則會縮水一圈。

要解決這個問題,能夠調整GirdView的horizontalSpacing和verticalSpacing屬性,分別表示子佈局橫向間隙和縱向間隙,這兩個值是能夠設置爲負數的。若是子佈局加上selector後縮水太嚴重,這兩個值調整爲負數能解決這個問題。

同時,GridView的子佈局寬度是沒法調整的,也是要經過設置numColumns和horizontalSpacing來調整寬度。

相關文章
相關標籤/搜索