gpt4 book ai didi

java - Android Actionbar 选项卡和键盘焦点

转载 作者:塔克拉玛干 更新时间:2023-11-02 09:02:52 26 4
gpt4 key购买 nike

问题

我有一个非常简单的 Activity ,有两个选项卡,我正在尝试在自定义 View 中处理键盘输入。这很好用......直到我交换标签。一旦我交换了选项卡,我就再也无法捕获事件了。然而,在另一个应用程序中,打开一个对话框然后关闭它会允许我的关键事件通过。如果不这样做,我发现无法再次获取我的关键事件。

这里有什么问题?一旦我交换选项卡,我就找不到任何方法来获取关键事件,并且很好奇是什么在吞噬它们。这个例子非常简短,切中要点。

代码

主.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>

我的 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,那么在蜂窝平板电脑上,人们可能会认为点击它和类似的操作会使它聚焦。然而,这不一定是真的。如果该 View 恰好是android:clickable,那么点击确实会聚焦 View 。如果它不可点击,则不会被触摸聚焦。

此外,当换出 fragment 时,会出现与首次实例化 Activity View 时非常相似的问题。只有在 View 层次结构完全准备好后,才需要进行某些更改。

如果在 View 层次结构完全准备好之前在 fragment 中的 View 上调用“requestFocus()”, View 确实会认为它已获得焦点;但是,如果软键盘启动,它实际上不会向该 View 发送任何事件!更糟糕的是,如果那个 View 是可点击的,此时点击它并不能解决这个键盘焦点问题,因为 View 认为它确实得到了焦点并且无事可做。如果要聚焦一些其他 View ,然后点击回到这个 View ,但是,因为它既可单击又可聚焦,它确实会聚焦并将键盘输入定向到该 View 。

鉴于该信息,在切换到选项卡时设置焦点的正确方法是在 fragment 切换后将可运行对象发布到 fragment 的 View 层次结构,然后才调用 requestFocus()。调用 requestFocus() 之后 View 层次结构已完全准备好,这将聚焦 View 以及将键盘输入直接输入到它,如我们所愿。它不会进入 View 聚焦但键盘输入不知何故未指向它的那种奇怪的聚焦状态,如果在 View 层次结构完全准备好之前调用 requestFocus() 就会发生这种情况。

同样重要的是,在 fragment 布局的 XML 中使用“requestFocus”标记通常会过早调用 requestFocus()。没有理由在 fragment 的布局中使用该标签。在 fragment 之外,也许……但不在 fragment 之内。

在代码中,我在 fragment 的顶部添加了一个 EditText,只是为了测试点击焦点更改行为,点击自定义 View 也会切换软键盘。交换选项卡时,焦点也应默认为自定义 View 。我试图有效地注释代码。

代码

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");
}
}

主.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>

我的 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>

关于java - Android Actionbar 选项卡和键盘焦点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8087715/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com