精华内容
下载资源
问答
  • 本文实例为大家分享了Android实现手机自动获取短信验证码功能,供大家参考,具体内容如下 1、短信监听广播 2、读取短信内容 3、截取短信内容【可以 reg截取】 4、填写至相应控件 PS:DevStore测试没有获取到验证码 ...
  • Android之UI控件

    2016-04-01 16:10:08
    2. 在activity获取Spinner控件 3. 定义Spinner下拉列表项数组并将下拉项的内容添加到这个数组中,通过这个数组建立一个下拉列表的适配器 4. 将上3中的适配器配置给获取的Spinner控件 5. 设置

    本文主要包括以下内容

    1. Spinner的使用
    2. Gallery的使用

    Spinner的使用

    Spinner的实现过程是
    1. 在xml文件中定义Spinner的控件
    2. 在activity中获取Spinner控件
    3. 定义Spinner下拉列表项数组并将下拉项的内容添加到这个数组中,通过这个数组建立一个下拉列表的适配器
    4. 将上3中的适配器配置给获取的Spinner控件
    5. 设置下拉列表的显示样式
    6. 为获得的Spinner控件添加事件监听

    在XML文件中定义

    //在主XML中<include android:id="@+id/sp_chose" layout="@layout/spinner_down"/>
    
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
             android:layout_height="30dip"
             android:orientation="horizontal"
             android:background="@drawable/filter_bg"
             android:layout_marginTop="5dip"
             android:layout_marginLeft="5dip"
             android:layout_marginRight="5dip">
    
            <Spinner
                 android:id="@+id/nearby_distance_spinner"
                 style="@style/nearby_spinner_style" />
            <Spinner
                 android:id="@+id/nearby_class_spinner"
                 style="@style/nearby_spinner_style" />
            <Spinner
                 android:id="@+id/nearby_away_spinner"
                 style="@style/nearby_spinner_style" />
    
    
    </LinearLayout>

    其中背景图片为

    这里写图片描述

    nearby_spinner_style为

    <style name="nearby_spinner_style">
            <item name="android:layout_width">0.0dip</item>
            <item name="android:layout_height">wrap_content</item>
            <item name="android:background">@null</item>
            <item name="android:layout_marginTop">6dip</item>
            <item name="android:layout_weight">1.0</item>
        </style>

    找到Spinner并初始化适配器

    private void init() {
            // TODO Auto-generated method stub
            topText=(TextView) findViewById(R.id.tv_chose_shop);
            topText.setText(getIntent().getStringExtra("type"));
            disSpi=(Spinner) findViewById(R.id.nearby_distance_spinner);
            claSpi=(Spinner) findViewById(R.id.nearby_class_spinner);
            awaySpi=(Spinner) findViewById(R.id.nearby_away_spinner);
    
             disAdapter=new ArrayAdapter<String>(this, R.layout.nearby_spinner_text, DIS_DATE);
             claAdapter=new ArrayAdapter<String>(this, R.layout.nearby_spinner_text, CLASS_DATE);
             awayAdapter=new ArrayAdapter<String>(this, R.layout.nearby_spinner_text, AWAY_DATE);
    
    
    
        }

    其中nearby_spinner_text为

    <?xml version="1.0" encoding="utf-8"?>
    
     <TextView xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@android:id/text1"
         style="?android:attr/spinnerDropDownItemStyle"
         android:singleLine="true"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:gravity="center_vertical"
         android:textColor="#ffffff"
         android:textSize="12sp"/>

    设置下拉列表的显示样式并且将适配器配置给spinner

    //设置列表项显示风格为完全显示
        disAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            claAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            awayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    
    
            disSpi.setAdapter(disAdapter);
            claSpi.setAdapter(claAdapter);
            awaySpi.setAdapter(awayAdapter);
    
            disSpi.setSelection(2);
            claSpi.setSelection(0);
            awaySpi.setSelection(0);

    设置监听事件

    disSpi.setOnItemSelectedListener(new OnItemSelectedListener() {
    
                @Override
                public void onItemSelected(AdapterView<?> parent, View view,
                        int position, long id) {
                    // TODO Auto-generated method stub
                    Toast.makeText(getApplicationContext(), DIS_DATE[position], 0).show();
                }
    
                @Override
                public void onNothingSelected(AdapterView<?> parent) {
                    // TODO Auto-generated method stub
    
                }
    
            });

    完成,效果如下

    这里写图片描述
    这里写图片描述

    展开全文
  • 就是指通过视图的根View(例如Activity的DecorView)如何能找到控件树中当前获取焦点的控件。我们将核心内容分为如下三部分: 控件树对焦点的管理; 按键事件分发; 下一个焦点控件的查找; 控件焦点体系建立 我们...

    Android版本:7.0(API27)

    [TOC]


      按键事件分发需要根据控件树对焦点的管理进行事件分发,那控件树是如何管理焦点的呢?就是指通过视图的根View(例如Activity的DecorView)如何能找到控件树中当前获取焦点的控件。我们将核心内容分为如下三部分:

    • 控件树对焦点的管理;
    • 按键事件分发;
    • 下一个焦点控件的查找;

    控件焦点体系建立

    我们通过View.requestFoucs()的实现来揭示控件树对焦点的管理方式。

    public final boolean requestFocus() {
        return requestFocus(View.FOCUS_DOWN);
    }
    
    public final boolean requestFocus(int direction) {
        return requestFocus(direction, null);
    }
    
    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
        return requestFocusNoSearch(direction, previouslyFocusedRect);
    }
    复制代码
    • direction:表示焦点的寻找方向。当控件是ViewGroup时将会从左上角开始沿着这个方向查找可以获取焦点的子控件。本例只分析View,所以该参数并无任何效果;
    • previouslyFocusedRect:之前拥有焦点的控件的位置;

    获取焦点条件判断

    private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
        // need to be focusable
        // 1.控件可见并且可以获得焦点
        if ((mViewFlags & FOCUSABLE) != FOCUSABLE
                || (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
            return false;
        }
    
        // need to be focusable in touch mode if in touch mode
        // 2.在触摸模式下可以获取焦点
        if (isInTouchMode() &&
            (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
               return false;
        }
    
        // need to not have any parents blocking us
        if (hasAncestorThatBlocksDescendantFocus()) {
            return false;
        }
    
        /* 4.调用handleFocusGainInternal使此控件获取焦点 */
        handleFocusGainInternal(direction, previouslyFocusedRect);
        return true;
    }
    复制代码

    标记3:
      如果该View的任意一父类控件(父亲、爷爷、祖爷爷等等)的mGroupFlags设置了FOCUS_BLOCK_DESCENDANTS时,则阻止该控件获取焦点。hasAncestorThatBlocksDescendantFocus会沿着控件树一路回溯到整个控件树的根控件并逐一检查mGroupFlags的设置。
      mGroupFlags的设置可以有三种取值,其中一种为FOCUS_BLOCK_DESCENDANTS。当父控件的这一特性取值为FOCUS_BLOCK_DESCENDANT时,父控件将会阻止其子控件、孙子控件等获取焦点。在介绍ViewGroup.requestFocus()时会详细介绍。
    标记4:
      调用handleFocusGainInternal使此控件获取焦点

    获取焦点

    void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) {
        if (DBG) {
            System.out.println(this + " requestFocus()");
        }
    
        if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
            // 1
            mPrivateFlags |= PFLAG_FOCUSED;
    
            View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;
    
            if (mParent != null) {
                // 2
                mParent.requestChildFocus(this, this);
                updateFocusedInCluster(oldFocus, direction);
            }
    
            if (mAttachInfo != null) {
                // 3
                mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
            }
    
            // 4
            onFocusChanged(true, direction, previouslyFocusedRect);
            // 5
            refreshDrawableState();
        }
    }
    复制代码

    handleFocusGainInternal让该View获取焦点:
    标记1:
      设置mPrivateFlags标志为PFLAG_FOCUSED,表示该控件获取焦点。
    标记2:
      将这一变化通知其父控件,以将焦点从上一个焦点控件夺走并转移到本控件身上,并在ViewRootImpl中触发一次"遍历"以便对控件树进行重绘。
    标记3:
      触发通过如下代码添加的监听

    view.getViewTreeObserver().addOnGlobalFocusChangeListener(this); 
    复制代码

    标记4:
      触发mOnFocusChangeListener的监听
    标记5:
      刷新drawable获取焦点时的状态

      上述所有的工作都是在改变旧的焦点体系的状态,设置新的焦点体系;PFLAG_FOCUSED是一个控件拥有焦点的最直接体现,然而这并不是焦点管理的全部。这一标记仅仅体现了焦点在个体上的特性;而mParent.requestChildFocus则体现了焦点在控件树级别上的特性。

    控件树中的焦点体系

    mParent.requestChildFocus是一个定义在ViewParent接口中的方法,其实现者为ViewGroup和ViewRootImpl。
    ViewGroup的实现目的有两个:

    • 将焦点从上一个焦点控件手中夺走,即将PFLAG_FOCUSED标记从控件的mPrivateFlags中移除;
    • 将这一操作继续向控件树的根部进行回溯,直到ViewRootImpl,ViewRootImpl的requestChildFocus会将焦点控件保存起来备用,并引发一次“遍历”使得整个控件树进行重绘;

    ViewGroup.requestChildFocus

    @Override
    public void requestChildFocus(View child, View focused) {
        if (DBG) {
            System.out.println(this + " requestChildFocus()");
        }
        if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
            return;
        }
    
        // Unfocus us, if necessary
        // 1.
        super.unFocus(focused);
    
        // We had a previous notion of who had focus. Clear it.
        if (mFocused != child) {
            // 2.
            if (mFocused != null) {
                mFocused.unFocus(focused);
            }
    
            mFocused = child;
        }
        // 3.
        if (mParent != null) {
            mParent.requestChildFocus(this, focused);
        }
    }
    复制代码

    标记1:
      如果之前的焦点控件就是这个ViewGroup,则释放其焦点;
    标记2:

    • 拥有焦点的旧控件mFocused与期望获取焦点的新控件child不为同一个控件,且mFocused不为null,则释放旧控件mFocused的焦点;
    • 将child赋值给mFocused,注:mFocused指向的是新拥有焦点的控件或者新拥有焦点的控件的父亲、爷爷、祖爷爷等;

    标记3:
    将这一操作继续向控件树的根部回溯;

      此方法中的mFocused是一个View类型的变量,它是控件树焦点管理的核心所在,围绕着mFocused,ViewGroup.requestChildFocus方法包含了新的焦点体系的建立过程,以及旧的焦点体系的销毁过程。

    新焦点体系建立

      新的焦点体系的建立过程是通过在ViewGroup.requestChildFocus方法的回溯过程中进行mFocused = child这一赋值操作完成的。当回溯完成后,mFocused = child将会建立起一个单向链表,使得从根控件开始通过mFocused成员可以沿着这一单向链表找到实际拥有焦点的控件,即实际拥有焦点的控件位于这个单向链表的尾端,如下图所示:

    旧焦点体系销毁

      旧的焦点体系的销毁过程则是通过在回溯过程中调用mFocused.unFocus完成的。unFocus有ViewGroup和View两种实现。首先看一下ViewGroup.unfocus的实现:

    void unFocus(View focused) {
        if (DBG) {
            System.out.println(this + " unFocus()");
        }
        if (mFocused == null) {
            super.unFocus(focused);
        } else {
            mFocused.unFocus(focused);
            mFocused = null;
        }
    }
    复制代码

      可见ViewGroup.unfocus将unfocus调用沿着mFocused所描述的链表沿着控件树向下遍历,知道焦点的实际拥有者。焦点的实际拥有者会调用View.unFocus(),它会将PFLAG_FOCUSED移除,当然也少不了更新DrawableState以及onFocusChanged()方法的调用。 View.unfocus()

    void unFocus(View focused) {
        if (DBG) {
            System.out.println(this + " unFocus()");
        }
    
        clearFocusInternal(focused, false, false);
    }
    
    void clearFocusInternal(View focused, boolean propagate, boolean refocus) {
        if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
            mPrivateFlags &= ~PFLAG_FOCUSED;
    
            if (propagate && mParent != null) {
                mParent.clearChildFocus(this);
            }
    
            onFocusChanged(false, 0, null);
            refreshDrawableState();
    
            if (propagate && (!refocus || !rootViewRequestFocus())) {
                notifyGlobalFocusCleared(this);
            }
        }
    }
    复制代码

    重绘控件树

    控件树最终会调用根控件的requestChildFocus,这里的根控件其实就是ViewRootImpl

     public void requestChildFocus(View child, View focused) {
        if (DEBUG_INPUT_RESIZE) {
            Log.v(mTag, "Request child focus: focus now " + focused);
        }
        checkThread();
        scheduleTraversals();
    }
    复制代码

    前面的文章我们分析过View的绘制流程,ViewRootImpl.scheduleTraversals会触发整个控件树重新绘制,这样整个焦点变化的过程就完成了。

    总结


      以上图所示的控件树的焦点状态为例来描述旧有焦点体系的销毁以及新焦点体系的建立过程。当View2-1-1通过View.requestFocus()尝试获取焦点时,首先会将PLFAGL_FOCUSED标记加入其mPrivateFlags成员中以声明其拥有焦点。然后调用ViewGroup2-1的requestChildFocus(),此时ViewGroup2-1会尝试通过unFocus()销毁旧的焦点体系,但是由于其mFocused为null,它无法进行销毁,于是它将其mFocused设置为View2-1-1后将requestChildFocus()传递给ViewGroup2。此时ViewGroup2的mFocused指向ViewGroup2-2,于是调用ViewGroup2-2的unFocus()销毁旧的焦点体系。ViewGroup2-2的unFocus()将次操作传递给View2-2-2的unFocus()以移除View2-2-2的PLFAGL_FOCUSED标记,并将其mFocused置为null。回到ViewGroup2的requestChildFocus()方法后,ViewGroup2将其mFocused重新指向到ViewGroup2-1。在这些工作完成后,就形成了新的焦点体系,如下图所示

      总而言之,控件树的焦点管理分为两部分:

    1. 描述个体级别的焦点状态的PLFAGL_FOCUSED标记,用于表示一个控件是否拥有焦点;
    2. 描述控件树级别的焦点状态的ViewGroup.mFocused成员,用于提供一条链接控件树的根控件到实际拥有焦点的子控件的单向链表。这条链表提供了再控件树中快速查找焦点控件的简便办法。另外,由于焦点的排他性,当一个控件通过requestFocus()获取焦点以创建新的焦点体系时伴随着旧有焦点体系的销毁过程;

    ViewGroup.requestFocus

    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
        int descendantFocusability = getDescendantFocusability();
    
        switch (descendantFocusability) {
            case FOCUS_BLOCK_DESCENDANTS:
                return super.requestFocus(direction, previouslyFocusedRect);
            case FOCUS_BEFORE_DESCENDANTS: {
                final boolean took = super.requestFocus(direction, previouslyFocusedRect);
                return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
            }
            case FOCUS_AFTER_DESCENDANTS: {
                final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
                return took ? took : super.requestFocus(direction, previouslyFocusedRect);
            }
            default:
                throw new IllegalStateException("descendant focusability must be "
                        + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
                        + "but is " + descendantFocusability);
        }
    }
    复制代码

    根据descendantFocusability不同的取值,执行的处理方式不同:

    • FOCUS_BLOCK_DESCENDANTS:阻止所有子控件获取焦点,调用super.requestFocus尝试自己获取焦点;
    • FOCUS_BEFORE_DESCENDANTS:ViewGroup优先于子控件获取焦点,如果ViewGroup不满足获取焦点的条件,则通过调用onRequestFocusInDescendants方法将获取焦点的请求转发给子控件;
    • FOCUS_AFTER_DESCENDANTS:与FOCUS_BEFORE_DESCENDANTS相反,子控件优先ViewGroup获取焦点,如果子控件不满足获取焦点的条件,则ViewGroup尝试自己获取焦点;
    protected boolean onRequestFocusInDescendants(int direction,
                Rect previouslyFocusedRect) {
        int index;
        int increment;
        int end;
        int count = mChildrenCount;
        if ((direction & FOCUS_FORWARD) != 0) {
            index = 0;
            increment = 1;
            end = count;
        } else {
            index = count - 1;
            increment = -1;
            end = -1;
        }
        final View[] children = mChildren;
        for (int i = index; i != end; i += increment) {
            View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                if (child.requestFocus(direction, previouslyFocusedRect)) {
                    return true;
                }
            }
        }
        return false;
    }
    复制代码

    onRequestFocusInDescendants其实是一种最简单的焦点查找算法。它按照direction方向,在mChildren列表中依次调用子控件的requestFocus()方法,直到有一个子控件获取焦点。另外,需要注意子控件也可能是一个ViewGroup,所以这里将会有一个递归的过程。

    下一个焦点控件查找

      当一个控件获取焦点后,用户往往会通过方向键来移动焦点,这时控件系统需要在控件树中指定的方向上寻找距离当前控件最近的一个控件,并将焦点赋予它。与ViewGroup.onRequestFocusInDescendants()方法按照控件在mChildren数组中的顺序查找不同,这一查找依赖于控件在窗口中的位置。这一工作由View.focusSearch()方法完成。

    public View focusSearch(@FocusRealDirection int direction) {
        if (mParent != null) {
            return mParent.focusSearch(this, direction);
        } else {
            return null;
        }
    }
    复制代码

    又会调用父类的focusSearch,父类就是ViewGroup

    public View focusSearch(View focused, int direction) {
        if (isRootNamespace()) {
            // root namespace means we should consider ourselves the top of the
            // tree for focus searching; otherwise we could be focus searching
            // into other tabs.  see LocalActivityManager and TabHost for more info.
            return FocusFinder.getInstance().findNextFocus(this, focused, direction);
        } else if (mParent != null) {
            return mParent.focusSearch(focused, direction);
        }
        return null;
    }
    复制代码

    如果此ViewGroup不是根控件,则继续向控件树的根部回溯,直到根控件,然后使用FocusFinder的findNextFocus方法查找下一个焦点。这个方法三个参数的意义如下:

    • this:即root。findNextFocus方法通过这个参数获取整个控件树中所有的候选控件。
    • focused:当前拥有焦点的控件。findNextFocus方法会以这个控件所在的位置开始查找。
    • direction:查找的方向。
    public final View findNextFocus(ViewGroup root, View focused, int direction) {
        return findNextFocus(root, focused, null, direction);
    }
    
    private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
        View next = null;
        ViewGroup effectiveRoot = getEffectiveRoot(root, focused);
        if (focused != null) {
            next = findNextUserSpecifiedFocus(effectiveRoot, focused, direction);
        }
        if (next != null) {
            return next;
        }
        ArrayList<View> focusables = mTempList;
        try {
            focusables.clear();
            effectiveRoot.addFocusables(focusables, direction);
            if (!focusables.isEmpty()) {
                next = findNextFocus(effectiveRoot, focused, focusedRect, direction, focusables);
            }
        } finally {
            focusables.clear();
        }
        return next;
    }
    复制代码

      findNextFocus会首先尝试通过findNextUserSpecifiedFocus获取由开发者设置的下一个焦点控件。有时候控件系统内置的焦点查找算法并不能满足开发者的需求,因此需要开发者可以通过View.setNextFocusXXXId()方法或XML中的nextFocusXXX属性设置此控件的下一个可获取焦点的控件Id。其中XXX可以是Left、Right、Top、Bottom和Forword,分别用来设置不同方向上的下一个焦点控件。
      倘若开发者在指定方向上没有设置下一个焦点控件,则通过内置的搜索算法进行查找。这个内置算法会首先将控件树中所有可获取焦点的控件添加到一个名为focusables的列表中,并以这个列表作为焦点控件的候选集合。这样做的目的并不仅仅是提高效率,更重要的是这个列表打破了控件在控件树中的层次关系。它在一定程度上体现了焦点查找的一个原则,即控件在窗口上的位置是唯一查找依据,与控件在控件树中的层次无关。

      至于具体的查找算法,我们就不深入去研究了,有兴趣的同学可以自行阅读。

    按键事件分发

      在“Android事件分发”分析了事件分发的分水岭:

    final class ViewPostImeInputStage extends InputStage {
        public ViewPostImeInputStage(InputStage next) {
            super(next);
        }
    
        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    return processTrackballEvent(q);
                } else {
                    return processGenericMotionEvent(q);
                }
            }
        }
    }
    复制代码

    processKeyEvent处理按键事件

     private int processKeyEvent(QueuedInputEvent q) {
        final KeyEvent event = (KeyEvent)q.mEvent;
    
        // Deliver the key to the view hierarchy.
        if (mView.dispatchKeyEvent(event)) {
            return FINISH_HANDLED;
        }
    
        if (shouldDropInputEvent(q)) {
            return FINISH_NOT_HANDLED;
        }
    
        int groupNavigationDirection = 0;
    
        if (event.getAction() == KeyEvent.ACTION_DOWN
                && event.getKeyCode() == KeyEvent.KEYCODE_TAB) {
            if (KeyEvent.metaStateHasModifiers(event.getMetaState(), KeyEvent.META_META_ON)) {
                groupNavigationDirection = View.FOCUS_FORWARD;
            } else if (KeyEvent.metaStateHasModifiers(event.getMetaState(),
                    KeyEvent.META_META_ON | KeyEvent.META_SHIFT_ON)) {
                groupNavigationDirection = View.FOCUS_BACKWARD;
            }
        }
    
        // If a modifier is held, try to interpret the key as a shortcut.
        if (event.getAction() == KeyEvent.ACTION_DOWN
                && !KeyEvent.metaStateHasNoModifiers(event.getMetaState())
                && event.getRepeatCount() == 0
                && !KeyEvent.isModifierKey(event.getKeyCode())
                && groupNavigationDirection == 0) {
            if (mView.dispatchKeyShortcutEvent(event)) {
                return FINISH_HANDLED;
            }
            if (shouldDropInputEvent(q)) {
                return FINISH_NOT_HANDLED;
            }
        }
    
        // Apply the fallback event policy.
        if (mFallbackEventHandler.dispatchKeyEvent(event)) {
            return FINISH_HANDLED;
        }
        if (shouldDropInputEvent(q)) {
            return FINISH_NOT_HANDLED;
        }
    
        // Handle automatic focus changes.
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            if (groupNavigationDirection != 0) {
                if (performKeyboardGroupNavigation(groupNavigationDirection)) {
                    return FINISH_HANDLED;
                }
            } else {
                if (performFocusNavigation(event)) {
                    return FINISH_HANDLED;
                }
            }
        }
        return FORWARD;
    }
    复制代码

    processKeyEvent的代码较长,不过我们的摸底是抽离核心步骤进行分析即可,processKeyEvent有两个核心内容:

    • 调用mView.dispatchKeyEvent(event),在控件树中进行按键事件分发;
    • 如果在上一步中没有View消耗事情,那么再调用performFocusNavigation判断该事件是否是方向键,控制焦点的移动;

    事件分发

    这里是调用ViewRootImpl内部字段mView.dispatchPointerEvent就行触摸事件分发,mView是什么呢?通过前面文章的分析我们能知道mView其实是PhoneWindow内部维护的DecorView,而DecorView继承FrameLayout,所以最终调用的是ViewGroup.dispatchKeyEvent

    public boolean dispatchKeyEvent(KeyEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 1);
        }
    
        if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
                == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
            // 1.
            if (super.dispatchKeyEvent(event)) {
                return true;
            }
        } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
                == PFLAG_HAS_BOUNDS) {
            // 2.
            if (mFocused.dispatchKeyEvent(event)) {
                return true;
            }
        }
    
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
        }
        return false;
    }
    复制代码

    标记1
    如果此ViewGroup拥有焦点,则调用super.dispatchKeyEvent尝试消费事件;
    标记2
    如果此ViewGroup不拥有焦点,则将事件沿折mFocused链表进行传递;如果mFocused为ViewGroup,那么进入递归过程;

    View.dispatchKeyEvent()

    public boolean dispatchKeyEvent(KeyEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 0);
        }
    
        // Give any attached key listener a first crack at the event.
        //noinspection SimplifiableIfStatement
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
            return true;
        }
    
        if (event.dispatch(this, mAttachInfo != null
                ? mAttachInfo.mKeyDispatchState : null, this)) {
            return true;
        }
    
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }
        return false;
    }
    复制代码

    首先由mOnKeyListener监听者尝试处理事件,如果mOnKeyListener返回true,那么整个案件事件结束。然后通过event.dispatch方法将事件发给View的指定回调,如onKeyDown()/onKeyUp()等。

    焦点移动

    performFocusNavigation方法:

    private boolean performFocusNavigation(KeyEvent event) {
        int direction = 0;
        switch (event.getKeyCode()) {
            case KeyEvent.KEYCODE_DPAD_LEFT:
                if (event.hasNoModifiers()) {
                    direction = View.FOCUS_LEFT;
                }
                break;
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                if (event.hasNoModifiers()) {
                    direction = View.FOCUS_RIGHT;
                }
                break;
            case KeyEvent.KEYCODE_DPAD_UP:
                if (event.hasNoModifiers()) {
                    direction = View.FOCUS_UP;
                }
                break;
            case KeyEvent.KEYCODE_DPAD_DOWN:
                if (event.hasNoModifiers()) {
                    direction = View.FOCUS_DOWN;
                }
                break;
            case KeyEvent.KEYCODE_TAB:
                if (event.hasNoModifiers()) {
                    direction = View.FOCUS_FORWARD;
                } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
                    direction = View.FOCUS_BACKWARD;
                }
                break;
        }
        if (direction != 0) {
            View focused = mView.findFocus();
            if (focused != null) {
                View v = focused.focusSearch(direction);
                if (v != null && v != focused) {
                    // do the math the get the interesting rect
                    // of previous focused into the coord system of
                    // newly focused view
                    focused.getFocusedRect(mTempRect);
                    if (mView instanceof ViewGroup) {
                        ((ViewGroup) mView).offsetDescendantRectToMyCoords(
                                focused, mTempRect);
                        ((ViewGroup) mView).offsetRectIntoDescendantCoords(
                                v, mTempRect);
                    }
                    if (v.requestFocus(direction, mTempRect)) {
                        playSoundEffect(SoundEffectConstants
                                .getContantForFocusDirection(direction));
                        return true;
                    }
                }
    
                // Give the focused view a last chance to handle the dpad key.
                if (mView.dispatchUnhandledMove(focused, direction)) {
                    return true;
                }
            } else {
                if (mView.restoreDefaultFocus()) {
                    return true;
                }
            }
        }
        return false;
    }
    复制代码
    1. 转换获取焦点移动的方向direction;
    2. mView.findFocus找到控件树中当前获得焦点的View;
    3. 以当前获得焦点控件focused为启动,调用focusSearch(direction)在direction方向上查找下一个获取焦点的控件;
    4. 调用requestFocus方法,实现旧焦点体系的移除和新焦点体系的建立(这点参考前面的分析);
    展开全文
  • 宿主activity和碎片之间的传值

    千次阅读 2017-02-06 16:22:49
    直接通过findViewById获取到碎片中的控件,然后给控件设置内容。 (2)如果碎片是通过代码生成的 activity中添加碎片的时候,通过碎片对象的.setArgments(bundle) 在fragment里面,通过getArgments()得到一个...
     1、activity--->碎片
    (1)如果碎片是通过xml文件生成的
        直接通过findViewById获取到碎片中的控件,然后给控件设置内容。
    (2)如果碎片是通过代码生成的
         activity中添加碎片的时候,通过碎片对象的.setArgments(bundle)
         在fragment里面,通过getArgments()得到一个bundle对象,再从bundle对象里面获取内容
    
     2、碎片--->activity
         在activity中声明一个公共的方法,在这个方法中必须要有一个参数(参数类型就是要传递的数据类型)
         在fragment里面,通过getActivity(),可以获取到宿主activity对象,再调用宿主对象中提供的公共方法,
                 把数据传递在这个方法中。
    
         获取assets目录中的文件流的方法:getActivity().getAssets().open("day10.txt");
    
     3、碎片--->碎片        
            先把一个碎片里的值传到它的宿主activity,再从宿主activity传到另一个碎片。
     注意:
            如果碎片中有按钮,那么按钮的点击事件只能用监听器的方式绑定,不能用属性onClick
    
    展开全文
  • 在开发中有时候会遇见...很多时候我们都是想的在fragment添加到Activity中时通过 fragmet. getView()获取fragment对象的View 。然后用findViewById()获取到fragment中的控件对象进行操作,很悲剧的是不管用啊有木有。


    在开发中有时候会遇见一些的情况:根据不同的需求切换不同的fragment ,然后一些操作使当前fragment中显示的内容进行一些调整。很多时候我们都是想的在fragment添加到Activity中时通过 fragmet. getView()获取fragment对象的View 。然后用findViewById()获取到fragment中的控件对象进行操作,很悲剧的是不管用啊有木有。我是个小白,研究了近一周都没搞定,只有采用另一种方式对fragment中的控件进行操作了。

    一. 发送广播更新UI

        UI中需要时发送一条广播 可顺便传入一些需要的值,在fragment中接收广播,获取值并对fragment中的控件进行操作,经本人验证完全可以。以下是Demo关键代码

    在Fragment中

    public class GasFragment extends Fragment {
    	private TextView gasName;
    	private TextView gasadderss;
    	
    	private ReceiveBroadCast receiveBroadCast;
    	public	GasFragment (){}
    	
    	@Override
    	public void onAttach(Activity activity) {
    		
    		/** 注册广播 */
    		receiveBroadCast = new ReceiveBroadCast();
            IntentFilter filter = new IntentFilter();
            filter.addAction("com.gasFragment");    //只有持有相同的action的接受者才能接收此广播
            activity.registerReceiver(receiveBroadCast, filter);
    		super.onAttach(activity);
    	}
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState) {
    	    	View view=inflater.inflate(R.layout.fragment_gas, null);
    
    		gasName = (TextView) view.findViewById(R.id.frag_tv_gasname);
    		gasadderss = (TextView) view.findViewById(R.id.fraggas_tv_adderss);
    		
    		
            	}  
          });
    		return view;
    	}
    	 class ReceiveBroadCast extends BroadcastReceiver
    		{
    		        @Override
    		        public void onReceive(Context context, Intent intent)
    		        {
    		            //得到广播中得到的数据,并显示出来
    	            	String gasname = intent.getExtras().getString("gasName");
    	            	String address = intent.getExtras().getString("address");
    	            	
    					gasadderss.setText("地址:\n  "+address);
    	            	gasName.setText(gasname);
    		        }
    		}
    	 /**
    	  *注销广播
    	  * */
    	 @Override
    	 public void onDestroyView() {
    	 	getActivity().unregisterReceiver(receiveBroadCast);
    	 	super.onDestroyView();
    	 }
    	
    	 }   

    在UI中

    public class MainPage extends FragmentActivity {
    
    	private GasFragment gasFragment;       //加油站的fragment
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		
    		setContentView(R.layout.main_page);
            gasFragment = new GasFragment();
            FragmentManager fm = getSupportFragmentManager();
    		FragmentTransaction ft = fm.beginTransaction();
    		ft.replace(R.id.main_fl_fragment, couponFragment);// 将帧布局替换成Fragment
    		ft.commit();// 提交
    		
    		Intent intent = new Intent(); // Itent就是我们要发送的内容
    		intent.setAction("com.gasFragment"); // 设置你这个广播的action
    		intent.putExtra("gasName","核反应能量加油站");
    		intent.putExtra("address", "太平洋街11号");
    		sendBroadcast(intent); // 发送广播
    	}
    }

    对应xml就不用上传了吧,很简单的布局。这样就可以动态的更新fragment中的显示内容了。


    二. 通过Handler刷新Fragment

           在需要交互的Activity中建立Handler对象 具体上代码

    public class MainActivity extends FragmentActivity implements OnClickListener{
    
    	FrameLayout fl_replace;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		fragmentA = new FragmentA();
    		fragmentA.setHandler(handler);
    		
    		fl_replace=(FrameLayout) findViewById(R.id.fl_replace);
    		
    		findViewById(R.id.btn_replace).setOnClickListener(this);
    		findViewById(R.id.btn_refresh).setOnClickListener(this);
    		findViewById(R.id.btn_return).setOnClickListener(this);
    	}
    	@Override
    	public void onClick(View v) {
    		switch (v.getId()) {
    		case R.id.btn_replace:
    			fl_replace.setVisibility(View.VISIBLE);
            FragmentManager fm=getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            ft.replace(R.id.fl_replace, fragmentA);
            ft.commit();
    			break;
    		case R.id.btn_refresh:
    			handler.sendEmptyMessage(FragmentA.ACT_REF);
    			break;
    		case R.id.btn_return:
    			 fl_replace.setVisibility(View.GONE);
    			break;
    		}
    	}
    	Handler handler =new Handler(){
    		public void handleMessage(android.os.Message msg) {
    			switch (msg.what) {
    			case FragmentA.ACT_REF:// 在Activity中刷新Fragment
    				fragmentA.setTextStr("on Activity refresh ");
    				break;
    			case FragmentA.FRAG_REF://在Fragment中刷新Fragment
    				fragmentA.setTextStr("on Fragment refresh ");
    				break;
    			}
    		};
    	};
    	private FragmentA fragmentA;
    }
    在Fragment 中获取Handler 并通过此对象发消息  代码如下
    public class FragmentA extends Fragment{
    	
    public static final int FRAG_REF=0;//on Fragment refresh Fragment	
    public static final int ACT_REF=1;//on Activity refresh Fragment	
    private TextView tv;
    public FragmentA(){}
    @Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState) {
    	        View view=inflater.inflate(R.layout.frag_a, null);
    	        tv = (TextView) view.findViewById(R.id.fragment_tv);
    	        view.findViewById(R.id.fragment_btn).setOnClickListener(new OnClickListener() {
    				@Override
    				public void onClick(View v) {
    				handler.sendEmptyMessage(FRAG_REF);
    				}
    			});
    	        return view;
    	}
      Handler handler;
        public void setHandler(Handler handler){
        	this.handler =handler;
        } 
        public void setTextStr(String str){
        	tv.setText(str);
        }
    }
    handler 更新是随后添加的  所以我写了一个Demo 需要的可以去下载,一看就懂了 很简单的  http://download.csdn.net/detail/yangbo437993234/8423493







    我是小白,如果有什么不足的地方请大家提出来我改正,谢谢

    展开全文
  • 小弟刚刚学习android,新手一枚,这两天再搞android通过webservice取的数据展示到listview上 我经过调试,确定数据已经取到,sql也非常简单,select * from table,但是list控件并没有展示到模拟器上,搞了很久也没...
  • //利用edit()方法获取Editor对象。 edit = sp.edit(); /* 账户 和密码的控件变量赋值 登录按钮的控件变量 赋值 */ // 4 完成用户名密码的保存功能 //m_pswSave = (CheckBox) findViewById(R.id....
  • 4_Android的view

    2018-08-15 21:20:27
    1.view的基本概念 Activity所有显示的控件都是用...例子:在Activity获取代表控件的对象,调用控件的方法,修改控件显示的内容通过代码设置控件的属性。 新建一个Android 应用程序,修改布局文件 在Main...
  • 通常包壳应用,大家都会用到一些第三方封装好的WebView框架,页面中不可避免会出现输入框,在输入框获取焦点时,系统或者通过代码来通知弹出输入框,为了避免布局被遮挡,会设置当前Activity的软键盘模式,比如: ...
  • databinding的双向绑定通过binding可以获取绑定界面的任何view,通过model对象使得控件属性脱离view存在databinding使得逻辑层可以全面的脱离activity,可以通过xml设置方法所需要的参数 dataibnding 绑定text时...
  • 摘要:Java源码,Android源码,任务管理器 Android 源码之... 通过设置TAG,读写其中各个控件内容  添加点击事件处理机制,以支持弹出菜单  获取进程列表  从其他Activity切回当前Activity时,进程列表要刷新……
  • fragment的静态加载

    千次阅读 2015-03-04 21:37:10
    fragment是android中一个非常重要的内容,中文译名叫“碎片”或者“片段”,本人喜欢叫他“碎片”,感觉...【注意】fragment不是控件,而是Activity的一部分(即“碎片”),所以不能通过findViewById()的方法获取……
  • 文章目录一、实现内容二、用到的部分知识点及参考1.实现界面跳转并传递数据——使用Intent2.控件的隐藏与显示3.通过Handler把需要在另一个线程执行的操作加入到消息队列中去4.使用okHttp解析XML文件5.单选项——使用...
  • |--内容提供者之短信的获取与写入 |--内容提供者之联系人读写与批量操作 |--内容提供者之获取通话记录 |--内容提供者的定义 |--写入联系人信息 |--利用FinalHttp实现多线程断点续传 |--加密之MD5 |--动画Animation...
  •  6.4 获取Activity的返回值  6.5 广播和广播接收者(Broadcast Receiver)  6.6 本章小结  第7章 多媒体  7.1 多媒体开发  7.1.1 多媒体核心OpenCore  7.1.2 媒体播放器(MediaPlayer)  7.1.3 媒体录制...
  • 我是这么理解的,我们需要在C 层当中创建 M 层的对象然后调用其相关方法:譬如访问网络方法、存储数据方法,而这些方法所用到的参数就通过V层来获取,同时如果M层有数据返回,那么V层可以直接操作这个返回的数据。...
  • (1)针对多窗口类浏览器模式问题,指出并分析了该问题存在的原因,利用Activity的运行机制,通过Fragment栈对主要模块的Webview进行管理,实现对不同模块之间切换的控制。 (2)针对跨域数据交互问题,指出并分析了...

空空如也

空空如也

1 2 3 4 5
收藏数 96
精华内容 38
关键字:

通过activity获取内容控件