Android TV開發經驗總結---控制元件Gridview使用
前一篇我們已經講解的基本Android TV 開發的基礎技能,接下來聊聊TV開發中所涉及到的控制元件運用
目前Android TV 開發的資料不是蠻多,經過一系列的搜尋,目前比較適用的框架推薦
https://git.oschina.net/hailongqiu/AndroidTVWidget
在我們TV開發中用到最多的可能就是網格和列表了,今天我們先講講GridView,有人就會想現在不都是在用recyclerview嗎? 但是我告訴你,在TV開發中目前如果不使用google自己的LeackbackTV框架的話目前的原生recyclerview在開發中你會發現,上下左右快速滑動的時候焦點不能預期的達到效果,當然後面我會講解recyclerview在開發過程中的使用
系統中的原生GridView在開發過程中會出現哪些問題呢,我們又可以怎麼解決?
首先為了讓GridView能夠上下左右焦點位置不亂跑,我使用的是Androd-tv-frame中的GridViewTV控制元件,具體程式碼請看上面連線中的原始碼
然後在使用GridView過程中一般需要用到的監聽事件
gridView.setOnItemSelectedListener 選中事件
<pre name="code" class="java"> //選中事件 gridView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){ @Override public void onItemSelected (AdapterView < ? > parent, View view,int position, long id){ curposition = position; } @Override public void onNothingSelected (AdapterView < ? > parent){ } }); //點選事件 gridView.setOnItemClickListener(new AdapterView.OnItemClickListener(){ @Override public void onItemClick (AdapterView < ? > parent, View view,int position, long id){ } }); //是否獲取焦點事件 gridView.setOnFocusChangeListener(new View.OnFocusChangeListener(){ @Override public void onFocusChange (View view,boolean b){ } });
此處需要特別說明的是 gridView.setFocusable(true); 改變控制元件是否可以獲得焦點,然而同時會觸發 setOnFocusChangeListener事件
在GridView開發過程中還有很坑爹的問題,GridView在初始化或者重新setAdapter後在4.3版本以上會搶焦點預設選中第一個Item ,不光是GridView listview也是如此的情況,所以要避免這種情況的話需要給gridView.setFocusable(false)使得gridview不能搶焦
其實在開發中如果在4.3版本一下你需要預設第一個Item也是有方法的,可以看看
/**
* 判斷當前選中的位置在螢幕中的相對位置
*
* @param
* @return
*/
public static final int LEFT = 0;
public static final int RIGHT = 1;
public static final int BOTTOM = 2;
public static final int TOP = 3;
public int getGridSelectionState() {
int selection = gridView.getSelectedItemPosition();
int total = mLiveData.size();
if (total <= 0) {
return -1;
}
if(selection<gridView.getNumColumns()){
return TOP;
}
if (selection + gridView.getNumColumns() >= total) {
return BOTTOM;
}
if (selection % gridView.getNumColumns() == 0) {
return LEFT;
} else if (selection % gridView.getNumColumns() == gridView.getNumColumns()-1) {
return RIGHT;
} else {
return -1;
}
}
如果這時候你還需要通過方向來確定的話可以使用如下程式碼private int direction = -1;
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_LEFT:
direction = 1;
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
direction = 2;
break;
case KeyEvent.KEYCODE_DPAD_UP:
direction = 3;
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
direction = 4;
break;
default:
direction = -1;
break;
}
return super.dispatchKeyEvent(event);
}
有的時候我們需要分頁載入資料怎麼辦,我的思路是判斷當前選中的Item的位置是否在最底下,如果是就進行網路請求,在頁面的OnkeyDown事件中監聽,其實將邏輯封裝在自定義Gridview中然後暴露回撥接口出來
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_DOWN:
if (isGridFoucse) { //<isGridFoucse標示Gridview是否獲取了焦點
if (getGridSelectionState() == BOTTOM && isHaveMore) { //翻頁
//TODO:載入資料
return true;
}
}
break;
}
return false;
}
接著可能就是選中放大的效果了,之前我一直使用的還是上面框架中的放大效果,移動放大框效果很給力,只是在過程中你特別需要注意設定兩個屬性
android:clipToPadding="false" android:clipChildren="false" 具體有什麼用處你可以問問度娘,會解釋的很清楚。
但是由於後期配合專案整體UI的要求,框架不適合,那麼又如何有放大選中的效果呢,我的思路是設定Item放大後的背景,先隱藏當選中的時候對View進行放大正好是背景的大小,這就需要我們自己計算Item的寬高了,給個我實現的案例
先看看xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/px449"
android:layout_height="@dimen/px323"
android:orientation="vertical">
<LinearLayout
android:id="@+id/root_layout"
android:layout_width="@dimen/px400"
android:layout_height="@dimen/px286"
android:layout_centerInParent="true"
android:background="@drawable/shape_rect_grid_item_bg"
android:orientation="vertical"
android:padding="@dimen/px4">
</LinearLayout>
<View
android:id="@+id/grid_border"
android:layout_width="@dimen/px449"
android:layout_height="@dimen/px322"
android:layout_centerInParent="true"
android:background="@drawable/border_bg"
android:focusable="false"
android:visibility="invisible"/>
</RelativeLayout>
然後就是在選中中放大效果
gridView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
if (isGridFoucse && view != null) {
if (mOldView != null) {
LiveGridAdapter.ViewHolder oldholder = (LiveGridAdapter.ViewHolder) mOldView.getTag();
setGridBg(oldholder,false);
}
if (view != null) {
LiveGridAdapter.ViewHolder holder = (LiveGridAdapter.ViewHolder) view.getTag();
view.bringToFront();
setGridBg(holder, true);
}
mOldView = view;
curposition = position;
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
private void setGridBg(final LiveGridAdapter.ViewHolder oldholder,boolean toBig){
if(toBig){
oldholder.root_layout.animate().scaleX(1.09f).scaleY(1.09f).setDuration(DEFAULT_TRAN_DUR_ANIM)
.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
if (oldholder.grid_border != null) {
oldholder.grid_border.setVisibility(View.VISIBLE);
}
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}else{
oldholder.root_layout.animate().scaleX(1.0f).scaleY(1.0f).setDuration(DEFAULT_TRAN_DUR_ANIM)
.setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
if (oldholder.grid_border != null) {
oldholder.grid_border.setVisibility(View.INVISIBLE);
}
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
}
其實網上還有很多自定義Gridview 的放大效果,大家可以去搜索下看看