- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我有一个非常简单的 Activity ,有两个选项卡,我正在尝试在自定义 View 中处理键盘输入。这很好用......直到我交换标签。一旦我交换了选项卡,我就再也无法捕获事件了。然而,在另一个应用程序中,打开一个对话框然后关闭它会允许我的关键事件通过。如果不这样做,我发现无法再次获取我的关键事件。
这里有什么问题?一旦我交换选项卡,我就找不到任何方法来获取关键事件,并且很好奇是什么在吞噬它们。这个例子非常简短,切中要点。
<?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>
<?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>
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 。我试图有效地注释代码。
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 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>
<?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/
我最近在/ drawable中添加了一些.gifs,以便可以将它们与按钮一起使用。这个工作正常(没有错误)。现在,当我重建/运行我的应用程序时,出现以下错误: Error: Gradle: Execu
Android 中有返回内部存储数据路径的方法吗? 我有 2 部 Android 智能手机(Samsung s2 和 s7 edge),我在其中安装了一个应用程序。我想使用位于这条路径中的 sqlit
这个问题在这里已经有了答案: What's the difference between "?android:" and "@android:" in an android layout xml f
我只想知道 android 开发手机、android 普通手机和 android root 手机之间的实际区别。 我们不能从实体店或除 android marketplace 以外的其他地方购买开发手
自Gradle更新以来,我正在努力使这个项目达到标准。这是一个团队项目,它使用的是android-apt插件。我已经进行了必要的语法更改(编译->实现和apt->注释处理器),但是编译器仍在告诉我存在
我是android和kotlin的新手,所以请原谅要解决的一个非常简单的问题! 我已经使用导航体系结构组件创建了一个基本应用程序,使用了底部的导航栏和三个导航选项。每个导航选项都指向一个专用片段,该片
我目前正在使用 Facebook official SDK for Android . 我现在正在使用高级示例应用程序,但我不知道如何让它获取应用程序墙/流/状态而不是登录的用户。 这可能吗?在那种情
我在下载文件时遇到问题, 我可以在模拟器中下载文件,但无法在手机上使用。我已经定义了上网和写入 SD 卡的权限。 我在服务器上有一个 doc 文件,如果用户单击下载。它下载文件。这在模拟器中工作正常但
这个问题在这里已经有了答案: What is the difference between gravity and layout_gravity in Android? (22 个答案) 关闭 9
任何人都可以告诉我什么是 android 缓存和应用程序缓存,因为当我们谈论缓存清理应用程序时,它的作用是,缓存清理概念是清理应用程序缓存还是像内存管理一样主存储、RAM、缓存是不同的并且据我所知,缓
假设应用程序 Foo 和 Eggs 在同一台 Android 设备上。任一应用程序都可以获取设备上所有应用程序的列表。一个应用程序是否有可能知道另一个应用程序是否已经运行以及运行了多长时间? 最佳答案
我有点困惑,我只看到了从 android 到 pc 或者从 android 到 pc 的例子。我需要制作一个从两部手机 (android) 连接的 android 应用程序进行视频聊天。我在想,我知道
用于使用 Android 以编程方式锁定屏幕。我从 Stackoverflow 之前关于此的问题中得到了一些好主意,并且我做得很好,但是当我运行该代码时,没有异常和错误。而且,屏幕没有锁定。请在这段代
文档说: android:layout_alignParentStart If true, makes the start edge of this view match the start edge
我不知道这两个属性和高度之间的区别。 以一个TextView为例,如果我将它的layout_width设置为wrap_content,并将它的width设置为50 dip,会发生什么情况? 最佳答案
这两个属性有什么关系?如果我有 android:noHistory="true",那么有 android:finishOnTaskLaunch="true" 有什么意义吗? 最佳答案 假设您的应用中有
我是新手,正在尝试理解以下 XML 代码: 查看 developer.android.com 上的文档,它说“starStyle”是 R.attr 中的常量, public static final
在下面的代码中,为什么当我设置时单选按钮的外观会发生变化 android:layout_width="fill_parent" 和 android:width="fill_parent" 我说的是
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 9
假设我有一个函数 fun myFunction(name:String, email:String){},当我调用这个函数时 myFunction('Ali', 'ali@test.com ') 如何
我是一名优秀的程序员,十分优秀!