Android Scrollview+Listview 实现不同条目点赞并计数功能及原理分析(2)

来源:互联网 发布:淘宝网买家地域解读 编辑:程序博客网 时间:2024/06/01 09:43

ok,到了第二篇博客,我们继续解决问题。

  • 子view中的button等劫持了listview中item的clickListener。
  • OnClickListener接口的onClick方法中的View view参数究竟是什么

我们常在listview的item中设置自己喜欢的样式,这其中可能就会出现button和checkBox这样的控件,你可能会遇见item中的button拦截了listview的onItemClickListener,导致其点击事件失效。
这个问题的原因是因为item中的控件霸占了焦点
也许你会把ImageButton改成ImageView然后给它设一个OnClickListener方法,你会发现ImageView并不会强占焦点。很神奇对不对,我们先看这个问题的解决方法,然后回来再说。
网上的解决方法非常多,基本一致:

  1. 将ListView中的Item布局中的子控件focusable属性设置为false
  2. 在getView方法中设置button.setFocusable(false)
  3. 设置item的根布局的属性android:descendantFocusability=”blocksDescendant”

常用的是第三个,从第二个方法中,我们可以感觉出button的focusable属性应该是true,而普通的控件(如TextView)是false的。
看一下源码吧,Button继承TextView,TextView继承View。看一下View对这几个函数的处理。

public void setFocusable(boolean focusable) {        if (!focusable) {            setFlags(0, FOCUSABLE_IN_TOUCH_MODE);        }        setFlags(focusable ? FOCUSABLE : NOT_FOCUSABLE, FOCUSABLE_MASK);    }
public void setOnClickListener(OnClickListener l) {        if (!isClickable()) {            setClickable(true);        }        getListenerInfo().mOnClickListener = l;    }
public void setClickable(boolean clickable) {        setFlags(clickable ? CLICKABLE : 0, CLICKABLE);    }
void setFlags(int flags, int mask) {        final boolean accessibilityEnabled =                AccessibilityManager.getInstance(mContext).isEnabled();        final boolean oldIncludeForAccessibility = accessibilityEnabled && includeForAccessibility();        int old = mViewFlags;        mViewFlags = (mViewFlags & ~mask) | (flags & mask);        int changed = mViewFlags ^ old;        if (changed == 0) {            return;        }        int privateFlags = mPrivateFlags;        /* Check if the FOCUSABLE bit has changed */        if (((changed & FOCUSABLE_MASK) != 0) &&                ((privateFlags & PFLAG_HAS_BOUNDS) !=0)) {            if (((old & FOCUSABLE_MASK) == FOCUSABLE)                    && ((privateFlags & PFLAG_FOCUSED) != 0)) {                /* Give up focus if we are no longer focusable */                clearFocus();            } else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE)                    && ((privateFlags & PFLAG_FOCUSED) == 0)) {                /*                 * Tell the view system that we are now available to take focus                 * if no one else already has it.                 */                if (mParent != null) mParent.focusableViewAvailable(this);            }        }        final int newVisibility = flags & VISIBILITY_MASK;        if (newVisibility == VISIBLE) {            if ((changed & VISIBILITY_MASK) != 0) {                /*                 * If this view is becoming visible, invalidate it in case it changed while                 * it was not visible. Marking it drawn ensures that the invalidation will                 * go through.                 */                mPrivateFlags |= PFLAG_DRAWN;                invalidate(true);                needGlobalAttributesUpdate(true);                // a view becoming visible is worth notifying the parent                // about in case nothing has focus.  even if this specific view                // isn't focusable, it may contain something that is, so let                // the root view try to give this focus if nothing else does.                if ((mParent != null) && (mBottom > mTop) && (mRight > mLeft)) {                    mParent.focusableViewAvailable(this);                }            }        }        /* Check if the GONE bit has changed */        if ((changed & GONE) != 0) {            needGlobalAttributesUpdate(false);            requestLayout();            if (((mViewFlags & VISIBILITY_MASK) == GONE)) {                if (hasFocus()) clearFocus();                clearAccessibilityFocus();                destroyDrawingCache();                if (mParent instanceof View) {                    // GONE views noop invalidation, so invalidate the parent                    ((View) mParent).invalidate(true);                }                // Mark the view drawn to ensure that it gets invalidated properly the next                // time it is visible and gets invalidated                mPrivateFlags |= PFLAG_DRAWN;            }            if (mAttachInfo != null) {                mAttachInfo.mViewVisibilityChanged = true;            }        }        /* Check if the VISIBLE bit has changed */        if ((changed & INVISIBLE) != 0) {            needGlobalAttributesUpdate(false);            /*             * If this view is becoming invisible, set the DRAWN flag so that             * the next invalidate() will not be skipped.             */            mPrivateFlags |= PFLAG_DRAWN;            if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE)) {                // root view becoming invisible shouldn't clear focus and accessibility focus                if (getRootView() != this) {                    if (hasFocus()) clearFocus();                    clearAccessibilityFocus();                }            }            if (mAttachInfo != null) {                mAttachInfo.mViewVisibilityChanged = true;            }        }        if ((changed & VISIBILITY_MASK) != 0) {            // If the view is invisible, cleanup its display list to free up resources            if (newVisibility != VISIBLE) {                cleanupDraw();            }            if (mParent instanceof ViewGroup) {                ((ViewGroup) mParent).onChildVisibilityChanged(this,                        (changed & VISIBILITY_MASK), newVisibility);                ((View) mParent).invalidate(true);            } else if (mParent != null) {                mParent.invalidateChild(this, null);            }            dispatchVisibilityChanged(this, newVisibility);        }        if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {            destroyDrawingCache();        }        if ((changed & DRAWING_CACHE_ENABLED) != 0) {            destroyDrawingCache();            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;            invalidateParentCaches();        }        if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0) {            destroyDrawingCache();            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;        }        if ((changed & DRAW_MASK) != 0) {            if ((mViewFlags & WILL_NOT_DRAW) != 0) {                if (mBackground != null) {                    mPrivateFlags &= ~PFLAG_SKIP_DRAW;                    mPrivateFlags |= PFLAG_ONLY_DRAWS_BACKGROUND;                } else {                    mPrivateFlags |= PFLAG_SKIP_DRAW;                }            } else {                mPrivateFlags &= ~PFLAG_SKIP_DRAW;            }            requestLayout();            invalidate(true);        }        if ((changed & KEEP_SCREEN_ON) != 0) {            if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {                mParent.recomputeViewAttributes(this);            }        }        if (accessibilityEnabled) {            if ((changed & FOCUSABLE_MASK) != 0 || (changed & VISIBILITY_MASK) != 0                    || (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0) {                if (oldIncludeForAccessibility != includeForAccessibility()) {                    notifySubtreeAccessibilityStateChangedIfNeeded();                } else {                    notifyViewAccessibilityStateChangedIfNeeded(                            AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);                }            } else if ((changed & ENABLED_MASK) != 0) {                notifyViewAccessibilityStateChangedIfNeeded(                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);            }        }    }

可以看到每个控件都可以设置clickable和focusable,这个问题还是很难解释清楚,openStack有这样的解释

Focusable means ACTION_UP event will occur. It will gain it and it won’t release it.
Clickable means ACTION_UP first and ACTION_DOWN at the end. It will gain and release it at the end.

你可以这样理解,focus占用action,所以click不会接收到action,所以被劫持了。

接下来说另外一个问题。
view这个参数可能我们用的不多,其实view就是我们点击的那个图案,比如一个button或者说一个listview中的item。别忘了控件都是继承view的。而且view有一个tag的属性,这个属性可以通过view来传递一些信息,这个属性尤其有用,特别是在listview中。因为我们总是在adapter中设置点击事件,但是可能具体的实现是在外层中,比如activity。这时点击事件就需要传一些相关信息到外层。比如说这个点击事件所属的某个item的position信息。
我们在adapter中传入位置信息

viewHolder.up.setTag(position);

在外层获得

int position = (Integer)view.getTag()

ok,上代码吧,空听我说可能也不明白什么意思。希望能帮你解决问题,下次将继续解决最后的问题。
代码代码

1 0
原创粉丝点击