Android TV壁纸库之GridView使用及焦点处理

首先,给出整个代码的效果图:




这是整个壁纸库应用的效果图,项目地址在这里

一、GridView的介绍:

官网地址


Gridview是一个ViewGroup,它展示的是一个二维的,可滑动的表格.这个表格的item是自动插入的布局,默认使用一个ListAdapter容器。

1.GridView填充数据,这个东西网上的内容太多了,这里不再赘述,只是有一个点需要注意一下:

gridview在笔者实现放大和缩小动画效果的过程中,为了保持选中的item属于最后执行的效果,重写了一下Gridview的getChildDrawingOrder()方法;同时为了避免Gridview在测量item的时候过渡绘制,导致界面卡顿,重写了onMeasure()方法,这里对此作出解释。

代码如下:

/**
 * Created by fengjw on 2018/3/12.
 */

public class MyGridView extends GridView {

    public boolean isOnMeasure;

    public MyGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setChildrenDrawingOrderEnabled(true); //这里设置为true才能调用getChildDrawingOrder()方法
    }

    @Override
    public void setSelection(int position) {
        Constants.debug("MyGridView setSelection position : " + position);
        Constants.position = position;
        super.setSelection(position);
    }



    @Override
    protected void setChildrenDrawingOrderEnabled(boolean enabled) {
        super.setChildrenDrawingOrderEnabled(enabled);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        isOnMeasure = true;
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        isOnMeasure = false;
        super.onLayout(changed, l, t, r, b);
    }



    @Override
    protected int getChildDrawingOrder(int childCount, int i) {
        if (this.getSelectedItemPosition() != -1){ //不选中默认为-1(INVALID_POSITION),一般从0开始
            if (i + this.getFirstVisiblePosition() == this.getSelectedItemPosition()){//getFirstVisiblePosition获得的是第一个item的位置
                return childCount - 1;
            }
            if (i == childCount - 1){
                return this.getSelectedItemPosition() - this.getFirstVisiblePosition();
            }
        }
        return i;
    }
}
【关于避免过度测绘的问题, 请看这里

一般来说,GridView的绘制顺序是自上而下的,这样会导致后面的item覆盖了前面的item,所以我们就重写getChildDrawingOrder()方法来改变绘制的顺序。

实现的逻辑是这样的:当我们选中某个item的时候,当前item 也就是i+this.getFirstVisiblePosition的值如果等于选中的position,那么就return childCount -1;也就是最后一个item;不然的话就返回当前的item。这样就会让最后一个绘制的item是我们选择的item。

2.GridView的常用方法介绍:

常用参数:

columnWidth:设置每列固定宽度

gravity:设置每个item的中心

horizontalSpacing:定义水平行间距

numColumns:设置每列显示的数目

stretchMode:定义如何使用多余的空空间.

verticalSpacing:定义垂直行间距

get常用方法:

getNumColumns:得到grid每列的数目

set常用方法:

setNumColumns:设置每列显示的数目

setOnKeyListener:gridview作为一个view的按键监听

setOnItemClickListener:gridview内部item的点击监听

setOnItemSelectedListener:gridview内部选中的监听

setAdapter:设置adapter

setFocusable:设置gridview是否获得焦点

另一个是gridview的刷新:

gridview的刷新主要通过两种方式:

a.setAdapter来刷新容器内容

b.使用容器依赖的Adapter的notifyDataSetChanged().

二、GridView的使用:

1.首先设置一个GridView在资源文件中,并使用自定义MyGridView来extends GridView,重写一些方法。

scrollbars:设置为none,不让侧边的滚动条出现。

listSelector设置的目的是为了消除选中的item的背景黄色。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
     <item android:state_selected="true">
        <shape xmlns:android="http://schemas.android.com/apk/res/android">
            <solid android:color="#00000000"/>
        </shape>
    </item>
    <item android:state_pressed="true">
        <shape xmlns:android="http://schemas.android.com/apk/res/android">
            <solid android:color="#00000000"/>
        </shape>
    </item>
    <item android:state_focused="true">
        <shape xmlns:android="http://schemas.android.com/apk/res/android">
            <solid android:color="#00000000"/>
        </shape>
    </item>
</selector>

2.在onCreate中设置Gridiveiw的属性,因为是TV开发,我们这里主要注意焦点的位置及处理。

private void initGridView() {
        mGridAdapter = new GridAdapter(this, mList, gridview);
        gridview.setAdapter(mGridAdapter);
        gridview.setOnKeyListener(new GridViewOnKeyListener());
        gridview.setOnItemClickListener(new GridViewOnItemClickListener());
        gridview.setOnItemSelectedListener(new GridViewOnItemSelectedListener());
    }

如最开始的UI效果图,我们在离开gridview和进入gridview的时候,要手动进行focus的设置。为了记录原来离开时Focus的位置,我们定义一个全局的变量,如图:


在onItemSelectedListener中,我们每次进行一次改变当前item的操作,都记录一下position:

private class GridViewOnItemSelectedListener implements AdapterView.OnItemSelectedListener{
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Constants.debug("onItemSelected position : " + position);
            Constants.position = position;
            currentSelectPosition = position;
            mGridAdapter.setSelection(position);
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
            Constants.debug("onNothingSelected");
            mGridAdapter.setSelection(-2);
        }
    }

而每次按键的时候,都会响应onKey()操作,我们在GridView的onkey监听中做一下position的处理:

private class GridViewOnKeyListener implements View.OnKeyListener{

        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            Constants.debug("GridViewOnKeyListener onKey()");
            if (event.getAction() == KeyEvent.ACTION_DOWN){
                if (keyCode == KeyEvent.KEYCODE_DPAD_UP ){//这里是按键向上的操作
                    if (currentSelectPosition == 3){ //当前选中的position
                        mGridAdapter.setSelection(-2);
                        mMainTopMarketRlRoot.setFocusable(false);
                        mMainTopHomeRlRoot.setFocusable(false);
                        mMainArrowUpRlRoot.setVisibility(View.INVISIBLE);
                        mMainArrowDownRlRoot.setVisibility(View.INVISIBLE);
                        wifiFg.getView().setFocusable(true);
                    }
                    if (currentSelectPosition >= 0 && currentSelectPosition <= 2){
                        Constants.debug(".........");
//                        Constants.position = -2;
//                        currentSelectPosition = -2;
                        mGridAdapter.setSelection(-2);
                        mMainTopMarketRlRoot.setFocusable(true);
                        mMainTopHomeRlRoot.setFocusable(false);
                        mMainArrowUpRlRoot.setVisibility(View.INVISIBLE);
                        mMainArrowDownRlRoot.setVisibility(View.INVISIBLE);
                    }else if (currentSelectPosition > 11){
                        mMainArrowUpRlRoot.setVisibility(View.VISIBLE);
                        mMainArrowDownRlRoot.setVisibility(View.INVISIBLE);
                    }else if (currentSelectPosition >= 8 && currentSelectPosition <=11){
                        mMainArrowUpRlRoot.setVisibility(View.VISIBLE);
                        mMainArrowDownRlRoot.setVisibility(View.VISIBLE);
                    }else if (currentSelectPosition >= 4 && currentSelectPosition <=7){
                        mMainArrowUpRlRoot.setVisibility(View.INVISIBLE);
                        mMainArrowDownRlRoot.setVisibility(View.VISIBLE);
                    }

                }

                if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN){
                    if (currentSelectPosition >= 8 && currentSelectPosition <= 11){
                        mMainArrowUpRlRoot.setVisibility(View.VISIBLE);
                        mMainArrowDownRlRoot.setVisibility(View.INVISIBLE);
                    }else if (currentSelectPosition >= 4 && currentSelectPosition <= 7){
                        mMainArrowUpRlRoot.setVisibility(View.VISIBLE);
                        mMainArrowDownRlRoot.setVisibility(View.VISIBLE);
                    }
                }

            }
            return false;
        }
    }

同时,我们要更新GridView内容,通过在GridAdapter中设置一个方法体来实现:

public void setSelection(int position){
        selectItem = position;
        Constants.debug("selectItem : " + selectItem);
        settingPosition = mPreference.loadSharedPreferences("settingPosition", -2);
        Constants.debug("----------------------");
        Constants.debug("settingPosition : " + settingPosition);
        Constants.debug("----------------------");
        super.notifyDataSetChanged();//刷新adapter
    }

刷新adapter会调用getView方法,从而实现数据的改变操作。


同理,当我们在进入GridView的时候,设置GridView的setFocusable为true,并调用GridAdapter的setSelection方法来达到更新效果。

三、fragment焦点改变的问题

UI效果图第一张的右上角的图标在获得焦点后无法通过监听key值来更改Focus,发现该控件为fragment有很大关系。

所以,我选择了一种方法,通过监听window层的onKeyDown事件来处理,并通过获得当前Focus的View Id方式来手动更改焦点的位置,代码如下:

public boolean onKeyDown(int keyCode, KeyEvent event) {
        Constants.debug("onKeyDown");
        View rootview = this.getWindow().getDecorView();
        int focusId = rootview.findFocus().getId();//这里是获得当前focus id的方法
        Constants.debug("focusId : " + focusId);
        if (focusId == R.id.root_main_top_wifi_fg){
            if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT){
                wifiFg.getView().setFocusable(false);
                mMainTopHomeRlRoot.setFocusable(false);
                mMainTopMarketRlRoot.setFocusable(true);
                gridview.setFocusable(false);
            }
            if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DPAD_DOWN){
                wifiFg.getView().setFocusable(false);
                mMainTopHomeRlRoot.setFocusable(false);
                mMainTopMarketRlRoot.setFocusable(false);
                gridview.setFocusable(true);
                gridview.requestFocus();
                currentSelectPosition = Constants.position;
                mGridAdapter.setSelection(Constants.position);
                mMainArrowUpRlRoot.setVisibility(View.INVISIBLE);
                mMainArrowDownRlRoot.setVisibility(View.VISIBLE);
            }
        }
        return super.onKeyDown(keyCode, event);//不阻断按键时间的传递
    }
 

总结:GridView不难,但是要快速上手,还是需要理解一些基础性的东西才行。

========================================

补充:2018.06.08

解决了onCreate gridview的时候,第一个item选中无法放大的方法:

执行onItemSelection的时候,选中第一个item默认会执行放大的动画,但是在第一次启动有Gridview的界面并默认让第一个item持有focus的时候,大多数情况下并不会有该动画,这其实是GridView设计上的一个缺陷。解决方法很简单:

我们调用Gridiview支持的方法会发现,每次当GridView第一次获得焦点的时候,都首次会执行一个onFocusChange的方法,那么我们在这里从新执行setSelection(0),并通过notifyDataSetChanged刷新adapter,来达到我们的目的。

如图:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值