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