一.GridView在Android4.4下关于焦点的bug的解决方式
Android4.4下,关于GridView有一个关于焦点的bug。这个bug并不容易被发现,但是在电视盒子的开发过程中就很明显。具体表现是,Gridview会一直持有一个焦点,只要有数据,就一直会有一个子View是Selected状态。而且,当数据从无到有的时候,GridView还会抢焦点。
最后解决这个bug的方式是这样的,建立一个自定义的GridView,重写一个方法,
@Override public boolean isInTouchMode() { return !(hasFocus() && !super.isInTouchMode()); }
我并没有弄懂为啥,但是加上这句话确实就解决了这个bug。
二.选中项有放大效果的GirdView的实现
先在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。
这个方法在外面给GridView设置OnSelectedListener的时候调用。
同理,设置OnFocusChangeListener中要调用的onFucusChange方法。
这里还有一点要说的,虽然选中项放大了,但是它并不一定被画在最前面。为了保证不会被挡住,我们还要加一个方法。
/** * @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; }
这段代码加上后,要在构造方法中加一句,
setChildrenDrawingOrderEnabled(true);
来保证加载顺序正确。
三.重置数据setList()方法中要注意的
有两种重置数据的方法,如果在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就可以了。
五.获取正在显示的数据位置以及出现bug的修正方式
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的视图小细节
GridView中,selector会影响子布局的排版。一般并不明显,但是如果selector是一层比较厚的阴影的话,子布局加上selector则会缩水一圈。
要解决这个问题,可以调整GirdView的horizontalSpacing和verticalSpacing属性,分别表示子布局横向间隙和纵向间隙,这两个值是可以设置为负数的。如果子布局加上selector后缩水太严重,这两个值调整为负数能解决这个问题。
同时,GridView的子布局宽度是无法调整的,也是要通过设置numColumns和horizontalSpacing来调整宽度。
GridView & ListView (Android盒子开发)