Android4.4下,關於GridView有一個關於焦點的bug。這個bug並不容易被發現,可是在電視盒子的開發過程當中就很明顯。具體表現是,Gridview會一直持有一個焦點,只要有數據,就一直會有一個子View是Selected狀態。並且,當數據從無到有的時候,GridView還會搶焦點。 android
最後解決這個bug的方式是這樣的,創建一個自定義的GridView,重寫一個方法, 數組
@Override public boolean isInTouchMode() { return !(hasFocus() && !super.isInTouchMode()); }
我並無弄懂爲啥,可是加上這句話確實就解決了這個bug。 ide
先在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);
來保證加載順序正確。
有兩種重置數據的方法,若是在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就能夠了。
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中,selector會影響子佈局的排版。通常並不明顯,可是若是selector是一層比較厚的陰影的話,子佈局加上selector則會縮水一圈。
要解決這個問題,能夠調整GirdView的horizontalSpacing和verticalSpacing屬性,分別表示子佈局橫向間隙和縱向間隙,這兩個值是能夠設置爲負數的。若是子佈局加上selector後縮水太嚴重,這兩個值調整爲負數能解決這個問題。
同時,GridView的子佈局寬度是沒法調整的,也是要經過設置numColumns和horizontalSpacing來調整寬度。