我已经解决了自己的问题,所以我认为我应该分享解决方案。如果有措词问题,请在评论中纠正我;我正在尝试尽可能地准确,但我并不完全是一名Android专家。一般而言,此答案还应作为一个很好的示例,说明如何处理换出ActionBar选项卡。无论是否喜欢解决方案代码的设计,它都应该很有用。
以下链接帮助我弄清楚了我的问题:http
:
//pre.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 { public static class ActionBarTabManager { public static interface TabChangeListener { public abstract void onTabSelected(String tag); public abstract void onTabSelectedPost(String tag); public abstract void onTabReselected(String tag); 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; 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); } private class TabSelectedPostRunnable implements Runnable { String mTag = null; public TabSelectedPostRunnable(String tag) { mTag=tag; } @Override public void run() { if (mListener != null) { mListener.onTabSelectedPost(mTag); } } } 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); } } } 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)));} } 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 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>



