-
FocusFinder类的实例是依附于线程的,每个线程一个实例。
private static final ThreadLocal<FocusFinder> tlFocusFinder = new ThreadLocal<FocusFinder>() { @Override protected FocusFinder initialValue() { return new FocusFinder(); } };
public final View findNextFocus(ViewGroup root, View focused, int direction)
public View findNextFocusFromRect(ViewGroup root, Rect focusedRect, int direction)
-
先从
findNextFocus()
入手private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) { //流程一: View next = null; //1.如果当前的焦点view不为空,则寻找用户指定的下一个焦点 if (focused != null) { next = findNextUserSpecifiedFocus(root, focused, direction); } //如果找到,直接返回用户指定的焦点 if (next != null) { return next; } //流程二: ArrayList<View> focusables = mTempList; try { focusables.clear(); //2.把当前root下的所有的可以获得焦点的view加入列表 root.addFocusables(focusables, direction); if (!focusables.isEmpty()) { //3.继续寻找当前root下的焦点 next = findNextFocus(root, focused, focusedRect, direction, focusables); } } finally { focusables.clear(); } return next; }
流程一:
findNextUserSpecifiedFocus()
//分析流程一: //----------------------FocusFinder------------------------- //1.FocusFinder findUserSetNextFocus()找到用户指定的下一个焦点 private View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) { // check for user specified next View userSetNextFocus = focused.findUserSetNextFocus(root, direction); if (userSetNextFocus != null && userSetNextFocus.isFocusable() && (!userSetNextFocus.isInTouchMode() || userSetNextFocus.isFocusableInTouchMode())) { return userSetNextFocus; } return null; } //----------------------focused--view------------------------- //2.进入focused View里面,findViewInsideOutShouldExist() View findUserSetNextFocus(View root, @FocusDirection int direction) { switch (direction) { case FOCUS_LEFT: if (mNextFocusLeftId == View.NO_ID) return null; return findViewInsideOutShouldExist(root, mNextFocusLeftId); case FOCUS_RIGHT: ... ... } return null; } //3. private View findViewInsideOutShouldExist(View root, int id) { if (mMatchIdPredicate == null) { //Predicate<View> mMatchIdPredicate = new MatchIdPredicate(); } //id == 要寻找的下一个焦点的view的id mMatchIdPredicate.mId = id; //Note:this == focusedView View result = root.findViewByPredicateInsideOut(this, mMatchIdPredicate); if (result == null) { Log.w(VIEW_LOG_TAG, "couldn't find view with id " + id); } return result; } //----------------------root--view------------------------ //4.start == focusedView是获得焦点的view public final View findViewByPredicateInsideOut(View start, Predicate<View> predicate) { View childToSkip = null; for (;;) { //比对一下start是不是要寻找的上面的id的view,如果是则返回start View view = start.findViewByPredicateTraversal(predicate, childToSkip); //如果找到了view,或者start已经是root view了,则返回 if (view != null || start == this) { return view; } //如果没有找到,则一层层找到start的父view继续比较 ViewParent parent = start.getParent(); if (parent == null || !(parent instanceof View)) { return null; } childToSkip = start; start = (View) parent; } } //5.就是比对start是不是上面要寻找的id的view protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { if (predicate.apply(this)) { return this; } return null; } //总结: 1.如果mNextFocusLeftId == View.NO_ID 即用户没有指定下一个焦点直接返回null 2.如果用户指定了mNextFocusLeftId,则从比对focused view的id是不是等于mNextFocusLeftId。 如果不是,则向上找到focused view的parent继续比对id是否等于mNextFocusLeftId,直到找到root view 如果还没有找到,则返回root view
流程二:
addFocusables()
findNextFocus()
//----------------------root--view------------------------ //ViewGroup继承自View的方法 public void addFocusables(ArrayList<View> views, @FocusDirection int direction) { addFocusables(views, direction, isInTouchMode() ? FOCUSABLES_TOUCH_MODE : FOCUSABLES_ALL); } //ViewGroup复写自View的方法 @Override public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { final int focusableCount = views.size(); //root view是否可以获得焦点 final int descendantFocusability = getDescendantFocusability(); if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { if (shouldBlockFocusForTouchscreen()) { focusableMode |= FOCUSABLES_TOUCH_MODE; } final int count = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < count; i++) { final View child = children[i]; //如果child view 可见,继续循环调用chid的addFocusables()方法 //如果chid view是ViewGroup,则继续调用ViewGroup类中的addFocusables()方法 //如果chid view是View,则调用View类中的addFocusables()方法 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { child.addFocusables(views, direction, focusableMode); } } } //满足以下条件,则把当前的ViewGroup当做View尝试加入可获得焦点的列表 //1.触摸模式下可以获得焦点 || 触摸模式下不会拦截焦点 //2.focusableCount == views.size() || descendantFocusability != FOCUS_AFTER_DESCENDANTS // we add ourselves (if focusable) in all cases except for when we are // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is // to avoid the focus search finding layouts when a more precise search // among the focusable children would be more interesting. //FOCUS_AFTER_DESCENDANTS:This view will get focus only if none of its descendants want it. if ((descendantFocusability != FOCUS_AFTER_DESCENDANTS // No focusable descendants || (focusableCount == views.size())) && (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())) { //调用View类中的addFocusables()把ViewGroup计入可获得焦点的列表 super.addFocusables(views, direction, focusableMode); } } //View类中的方法 public void addFocusables(ArrayList<View> views, @FocusDirection int direction, @FocusableMode int focusableMode) { if (views == null) { return; } //view不可以获取焦点则返回 if (!isFocusable()) { return; } //如果是FOCUSABLES_TOUCH_MODE模式,并且在这种模式下不可以获得焦点,则返回 if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE && !isFocusableInTouchMode()) { return; } //满足条件,加入集合 views.add(this); } //总结: //把root view中的所有可以获得焦点的view,加入列表 //-----------------FocusFinder---------------------------- //先考虑focused != null的情况 private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction, ArrayList<View> focusables) { //1.焦点不为空的情况 if (focused != null) { if (focusedRect == null) { focusedRect = mFocusedRect; } // fill in interesting rect from focused focused.getFocusedRect(focusedRect); root.offsetDescendantRectToMyCoords(focused, focusedRect); } //2.焦点为空的情况 else { if (focusedRect == null) { focusedRect = mFocusedRect; // make up a rect at top left or bottom right of root switch (direction) { case View.FOCUS_RIGHT: case View.FOCUS_DOWN: setFocusTopLeft(root, focusedRect); break; ... case View.FOCUS_LEFT: case View.FOCUS_UP: setFocusBottomRight(root, focusedRect); break; ... } } } switch (direction) { case View.FOCUS_FORWARD: ... case View.FOCUS_UP: case View.FOCUS_DOWN: case View.FOCUS_LEFT: case View.FOCUS_RIGHT: //Note: return findNextFocusInAbsoluteDirection(focusables, root, focused, focusedRect, direction); ... } } 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: // //Note:mBestCandidateRect在focusedReact的右边 //并且距离focusedReact的右边一个像素 mBestCandidateRect.offset(focusedRect.width() + 1, 0); break; case View.FOCUS_RIGHT: ... } View closest = null; //遍历可获得焦点的列表 int numFocusables = focusables.size(); for (int i = 0; i < numFocusables; i++) { View focusable = focusables.get(i); // only interested in other non-root views //忽略当前的focused view 和root view if (focusable == focused || focusable == root) continue; // get focus bounds of other view in same coordinate system focusable.getFocusedRect(mOtherRect); root.offsetDescendantRectToMyCoords(focusable, mOtherRect); //找到最佳的候选的view,则返回 if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) { mBestCandidateRect.set(mOtherRect); closest = focusable; } } return closest; }
Android TV FocusFinder寻焦流程分析(一)
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
推荐阅读更多精彩内容
- 焦点: 焦点(Focus)可以理解为选中态,在Android TV上起很重要的作用。一个视图控件只有在获得焦点的状...
- 本文重点针对android TV开发的同学,分析遥控或键盘按键事件后焦点的分发机制。尤其是刚从手机开发转向TV开发...