本篇讲焦点移动不了的问题,先下下图效果。
项目疑难杂症记录(二):焦点移动不了

进入“添加网络摄像机”页面后,遥控器按下往右的按键,焦点只落在第一个框上面,再也移动不了,页面拍的不是很清楚,需要仔细看下。
正常焦点移动,是系统根据某个具体的方向去查找,然后判断每个可以获取焦点的view的坐标是否是最合适获取焦点的
经过定位:发现是三个框子的view是代码动态生成的,坐落于屏幕中的位置是通过setTranslationX确定的。

下面是出问题的代码:

 private void initSize() {
        LinearLayout.LayoutParams imageParams = new LinearLayout.LayoutParams(mItemWidth, mItemHeight);
        mImageViews.get(0).setLayoutParams(imageParams);
        mImageViews.get(1).setLayoutParams(imageParams);
        mImageViews.get(2).setLayoutParams(imageParams);

        FrameLayout.LayoutParams text1Params = new FrameLayout.LayoutParams(mItemWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
       // text1Params.setMargins(mMarginLeft1, mMarginTop, 0, 0);
        mCameraViews.get(0).setLayoutParams(text1Params);

        FrameLayout.LayoutParams text2Params = new FrameLayout.LayoutParams(mItemWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
        //text2Params.setMargins(mMarginLeft2, mMarginTop, 0, 0);
        mCameraViews.get(1).setLayoutParams(text2Params);

        FrameLayout.LayoutParams text3Params = new FrameLayout.LayoutParams(mItemWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
       // text3Params.setMargins(mMarginLeft3, mMarginTop, 0, 0);
        mCameraViews.get(2).setLayoutParams(text3Params);
        initData();
        initListen();
        initTransmator();
    }

    private void initTransmator() {
        mCameraViewLayout.setTranslationY(mMarginTop);
        //确定第一个框的位置
        mCameraViews.get(0).setTranslationX(mMarginLeft1);
        //确定第二个框的位置
        mCameraViews.get(1).setTranslationX(mMarginLeft2);
        //确定第三个框的位置
        mCameraViews.get(2).setTranslationX(mMarginLeft3);
    }

思路:
setTranslationX是不是影响了坐标?或者view的坐标不对了?

下面进入源码分析:

FocusFinder 这个类专门负责查找焦点,里面包含着不同方向的查找条件,下面看下它的这个方法

    View findNextFocusInAbsoluteDirection(ArrayList<View> focusables, ViewGroup root, View focused,
            Rect focusedRect, int direction) {
        // initialize the best candidate to something impossible
        // (so the first plausible view will become the best choice)
        mBestCandidateRect.set(focusedRect);
        switch(direction) {
            case View.FOCUS_LEFT:
                mBestCandidateRect.offset(focusedRect.width() + 1, 0);
                break;
            case View.FOCUS_RIGHT:
                mBestCandidateRect.offset(-(focusedRect.width() + 1), 0);
                break;
            case View.FOCUS_UP:
                mBestCandidateRect.offset(0, focusedRect.height() + 1);
                break;
            case View.FOCUS_DOWN:
                mBestCandidateRect.offset(0, -(focusedRect.height() + 1));
        }

        View closest = null;
        int numFocusables = focusables.size();
        //遍历当前页面上所有的可获取焦点的view
        for (int i = 0; i < numFocusables; i++) {
            View focusable = focusables.get(i);

            // only interested in other non-root views
            //如果是已经获取焦点的view,跳过
            if (focusable == focused || focusable == root) continue;

            // get focus bounds of other view in same coordinate system
            //得到候选者view的Rect
            focusable.getFocusedRect(mOtherRect);
            //重点看下这个方法,该方法是将view的Rect转换成坐标系
            root.offsetDescendantRectToMyCoords(focusable, mOtherRect);
            //找出最合适可以获取焦点的方法
            if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) {
                mBestCandidateRect.set(mOtherRect);
                closest = focusable;
            }
        }
        //返回焦点view
        return closest;
    }

经过定位,是offsetDescendantRectToMyCoords“出了问题”,该方法是将view的Rect转换成响应的坐标系,包含x,y坐标。

看下实现:

    public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
        offsetRectBetweenParentAndChild(descendant, rect, true, false);
    }
    
 void offsetRectBetweenParentAndChild(View descendant, Rect rect,
            boolean offsetFromChildToParent, boolean clipToBounds) {

        // already in the same coord system :)
        if (descendant == this) {
            return;
        }

        ViewParent theParent = descendant.mParent;
        //从当前view遍历,一直到根view的直接下一个view
        // search and offset up to the parent
        while ((theParent != null)
                && (theParent instanceof View)
                && (theParent != this)) {

            if (offsetFromChildToParent) {
               //依次增加左边距-scrollx
                rect.offset(descendant.mLeft - descendant.mScrollX,
                        descendant.mTop - descendant.mScrollY);
                if (clipToBounds) {
                    View p = (View) theParent;
                    boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
                            p.mBottom - p.mTop);
                    if (!intersected) {
                        rect.setEmpty();
                    }
                }
            } else {
                if (clipToBounds) {
                    View p = (View) theParent;
                    boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
                            p.mBottom - p.mTop);
                    if (!intersected) {
                        rect.setEmpty();
                    }
                }
                rect.offset(descendant.mScrollX - descendant.mLeft,
                        descendant.mScrollY - descendant.mTop);
            }

            descendant = (View) theParent;
            theParent = descendant.mParent;
        }
        //到达根view
        if (theParent == this) {
            if (offsetFromChildToParent) {
              //算上根view的left,top值。
                rect.offset(descendant.mLeft - descendant.mScrollX,
                        descendant.mTop - descendant.mScrollY);
            } else {
                rect.offset(descendant.mScrollX - descendant.mLeft,
                        descendant.mScrollY - descendant.mTop);
            }
        } else {
            throw new IllegalArgumentException("parameter must be a descendant of this view");
        }
    }


这段代码,是如何计算出view具体的坐标的呢?

下面我附上我画的一张图,没有考虑滑动(scroll)情况,看了这张图你就能知道了,画的不好,请忽略啊。

项目疑难杂症记录(二):焦点移动不了

在该bug中,由于view调用了setTranstionX这个属性动画的方式设置了坐落在屏幕中的位置,但是在计算坐标的时候,依次在view层级树上计算的时候,这个view相关的mLeft,与mTop是缺失的,以mLeft举例,mLeft指的是view相对于父View的左边距,我们正常在xml中设置marginLeft或者代码中动态设置setMarginLeft,最后setLayoutParams都可以生效,mLeft是有值的。
setTranstionx本质上是通过动画的方式,没有调用onLayout,mLeft没有值。

相关文章:

  • 2022-12-23
  • 2021-06-04
  • 2021-08-06
  • 2021-05-12
  • 2021-04-19
  • 2021-10-19
  • 2021-10-22
  • 2021-06-21
猜你喜欢
  • 2021-12-28
  • 2021-11-29
  • 2021-05-26
  • 2021-12-14
  • 2021-05-26
相关资源
相似解决方案