当前位置: 首页 > 面试题库 >

Android操作栏选项卡和键盘焦点

邵博远
2023-03-14
问题内容

我有一个非常简单的活动,有两个标签,并且我试图在自定义视图中处理键盘输入。这很好用…直到我交换标签。交换标签后,我再也无法捕获事件了。但是,在另一个html" target="_blank">应用程序中,打开一个对话框然后关闭它,将允许我的按键事件通过。如果不这样做,我将无法再次获取关键事件。

这里有什么问题?交换选项卡后,我找不到任何方法来获取关键事件,并且很好奇正在吃的东西。这个例子很简短,很关键。

main.xml

<?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:orientation="vertical"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
    >
    <FrameLayout
      android:id="@+id/actionbar_content" 
      android:layout_width="match_parent"
      android:layout_height="match_parent"
    />
</LinearLayout>

my_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
    <view 
      class="com.broken.keyboard.KeyboardTestActivity$MyView"
      android:background="#777777"
      android:focusable="true"
      android:focusableInTouchMode="true"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
    >
        <requestFocus/>
    </view>
</LinearLayout>

KeyboardTestActivity.java

package com.broken.keyboard;

import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;

import android.app.FragmentTransaction;
import android.app.ActionBar.Tab;
import android.content.Context;

public class KeyboardTestActivity extends Activity {

    public static class MyView extends View {
        public void toggleKeyboard()
        { ((InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput(0, 0); }

        public MyView(Context context)
        { super(context); }

        public MyView(Context context, AttributeSet attrs)
        { super(context, attrs); }

        public MyView(Context context, AttributeSet attrs, int defStyle)
        { super(context, attrs, defStyle); }


        // FIRST PLACE I TRY, WHERE I WANT TO GET THE PRESSES
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            Log.i("BDBG", "Key went down in view!");
            return super.onKeyDown(keyCode,event);
        }

        // Toggle keyboard on touch!
        @Override
        public boolean onTouchEvent(MotionEvent event)
        {
            if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN)
            {
                toggleKeyboard();
            }
            return super.onTouchEvent(event);
        }
    }

    // Extremely simple fragment
    public class MyFragment extends Fragment {        
        @Override
        public View onCreateView (LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View v = inflater.inflate(R.layout.my_fragment, container, false);
            return v;
        }
    }

    // Simple tab listener
    public static class MyTabListener implements ActionBar.TabListener
    {
        private FragmentManager mFragmentManager=null;
        private Fragment mFragment=null;
        private String mTag=null;
        public MyTabListener(FragmentManager fragmentManager, Fragment fragment,String tag)
        {
            mFragmentManager=fragmentManager;
            mFragment=fragment;
            mTag=tag;
        }
        @Override
        public void onTabReselected(Tab tab, FragmentTransaction ft) {
            // do nothing
        }

        @Override
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
            mFragmentManager.beginTransaction()
                .replace(R.id.actionbar_content, mFragment, mTag)
                .commit();
        }

        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
            mFragmentManager.beginTransaction()
                .remove(mFragment)
                .commit();
        }

    }

    FragmentManager mFragmentManager;
    ActionBar mActionBar;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // Retrieve the fragment manager
        mFragmentManager=getFragmentManager();
        mActionBar=getActionBar();

        // remove the activity title to make space for tabs
        mActionBar.setDisplayShowTitleEnabled(false);

        mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        // Add the tabs
        mActionBar.addTab(mActionBar.newTab()
                .setText("Tab 1")
                .setTabListener(new MyTabListener(getFragmentManager(), new MyFragment(),"Frag1")));

        mActionBar.addTab(mActionBar.newTab()
                .setText("Tab 2")
                .setTabListener(new MyTabListener(getFragmentManager(), new MyFragment(),"Frag2")));


    }

    // OTHER PLACE I TRY, DOESN'T WORK BETTER THAN IN THE VIEW
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.i("BDBG", "Key went down in activity!");
        return super.onKeyDown(keyCode,event);
    }
}

问题答案:

我已经解决了自己的问题,所以我认为我应该分享解决方案。如果有措词问题,请在评论中纠正我;我正在尝试尽可能地准确,但我并不完全是一名Android专家。一般而言,此答案还应作为一个很好的示例,说明如何处理换出ActionBar选项卡。无论是否喜欢解决方案代码的设计,它都应该很有用。

以下链接帮助我弄清楚了我的问题:http
:
//code.google.com/p/android/issues/detail?id=2705

事实证明,手头有两个重要问题。首先,如果一个View既是android:focusable又是android:focusableInTouchMode,那么在蜂窝平板电脑上,人们可能希望点击它并进行类似操作即可使其聚焦。但是,这不一定是正确的。如果该视图恰好
也是 android:clickable的,那么实际上点击会聚焦该视图。如果无法单击,则不会通过触摸聚焦。

此外,换出一个片段时,存在与首次实例化活动视图时非常相似的问题。仅在完全准备好View层次结构后,才需要进行某些更改。

如果在完全准备好View层次结构之前在片段内的某个视图上调用“
requestFocus()”,则View确实会认为它已聚焦。但是,如果软键盘打开,它实际上将不会向该视图发送任何事件!更糟的是,如果该View是可单击的,则此时单击它不能解决此键盘焦点问题,因为View认为它确实已聚焦并且无事可做。但是,如果要聚焦
其他 视图,然后再点击该视图,则因为它既可单击又可聚焦,所以确实可以聚焦,并且也可以将键盘输入定向到该视图。

有了这些信息,将焦点放在交换到选项卡上的正确方法是在交换片段后将可运行对象发布到该片段的View层次结构中,然后才调用requestFocus()。
完全准备好View层次结构 之后
调用requestFocus()既可以集中View的外观,也可以根据需要直接将键盘输入到ViewFocus()上。它不会进入视图已聚焦的那种奇怪的聚焦状态,但是键盘输入却没有以某种方式指向它,如果在完全准备好View层次结构之前调用requestFocus()会发生这种情况。

同样重要的是,在片段的布局的XML中使用“
requestFocus”标签将最早调用requestFocus()。没有理由在片段的布局中使用该标签。在片段之外,也许..但不在片段内。

在代码中,我在片段的顶部添加了一个EditText,仅用于测试点击焦点更改行为,而点击自定义视图也将切换软键盘。交换选项卡时,焦点也应默认为自定义视图。我试图有效地注释代码。

KeyboardTestActivity.java

package com.broken.keyboard;

import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;

import android.app.FragmentTransaction;
import android.app.ActionBar.Tab;
import android.content.Context;

public class KeyboardTestActivity extends Activity {

    /**
     * This class wraps the addition of tabs to the ActionBar,
     * while properly swapping between them.  Furthermore, it
     * also provides a listener interface by which you can
     * react additionally to the tab changes.  Lastly, it also
     * provides a callback for after a tab has been changed and
     * a runnable has been post to the View hierarchy, ensuring
     * the fragment transactions have completed.  This allows
     * proper timing of a call to requestFocus(), and other
     * similar methods.
     * 
     * @author nacitar sevaht
     *
     */
    public static class ActionBarTabManager 
    {
        public static interface TabChangeListener
        {
            /**
             * Invoked when a new tab is selected.
             * 
             * @param tag The tag of this tab's fragment.
             */
            public abstract void onTabSelected(String tag);

            /**
             * Invoked when a new tab is selected, but after
             * a Runnable has been executed after being post
             * to the view hierarchy, ensuring the fragment
             * transaction is complete.
             * 
             * @param tag The tag of this tab's fragment.
             */
            public abstract void onTabSelectedPost(String tag);

            /**
             * Invoked when the currently selected tab is reselected.
             * 
             * @param tag The tag of this tab's fragment.
             */
            public abstract void onTabReselected(String tag);

            /**
             * Invoked when a new tab is selected, prior to {@link onTabSelected}
             * notifying that the previously selected tab (if any) that it is no
             * longer selected.
             * 
             * @param tag The tag of this tab's fragment.
             */
            public abstract void onTabUnselected(String tag);


        }

        // Variables
        Activity mActivity = null;
        ActionBar mActionBar = null;
        FragmentManager mFragmentManager = null;
        TabChangeListener mListener=null;
        View mContainer = null;
        Runnable mTabSelectedPostRunnable = null;

        /**
         * The constructor of this class.
         * 
         * @param activity The activity on which we will be placing the actionbar tabs.
         * @param containerId The layout id of the container, preferable a  {@link FrameLayout}
         *        that will contain the fragments.
         * @param listener A listener with which one can react to tab change events.
         */
        public ActionBarTabManager(Activity activity, int containerId, TabChangeListener listener)
        {
            mActivity = activity;
            if (mActivity == null)
                throw new RuntimeException("ActionBarTabManager requires a valid activity!");

            mActionBar = mActivity.getActionBar();
            if (mActionBar == null)
                throw new RuntimeException("ActionBarTabManager requires an activity with an ActionBar.");

            mContainer = activity.findViewById(containerId);

            if (mContainer == null)
                throw new RuntimeException("ActionBarTabManager requires a valid container (FrameLayout, preferably).");

            mListener = listener;
            mFragmentManager = mActivity.getFragmentManager();

            // Force tab navigation mode
            mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        }

        /**
         * Simple Runnable to invoke the {@link onTabSelectedPost} method of the listener.
         * 
         * @author nacitar sevaht
         *
         */
        private class TabSelectedPostRunnable implements Runnable
        {
            String mTag = null;
            public TabSelectedPostRunnable(String tag)
            {
                mTag=tag;
            }
            @Override
            public void run() {
                if (mListener != null) {
                    mListener.onTabSelectedPost(mTag);
                }
            }

        }

        /**
         * Internal TabListener.  This class serves as a good example
         * of how to properly handles swapping the tabs out.  It also
         * invokes the user's listener after swapping.
         * 
         * @author nacitar sevaht
         *
         */
        private class TabListener implements ActionBar.TabListener
        {
            private Fragment mFragment=null;
            private String mTag=null;
            public TabListener(Fragment fragment, String tag)
            {
                mFragment=fragment;
                mTag=tag;
            }
            private boolean post(Runnable runnable)
            {
                return mContainer.post(runnable);
            }
            @Override
            public void onTabReselected(Tab tab, FragmentTransaction ft) {
                // no fragment swapping logic necessary

                if (mListener != null) {
                    mListener.onTabReselected(mTag);
                }

            }
            @Override
            public void onTabSelected(Tab tab, FragmentTransaction ft) {
                mFragmentManager.beginTransaction()
                    .replace(mContainer.getId(), mFragment, mTag)
                    .commit();
                if (mListener != null) {
                    mListener.onTabSelected(mTag);
                }
                // Post a runnable for this tab
                post(new TabSelectedPostRunnable(mTag));
            }

            @Override
            public void onTabUnselected(Tab tab, FragmentTransaction ft) {
                mFragmentManager.beginTransaction()
                    .remove(mFragment)
                    .commit();
                if (mListener != null) {
                    mListener.onTabUnselected(mTag);
                }
            }

        }

        /**
         * Simple wrapper for adding a text-only tab.  More robust
         * approaches could be added.
         * 
         * @param title The text to display on the tab.
         * @param fragment The fragment to swap in when this tab is selected.
         * @param tag The unique tag for this tab.
         */
        public void addTab(String title, Fragment fragment, String tag)
        {
            // The tab listener is crucial here.
            mActionBar.addTab(mActionBar.newTab()
                    .setText(title)
                    .setTabListener(new TabListener(fragment, tag)));   
        }

    }
    /**
     * A simple custom view that toggles the on screen keyboard when touched,
     * and also prints a log message whenever a key event is received.
     * 
     * @author nacitar sevaht
     *
     */
    public static class MyView extends View {
        public void toggleKeyboard()
        { ((InputMethodManager)getContext().getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput(0, 0); }

        public MyView(Context context)
        { super(context); }

        public MyView(Context context, AttributeSet attrs)
        { super(context, attrs); }

        public MyView(Context context, AttributeSet attrs, int defStyle)
        { super(context, attrs, defStyle); }


        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            Log.i("BDBG", "Key (" + keyCode + ") went down in the custom view!");
            return true;
        }

        // Toggle keyboard on touch!
        @Override
        public boolean onTouchEvent(MotionEvent event)
        {
            if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN)
            {
                toggleKeyboard();
            }
            return super.onTouchEvent(event);
        }
    }

    // Extremely simple fragment
    public class MyFragment extends Fragment {
        @Override
        public View onCreateView (LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View v = inflater.inflate(R.layout.my_fragment, container, false);
            return v;
        }
    }

    public class MyTabChangeListener implements ActionBarTabManager.TabChangeListener
    {
        public void onTabReselected(String tag) { }
        public void onTabSelected(String tag) { }
        public void onTabSelectedPost(String tag)
        {
            // TODO: NOTE: typically, one would conditionally set the focus based upon the tag.
            //             but in our sample, both tabs have the same fragment layout.
            View view=findViewById(R.id.myview);
            if (view == null)
            {
                throw new RuntimeException("Tab with tag of (\""+tag+"\") should have the view we're looking for, but doesn't!");
            }
            view.requestFocus();
        }
        public void onTabUnselected(String tag) { }
    }

    // Our tab manager
    ActionBarTabManager mActionBarTabManager = null;

    // Our listener
    MyTabChangeListener mListener = new MyTabChangeListener();

    // Called when the activity is first created.
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // instantiate our tab manager
        mActionBarTabManager = new ActionBarTabManager(this,R.id.actionbar_content,mListener);

        // remove the activity title to make space for tabs
        getActionBar().setDisplayShowTitleEnabled(false);

        // Add the tabs
        mActionBarTabManager.addTab("Tab 1", new MyFragment(), "Frag1");
        mActionBarTabManager.addTab("Tab 2", new MyFragment(), "Frag2");
    }
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <FrameLayout
        android:id="@+id/actionbar_content" 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    />
</LinearLayout>

my_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
    <EditText
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />

    <!-- note that view is in lower case here -->
    <view 
        class="com.broken.keyboard.KeyboardTestActivity$MyView"
        android:id="@+id/myview"
        android:background="#777777"
        android:clickable="true"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
    />
</LinearLayout>


 类似资料:
  • 主要内容:ion-tabs,实例,ion-tab,$ionicTabsDelegateion-tabs ion-tabs 是有一组页面选项卡组成的选项卡栏。可以通过点击选项来切换页面。 对于 iOS,它会出现在屏幕的底部,Android会出现在屏幕的顶部(导航栏下面)。 用法 实例 <ion-tabs class="tabs-positive tabs-icon-only"> <ion-tab title="首页" icon-on="ion-ios-filing" icon-off

  • ion-tabs ion-tabs 是有一组页面选项卡组成的选项卡栏。可以通过点击选项来切换页面。 对于 iOS,它会出现在屏幕的底部,Android会出现在屏幕的顶部(导航栏下面)。 用法<ion-tabs class="tabs-positive tabs-icon-only"> <ion-tab title="首页" icon-on="ion-ios-filing" icon-off="io

  • 我在开发一个Android应用程序。我有一个处于选项卡导航模式的actionbar和三个调用frameview容器中要替换的片段的选项卡。 我的问题是:如果用户在一个选项卡中生成一个backstack,然后切换到另一个选项卡,如果在这种状态下按下back按钮--那么backstack会弹出,但选项卡没有改变,这导致重叠的片段(属于Tab1的backstack片段会弹出/添加,但Tab2仍然被选中,

  • 如何从操作栏禁用“选项”菜单项? 我做了我自己的自定义栏,用于显示*. png标志,但我不想显示选项菜单的三个点按钮。 我试图找到一些解决办法,但没有任何效果。

  • 就我一直在寻找的情况而言,我没有为我的代码提出一个好的最终解决方案。我试图实现类似这样的布局: 动作栏 null

  • 实际上,我正在尝试构建一个ListView/RecycerView。在滚动列表时,actionbar get hide,其中action bar是工具栏,但listview的某些部分隐藏在选项卡后面。在这里,我使用了AppBarLayout和工具栏,其中有app:layout_scrollflags=“scrollenterAlways”,在我的recylerview中有app:layout_be