Android TV 自定義listview平滑滑動(dòng)

由于在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);
    }
}


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容