package reco.frame.tv.view; import reco.frame.tv.R; import reco.frame.tv.view.component.RecycleBin; import reco.frame.tv.view.component.TvBaseAdapter; import reco.frame.tv.view.component.TvUtil; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.database.DataSetObservable; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.Scroller; /** * 横向的gridview 自带选中框效果 * * @author keYence * */ public class TvHorizontalGridView extends RelativeLayout { /** * 光标 */ private ImageView cursor; private int cursorId; /** * 光标资源 */ private int cursorRes; /** * item可否缩放 */ private boolean scalable; /** * 放大比率 */ private float scale; /** * 光标飘移动画 默认无效果(尚未实现) */ private int animationType; public final static int ANIM_DEFAULT = 0;// 无效果 public final static int ANIM_TRASLATE = 1;// 平移 /** * 放大用时 */ private int durationLarge = 100; /** * 缩小用时 */ private int durationSmall = 100; /** * 平移用时 */ private int durationTraslate = 100; /** * 放大延迟 */ private int delay = 0; /** * 滚动速度 */ private int scrollDelay = 0; /** * 滚动速度 */ private int scrollDuration = 0; /** * 光标边框宽度 包括阴影 */ private int boarder; /** * 光标左边框宽度 含阴影 */ private int boarderLeft; /** * 光标顶边框宽度 含阴影 */ private int boarderTop; /** * 光标右边框宽度 含阴影 */ private int boarderRight; /** * 光标底边框宽度 含阴影 */ private int boarderBottom; /** * 外层容器布局是否改变 */ private boolean parentLayout = true; /** * 焦点移出容器 */ private boolean focusIsOut; /** * 除光标外 当前子类数 */ private int currentChildCount = 0; /** * 可否滚动 */ private final int ACTION_START_SCROLL = 0, ACTION_INIT_ITEMS = 1, ACTION_ADD_ITEMS = 2; /** * 刷新延迟 */ private final int DELAY = 371; /** * 行数 */ private int rows; private int colCount, selectCol; /** * 屏幕可显示最大列数 */ private int screenMaxColumns; /** * 当前选中子项下示 */ private int selectIndex; private int paddingLeft, paddingTop; private int spaceHori; private int spaceVert; /** * item宽高 不包括纵横间距 */ private int itemWidth, itemHeight; /** * item真实宽高 包括纵横间距 */ private int rowWidth, rowHeight; private SparseArray<Integer> itemIds; private OnItemSelectListener onItemSelectListener; private OnItemClickListener onItemClickListener; public AdapterDataSetObservable mDataSetObservable; private TvBaseAdapter adapter; private AnimatorSet animatorSet; private ObjectAnimator largeX; private WindowManager wm; private Scroller mScroller; private RecycleBin mRecycleBin; private boolean isInit = true; private boolean initFocus; /** * 以1280为准,其余分辨率放大比率 用于适配 */ private float screenScale = 1; private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case ACTION_START_SCROLL: int direction = (Integer) msg.obj; scrollByRow(direction); break; case ACTION_INIT_ITEMS: initItems(); break; case ACTION_ADD_ITEMS: addNewItems(); break; } }; }; public TvHorizontalGridView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public TvHorizontalGridView(Context context) { super(context); } public TvHorizontalGridView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray custom = getContext().obtainStyledAttributes(attrs, R.styleable.TvHorizontalGridView); this.cursorRes = custom.getResourceId(R.styleable.TvHorizontalGridView_cursorRes, 0); this.scalable = custom .getBoolean(R.styleable.TvHorizontalGridView_scalable, true); this.initFocus = custom.getBoolean(R.styleable.TvHorizontalGridView_initFocus, true); this.scale = custom.getFloat(R.styleable.TvHorizontalGridView_scale, 1.1f); this.animationType = custom.getInt( R.styleable.TvHorizontalGridView_animationType, ANIM_DEFAULT); this.delay = custom.getInteger(R.styleable.TvHorizontalGridView_delay, 110); this.scrollDelay = custom.getInteger( R.styleable.TvHorizontalGridView_scrollDelay, 171); this.scrollDuration = custom.getInteger( R.styleable.TvHorizontalGridView_scrollDuration, 371); this.durationLarge = custom.getInteger( R.styleable.TvHorizontalGridView_durationLarge, 230); this.durationTraslate=custom.getInteger( R.styleable.TvHorizontalGridView_durationTranslate, 170); this.durationSmall = custom.getInteger( R.styleable.TvHorizontalGridView_durationSmall, 230); this.rows = custom.getInteger(R.styleable.TvHorizontalGridView_rows, 2); this.spaceHori = (int) custom.getDimension( R.styleable.TvHorizontalGridView_spaceHori, 10); this.spaceVert = (int) custom.getDimension( R.styleable.TvHorizontalGridView_spaceVert, 10); itemWidth = (int) custom.getDimension(R.styleable.TvHorizontalGridView_itemWidth, 10); itemHeight = (int) custom.getDimension( R.styleable.TvHorizontalGridView_itemHeight, 10); rowWidth = itemHeight + spaceVert; rowWidth = itemWidth + spaceHori; paddingLeft = (int) custom.getDimension( R.styleable.TvHorizontalGridView_paddingLeft, 0); paddingTop = (int) custom.getDimension( R.styleable.TvHorizontalGridView_paddingTop, 0); this.boarder = (int) custom.getDimension( R.styleable.TvHorizontalGridView_boarder, 0) + custom.getInteger(R.styleable.TvHorizontalGridView_boarderInt, 0); if (boarder == 0) { this.boarderLeft = (int) custom.getDimension( R.styleable.TvHorizontalGridView_boarderLeft, 0) + custom.getInteger(R.styleable.TvHorizontalGridView_boarderLeftInt, 0); this.boarderTop = (int) custom.getDimension( R.styleable.TvHorizontalGridView_boarderTop, 0) + custom.getInteger(R.styleable.TvHorizontalGridView_boarderTopInt, 0); this.boarderRight = (int) custom.getDimension( R.styleable.TvHorizontalGridView_boarderRight, 0) + custom.getInteger(R.styleable.TvHorizontalGridView_boarderRightInt, 0); this.boarderBottom = (int) custom.getDimension( R.styleable.TvHorizontalGridView_boarderBottom, 0) + custom.getInteger( R.styleable.TvHorizontalGridView_boarderBottomInt, 0); } else { this.boarderLeft = boarder; this.boarderTop = boarder; this.boarderRight = boarder; this.boarderBottom = boarder; } if (cursorRes == 0) { switch (getResources().getDisplayMetrics().widthPixels) { case TvUtil.SCREEN_1280: cursorRes = custom.getResourceId( R.styleable.TvHorizontalGridView_cursorRes_1280, 0); break; case TvUtil.SCREEN_1920: cursorRes = custom.getResourceId( R.styleable.TvHorizontalGridView_cursorRes_1920, 0); break; case TvUtil.SCREEN_2560: cursorRes = custom.getResourceId( R.styleable.TvHorizontalGridView_cursorRes_2560, 0); break; case TvUtil.SCREEN_3840: cursorRes = custom.getResourceId( R.styleable.TvHorizontalGridView_cursorRes_3840, 0); break; } } custom.recycle(); // 关闭子控件动画缓存 使嵌套动画更流畅 // setAnimationCacheEnabled(false); init(); } private void init() { itemIds = new SparseArray<Integer>(); mScroller = new Scroller(getContext()); wm = (WindowManager) getContext().getSystemService( Context.WINDOW_SERVICE); mDataSetObservable = new AdapterDataSetObservable(); mRecycleBin = new RecycleBin(getContext().getCacheDir() .getAbsolutePath()); } /** * 设置适配器 * * @param adapter */ public void setAdapter(TvBaseAdapter adapter) { this.adapter = adapter; if (adapter != null) { adapter.registerDataSetObservable(mDataSetObservable); } // 清理原先数据 clear(); if (isInit) { initGridView(); isInit = false; } Message msg = handler.obtainMessage(); msg.what = ACTION_INIT_ITEMS; handler.sendMessageDelayed(msg, DELAY); } private void clear() { itemIds.clear(); this.removeAllViews(); this.clearDisappearingChildren(); this.destroyDrawingCache(); mScroller.setFinalY(0); parentLayout = false; focusIsOut=true; currentChildCount = 0; } /** * 首次加载屏幕可见行数*2 */ public void initGridView() { // 重设参数 RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) getLayoutParams(); RelativeLayout.LayoutParams newParams = new RelativeLayout.LayoutParams( params.width, params.height); this.setPadding((int) (boarderLeft * scale), (int) (boarderTop * scale), (int)(boarderRight*scale), boarderBottom); this.setClipChildren(false); this.setClipToPadding(false); newParams.setMargins(params.leftMargin, params.topMargin, params.rightMargin, params.bottomMargin); this.setLayoutParams(newParams); // switch (getResources().getDisplayMetrics().widthPixels) { // case TvConfig.SCREEN_1280: // screenScale = 1f; // break; // case TvConfig.SCREEN_1920: // screenScale = 1.5f; // break; // case TvConfig.SCREEN_2560: // screenScale = 2f; // break; // case TvConfig.SCREEN_3840: // screenScale = 3f; // break; // } // // paddingLeft = (int) (screenScale*boarderLeft + itemWidth // * (scale - 1) / 2 ); // paddingTop = (int) (boarderTop * screenScale + itemHeight * (scale - // 1) / 2); } private void initItems() { // 避免冲突 if (getChildCount() > 0) { return; } int screenWidth = wm.getDefaultDisplay().getWidth(); int initCols = screenWidth % rowWidth == 0 ? screenWidth / rowWidth : screenWidth / rowWidth + 1; int initLength = Math.min(adapter.getCount(), initCols * 2 * rows); for (int i = 0; i < initLength; i++) { int left = (i / rows) * (itemWidth + spaceHori); int top = (i % rows) * (spaceVert + itemHeight); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( itemWidth, itemHeight); if (initLength == 1) { rlp.setMargins(left, top, 0, paddingTop*2); } else { rlp.setMargins(left, top, 0, 0); } View child = adapter.getView(i, null, this); this.addView(child, rlp); int viewId = child.getId(); if (viewId == -1) { viewId = TvUtil.buildId(); // 此处硬设置id同时建议开发者不用此范围id } child.setId(viewId); itemIds.put(viewId, i); bindEventOnChild(child, i); layoutFlag=true; } colCount = itemIds.size() % rows == 0 ? itemIds.size() / rows : itemIds .size() / rows + 1; cursor = new ImageView(getContext()); cursorId = TvUtil.buildId(); cursor.setId(cursorId); cursor.setBackgroundResource(cursorRes); this.addView(cursor); cursor.setVisibility(View.INVISIBLE); if (initFocus) { View focus = ((ViewGroup) getParent()).findFocus(); if (focus == null) { View item = getChildAt(0); if (item != null) { item.requestFocus(); } } } } /** * layout辅助标记 表面有添加新的子项 */ private boolean layoutFlag=false; private void addNewItems() { currentChildCount = getChildCount(); parentLayout = false; int start = itemIds.size(); int end = Math.min(start + screenMaxColumns * rows * 2, adapter.getCount()); for (int i = start; i < end; i++) { int left = (i / rows) * (itemWidth + spaceHori); int top = (i % rows) * (spaceVert + itemHeight); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( itemWidth, itemHeight); rlp.setMargins(left, top, 0, 0); View child = adapter.getView(i, null, this); this.addView(child, rlp); int viewId = child.getId(); if (viewId == -1) { viewId = TvUtil.buildId(); // 此处硬设置id同时建议开发者不用此范围id } child.setId(viewId); itemIds.put(viewId, i); bindEventOnChild(child, i); layoutFlag=true; } colCount = itemIds.size() % rows == 0 ? itemIds.size() / rows : itemIds .size() / rows + 1; } private void bindEventOnChild(View child, final int index) { child.setFocusable(true); child.setOnFocusChangeListener(new OnFocusChangeListener() { @Override public void onFocusChange(final View item, boolean focus) { if (focus) { new Handler().postDelayed(new Runnable() { @Override public void run() { moveCover(item); } }, delay); // 选中事件 if (onItemSelectListener != null) { onItemSelectListener.onItemSelect(item, index); } } else { returnCover(item); } } }); child.setOnClickListener(new OnClickListener() { @Override public void onClick(View item) { if (onItemClickListener != null) { onItemClickListener.onItemClick(item, index); } } }); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { /** * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式 */ int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int sizeWidth = MeasureSpec.getSize(widthMeasureSpec); int sizeHeight = MeasureSpec.getSize(heightMeasureSpec); // 计算出所有的childView的宽和高 measureChildren(widthMeasureSpec, heightMeasureSpec); /** * 记录如果是wrap_content是设置的宽和高 */ int width = 0; int height = 0; int cCount = getChildCount(); int cWidth = 0; int cHeight = 0; MarginLayoutParams cParams = null; /** * 根据childView计算的出的宽和高,以及设置的margin计算容器的宽和高,主要用于容器是warp_content时 */ for (int i = currentChildCount; i < cCount; i++) { View childView = getChildAt(i); cWidth = childView.getMeasuredWidth(); cHeight = childView.getMeasuredHeight(); cParams = (MarginLayoutParams) childView.getLayoutParams(); // 上面两个childView width += cWidth + cParams.leftMargin + cParams.rightMargin; height += cHeight + cParams.topMargin + cParams.bottomMargin; } /** * 如果是wrap_content设置为我们计算的值 否则:直接设置为父容器计算的值 */ setMeasuredDimension( (widthMode == MeasureSpec.EXACTLY || width == 0) ? sizeWidth : width, (heightMode == MeasureSpec.EXACTLY || height == 0) ? sizeHeight : height); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (parentLayout) { parentLayout = false; return; } if (changed||layoutFlag) { int cCount = getChildCount(); int cWidth = 0; int cHeight = 0; // boolean cursorFlag=false; /** * 遍历所有childView根据其宽和高,以及margin进行布局 */ int start = currentChildCount; // Log.e(VIEW_LOG_TAG, "onLayout=" + currentChildCount + "----" // + itemIds.size()); for (int i = start; i < cCount; i++) { // 跳过光标子项 int index = i; if (currentChildCount != 0) { index = i - 1; } if (index < itemIds.size()) { View childView = findViewById(itemIds.keyAt(index)); if (childView != null) { cWidth = childView.getMeasuredWidth(); cHeight = childView.getMeasuredHeight(); int cl = 0, ct = 0, cr = 0, cb = 0; cl = (index / rows) * (itemWidth + spaceHori); ct = (index % rows) * (spaceVert + itemHeight); cr = cl + cWidth; cb = cHeight + ct; childView.layout(cl + paddingLeft, ct + paddingTop, cr + paddingLeft, cb + paddingTop); } } } screenMaxColumns = getWidth() % rowWidth == 0 ? getWidth() / rowWidth : getWidth() / rowWidth + 1; layoutFlag=false; } } @Override public boolean dispatchKeyEventPreIme(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { if (!mScroller.isFinished()) { return true; } boolean flag = false; int direction = 0; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_DOWN: direction = View.FOCUS_DOWN; break; case KeyEvent.KEYCODE_DPAD_RIGHT: direction = View.FOCUS_RIGHT; break; case KeyEvent.KEYCODE_DPAD_UP: direction = View.FOCUS_UP; break; case KeyEvent.KEYCODE_DPAD_LEFT: direction = View.FOCUS_LEFT; break; } View focused = this.findFocus(); if (focused != null && direction != 0) { View next = focused.focusSearch(direction); // 根据下标算出所在行 if (next != null) { int focusIndex = itemIds.get(focused.getId()); Integer temp = itemIds.get(next.getId()); // 焦点切出容器时 if (temp != null) { selectIndex = temp; focusIsOut=false; } else { parentLayout = true; focusIsOut=true; return super.dispatchKeyEventPreIme(event); } int nextCol = 0; selectCol = focusIndex / rows; nextCol = selectIndex / rows; // 向下到达最后一完整行时,可滚动; 向上到达最上一行完整行时,可滚动 if (nextCol > selectCol) { if ((next.getLeft() - mScroller.getFinalX()) >= (rowWidth * (screenMaxColumns - 1)) + paddingLeft) { flag = true; } } else if (nextCol < selectCol) { if ((next.getLeft() - mScroller.getFinalX()) < paddingLeft && selectCol != 0) { flag = true; } } if (flag) { if (nextCol > -1 && mScroller.isFinished()) { selectCol = nextCol; // 先清除按钮动画 Message msg = handler.obtainMessage(); msg.obj = direction; msg.what = ACTION_START_SCROLL; handler.sendMessageDelayed(msg, scrollDelay); } else { return true; } } } } } return super.dispatchKeyEventPreIme(event); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { if (l == mScroller.getFinalX()) { if (l > oldl) { // 下翻加载 当剩余行数小于一屏时 Log.i(VIEW_LOG_TAG, "添加更多条目=" + colCount + "--" + selectCol + "--" + screenMaxColumns); if ((colCount - selectCol) < screenMaxColumns) { Message msg = handler.obtainMessage(); msg.what = ACTION_ADD_ITEMS; handler.sendMessageDelayed(msg, DELAY); } } } super.onScrollChanged(l, t, oldl, oldt); } /** * 翻页 * * @param page */ private void scrollByRow(final int direction) { if (selectCol < 0 || selectCol > colCount - 1) { return; } if (direction == View.FOCUS_LEFT) { // mScroller.startScroll(0, mScroller.getFinalY(), 0, -rowWidth, // scrollDuration); mScroller.startScroll(mScroller.getFinalX(), 0, -rowWidth, 0, scrollDuration); } else if (direction == View.FOCUS_RIGHT) { // mScroller.startScroll(0, mScroller.getFinalY(), 0, rowWidth, // scrollDuration); mScroller.startScroll(mScroller.getFinalX(), 0, rowWidth, 0, scrollDuration); } invalidate(); // 滚动时进行回收操作 避免卡顿 // new Handler().postDelayed(new Runnable() { // // @Override // public void run() { // recycle(direction); // } // }, scrollDuration); } /** * 回收 * * @param page */ private void recycle(int direction) { if (selectCol < 0 || selectCol > colCount - 1) { return; } if (direction == View.FOCUS_LEFT) { int reloadRow = selectCol - screenMaxColumns + 1; // 上翻刷新 并重新选中行前一屏的行 if (reloadRow > -1) { View reloadView = null; int reloadIndex = 0; for (int i = 0; i < rows; i++) { reloadIndex = reloadRow * rows + i; reloadView = findViewById(itemIds.keyAt(reloadIndex)); mRecycleBin.reloadView(reloadView); } } // 回收行数大于selectCol 2倍屏数的子项 int recyleRow = selectCol + screenMaxColumns * 2; if (recyleRow < colCount - 1) { View recyleView = null; int recyleIndex = 0; for (int i = 0; i < rows; i++) { recyleIndex = recyleRow * rows + i; recyleView = findViewById(itemIds.keyAt(recyleIndex)); mRecycleBin.recycleView(recyleView); } } } else if (direction == View.FOCUS_RIGHT) { // 重新加载选中行以下一屏行数 int reloadRow = selectCol + screenMaxColumns - 1; if (reloadRow < colCount - 1) { // Log.e(VIEW_LOG_TAG, "reloadRow="+reloadRow); View reloadView = null; int reloadIndex = 0; for (int i = 0; i < rows; i++) { reloadIndex = reloadRow * rows + i; reloadView = findViewById(itemIds.keyAt(reloadIndex)); mRecycleBin.reloadView(reloadView); } } // 回收行数小于selectCol 2倍屏数的子项 int recyleRow = selectCol - screenMaxColumns * 2 - 1; if (recyleRow > -1) { // Log.e(VIEW_LOG_TAG, "recyleRow="+recyleRow); View recyleView = null; int recyleIndex = 0; for (int i = 0; i < rows; i++) { recyleIndex = recyleRow * rows + i; recyleView = findViewById(itemIds.keyAt(recyleIndex)); mRecycleBin.recycleView(recyleView); } } } } @Override public void computeScroll() { super.computeScroll(); // 先判断mScroller滚动是否完成 if (mScroller.computeScrollOffset()) { // 这里调用View的scrollTo()完成实际的滚动 scrollTo(mScroller.getCurrX(), 0); // 必须调用该方法,否则不一定能看到滚动效果 postInvalidate(); } super.computeScroll(); } /** * 光标移动 到达后 与控件同时放大 */ private void moveCover(View item) { if (cursor == null) { return; } if (!item.isFocused()) { return; } setBorderParams(item); } /** * 还原控件状态 */ private void returnCover(View item) { if (cursor == null) { return; } cursor.setVisibility(View.INVISIBLE); if (scalable) { scaleToNormal(item); } } private void scaleToLarge(View item) { if (!item.isFocused()) { return; } animatorSet = new AnimatorSet(); largeX = ObjectAnimator.ofFloat(item, "ScaleX", 1f, scale); ObjectAnimator largeY = ObjectAnimator.ofFloat(item, "ScaleY", 1f, scale); ObjectAnimator cursorX = ObjectAnimator.ofFloat(cursor, "ScaleX", 1f, scale); ObjectAnimator cursorY = ObjectAnimator.ofFloat(cursor, "ScaleY", 1f, scale); animatorSet.setDuration(durationLarge); animatorSet.play(largeX).with(largeY).with(cursorX).with(cursorY); animatorSet.start(); } private void scaleToNormal(View item) { if (animatorSet == null) { return; } if (animatorSet.isRunning()) { animatorSet.cancel(); } ObjectAnimator oa = ObjectAnimator.ofFloat(item, "ScaleX", 1f); oa.setDuration(durationSmall); oa.start(); ObjectAnimator oa2 = ObjectAnimator.ofFloat(item, "ScaleY", 1f); oa2.setDuration(durationSmall); oa2.start(); } /** * 指定光标相对位置 */ private void setBorderParams(final View item) { cursor.clearAnimation(); cursor.setVisibility(View.VISIBLE); final RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) item .getLayoutParams(); final int l = params.leftMargin + paddingLeft - boarderLeft; final int t = params.topMargin + paddingTop - boarderTop; final int r = l + itemWidth + boarderRight; final int b = t + itemHeight + boarderBottom; // 判断动画类型 switch (animationType) { case ANIM_DEFAULT: cursor.layout(l, t, r, b); item.bringToFront(); cursor.bringToFront(); if (scalable) { scaleToLarge(item); } break; case ANIM_TRASLATE: ValueAnimator transAnimatorX = ObjectAnimator.ofFloat(cursor, "x", cursor.getLeft(), l); ValueAnimator transAnimatorY = ObjectAnimator.ofFloat(cursor, "y", cursor.getTop(), t); cursor.layout(l, t, r, b); item.bringToFront(); cursor.bringToFront(); if (scalable) { scaleToLarge(item); } if (focusIsOut) { return; } animatorSet = new AnimatorSet(); animatorSet.play(transAnimatorY).with(transAnimatorX); animatorSet.setDuration(durationTraslate); animatorSet.setInterpolator(new DecelerateInterpolator(1)); // animatorSet.addListener(new AnimatorListener() { // // @Override // public void onAnimationStart(Animator arg0) { // } // // @Override // public void onAnimationRepeat(Animator arg0) { // } // // @Override // public void onAnimationEnd(Animator arg0) { // cursor.layout(l, t, r, b); // item.bringToFront(); // cursor.bringToFront(); // if (scalable) { // scaleToLarge(item); // } // } // // @Override // public void onAnimationCancel(Animator arg0) { // } // }); animatorSet.start(); break; } } public void setOnItemSelectListener(OnItemSelectListener myListener) { this.onItemSelectListener = myListener; } public void setOnItemClickListener(OnItemClickListener myListener) { this.onItemClickListener = myListener; } public interface OnItemSelectListener { public void onItemSelect(View item, int position); } public interface OnItemClickListener { public void onItemClick(View item, int position); } public class AdapterDataSetObservable extends DataSetObservable { @Override public void notifyChanged() { // 数据改变 若已翻至末端 则立即调用addNewItems Log.i(VIEW_LOG_TAG, "收到数据改变通知"); if (adapter.getCount() <= itemIds.size()) { // 删减刷新 clear(); Message msg = handler.obtainMessage(); msg.what = ACTION_INIT_ITEMS; handler.sendMessageDelayed(msg, DELAY); } else { // 添加刷新 if ((colCount - selectCol) < screenMaxColumns) { Message msg = handler.obtainMessage(); msg.what = ACTION_ADD_ITEMS; handler.sendMessageDelayed(msg, DELAY); } } super.notifyChanged(); } @Override public void notifyInvalidated() { super.notifyInvalidated(); } } }