由于在TV上面的操作不是通過手指來滑動(dòng),需要用遙控器來操作,所以Listview中item的焦點(diǎn)處理就顯得很重要,但自帶的listview會(huì)有一個(gè)缺點(diǎn)導(dǎo)致操作體驗(yàn)不是很好,那就是item的焦點(diǎn)會(huì)隨著上下按鍵顯示在最上方和最下方,而且也沒有動(dòng)畫效果。本篇文章介紹一種焦點(diǎn)在中間位置,listview上下滑動(dòng)的實(shí)現(xiàn)方法,這種效果在TV上體驗(yàn)比較好,效果圖如下:

Listview.gif
使用到的方法有:
1.重繪焦點(diǎn)的位置
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (this.isFocused()) {// 繪制焦點(diǎn)框的位置
Log.i(TAG, "onDraw = =======");
Rect rect = new Rect();
rect.set(0, itemHeight * itemFocusInCenter
+ getItemFocusCurrentPos(), itemWidth, itemHeight
* (itemFocusInCenter + 1) + getItemFocusCurrentPos());
mSelector.setBounds(rect);
mSelector.draw(canvas);
}
}
根據(jù)每次移動(dòng)的距離去重新繪制焦點(diǎn)框的位置,當(dāng)焦點(diǎn)框處于中間位置的時(shí)候就去移動(dòng)listview,這時(shí)候用到的就是smoothScrollBy(int distance, int duration),其中distance參數(shù)表示移動(dòng)的距離,duration表示移動(dòng)的時(shí)間。
2.在onKeyDown按鍵事件里去判斷并計(jì)算移動(dòng)的距離
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
View view = getSelectedView();
Log.i(TAG, "onKeyDown ###############################");
int currentPositon = getSelectedItemPosition();
if (view == null)
return false;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_DOWN:
if(itemFocusInCenter <= currentPositon){
smoothScrollBy(itemHeight, scrollDuration);
}
handler.removeMessages(SHOW_CURRENT_FOCUS);
handler.sendEmptyMessageDelayed(SHOW_CURRENT_FOCUS, 200);
break;
case KeyEvent.KEYCODE_DPAD_UP:
if(currentPositon < itemsCount - itemFocusInCenter){
smoothScrollBy(-itemHeight, scrollDuration);
}
handler.removeMessages(SHOW_CURRENT_FOCUS);
handler.sendEmptyMessageDelayed(SHOW_CURRENT_FOCUS, 200);
break;
}
return super.onKeyDown(keyCode, event);
}
以上就是實(shí)現(xiàn)該效果的重要代碼,最重要的就是焦點(diǎn)位置的判斷以及焦點(diǎn)的重繪和每次移動(dòng)距離的計(jì)算,理解了這幾點(diǎn)之后,實(shí)現(xiàn)起來就容易多了。
順便把全部代碼貼出來:
public class TvSmoothMoveListView extends ListView {
private String TAG = "TvSmoothMoveListView";
private int mItemsCount,mItemHeight,mItemWidth;
private int mShowItemSize = 7;//顯示的item的個(gè)數(shù)
private int mItemCenterFocus;//中間位置
private int mCurrentPositon;
private int mDataSize = 0;
private ListAdapter adapter;
private int scrollDuration = 300;
private final int SHOW_CURRENT_FOCUS = 0;
private int mColorWhite = Color.parseColor("#ffffff");
private int mColorBlue = Color.parseColor("#42b3ff");
private Drawable mSelector = null;
public TvSmoothMoveListView(Context context) {
super(context);
}
public TvSmoothMoveListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TvSmoothMoveListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private void init(){
mSelector = getResources().getDrawable(R.drawable.selected2);
setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// 預(yù)防切換焦點(diǎn)的時(shí)候出現(xiàn)錯(cuò)亂的情況
if (scrollState == SCROLL_STATE_IDLE) {
View itemView = getChildAt(0);
if (itemView != null) {
float deltaY = itemView.getY();
if (deltaY == 0) {
return;
}
if (Math.abs(deltaY) < mItemHeight / 2) {
smoothScrollBy(getDistance(deltaY), 10);
} else {
smoothScrollBy(getDistance(mItemHeight + deltaY), 10);
}
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
}
});
}
private int getDistance(float scrollDistance) {
if (Math.abs(scrollDistance) <= 2) {
return (int) scrollDistance;
} else if (Math.abs(scrollDistance) < 12) {
return scrollDistance > 0 ? 2 : -2;
} else {
return (int) (scrollDistance / 6);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (adapter != null && getChildAt(0) != null) {
mItemHeight = getChildAt(0).getHeight();
mItemCenterFocus = mShowItemSize / 2;
}
}
@Override
protected void onMeasure(int arg0, int arg1) {
super.onMeasure(arg0, arg1);
mItemWidth = getWidth();
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (this.isFocused()) {// 繪制焦點(diǎn)框的位置
Rect rect = new Rect();
rect.set(0, mItemHeight * mItemCenterFocus
+ getItemFocusCurrentPos(), mItemWidth, mItemHeight
* (mItemCenterFocus + 1) + getItemFocusCurrentPos());
mSelector.setBounds(rect);
mSelector.draw(canvas);
}
}
/**
* 通過當(dāng)前焦點(diǎn)位置計(jì)算出來需要移動(dòng)的距離
* @return
*/
private int getItemFocusCurrentPos(){
int currentSelectPos = getSelectedItemPosition();
if(mItemCenterFocus > currentSelectPos){
return (currentSelectPos - mItemCenterFocus) * mItemHeight;
}
if(currentSelectPos > (mItemsCount - 1) - mItemCenterFocus){
return (currentSelectPos - ((mItemsCount - 1) - mItemCenterFocus)) * mItemHeight;
}
return 0;
}
@Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
this.adapter = adapter;
mItemsCount = adapter.getCount();
Log.i(TAG, "getCount ################# " + mItemsCount);
}
/**
* 移動(dòng)到頂端
*/
public void startMoveTop(){
setSelectionFromTop(mItemCenterFocus, mItemHeight * mItemCenterFocus);
}
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case SHOW_CURRENT_FOCUS://防止選中的item和焦點(diǎn)不在一起
int currentSelectPos = getSelectedItemPosition();
setSelectionFromTop(currentSelectPos, mItemHeight * mItemCenterFocus + getItemFocusCurrentPos());
break;
default:
break;
}
};
};
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
View view = getSelectedView();
Log.i(TAG, "onKeyDown ###############################");
int currentPositon = getSelectedItemPosition();
if (view == null)
return false;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_DOWN:
if(mItemCenterFocus <= currentPositon){
smoothScrollBy(mItemHeight, scrollDuration);
}
handler.removeMessages(SHOW_CURRENT_FOCUS);
handler.sendEmptyMessageDelayed(SHOW_CURRENT_FOCUS, 200);
break;
case KeyEvent.KEYCODE_DPAD_UP:
if(currentPositon < mItemsCount - mItemCenterFocus){
smoothScrollBy(-mItemHeight, scrollDuration);
}
handler.removeMessages(SHOW_CURRENT_FOCUS);
handler.sendEmptyMessageDelayed(SHOW_CURRENT_FOCUS, 200);
break;
}
return super.onKeyDown(keyCode, event);
}
}