Android 片段活动捕获 onKeyDown 并在片段中使用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12210906/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 10:01:46  来源:igfitidea点击:

Fragment activity catch onKeyDown and use in fragment

androidandroid-activityfragmentonkeydown

提问by FlorinD

I have Fragment activity with pager:

我有寻呼机的片段活动:

List<Fragment> fragments = new Vector<Fragment>();
    fragments.add(Fragment.instantiate(this, PastEventListFragment.class.getName(),bundle));
    fragments.add(Fragment.instantiate(this, EventListFragment.class.getName(),bundle));

    this.mPagerAdapter  = new EventPagerAdapter(super.getSupportFragmentManager(), fragments);
    //
    ViewPager pager = (ViewPager)super.findViewById(R.id.viewpager1);

    pager.setAdapter(this.mPagerAdapter);
    pager.setCurrentItem(1);

I catch onKeyDown event :

我抓住了 onKeyDown 事件:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_MENU) {

    }
    return super.onKeyDown(keyCode, event);
}

The Question is: How to use event in all fragments i have instantiated in this activity . Thanks

问题是:如何在我在此活动中实例化的所有片段中使用事件。谢谢

回答by waqaslam

What you can do is to define a custom method in your fragment class(s). For example:

您可以做的是在片段类中定义自定义方法。例如:

public void myOnKeyDown(int key_code){
   //do whatever you want here
}

and call this method whenever a key-down event is raised in your Activity class. For example:

并在您的 Activity 类中引发按键按下事件时调用此方法。例如:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_MENU) {
        ((PastEventListFragment)fragments.get(0)).myOnKeyDown(keyCode);
        ((EventListFragment)fragments.get(1)).myOnKeyDown(keyCode);

        //and so on...
    }
    return super.onKeyDown(keyCode, event);
}

回答by PKAP

If someone is interessed how to do it with Boradcast:

如果有人对如何使用 Boradcast 感兴趣:

In your fragment in onViewCreated

在 onViewCreated 的片段中

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);


// Register to receive messages.
// We are registering an observer (mMessageReceiver) to receive Intents
// with actions named "custom-event-name".
 LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
 new IntentFilter("activity-says-hi"));

...}

 // Our handler for received Intents. This will be called whenever an Intent
 // with an action named "custom-event-name" is broadcasted.
 private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
 @Override
 public void onReceive(Context context, Intent intent) {
 // Get extra data included in the Intent

 doSomethingCauseVolumeKeyPressed();

 }
};

your keyevent - code to put in activity

您的关键事件 - 投入活动的代码

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    int action = event.getAction();
    int keyCode = event.getKeyCode();
    switch (keyCode) {
        case KeyEvent.KEYCODE_VOLUME_UP:
            if (action == KeyEvent.ACTION_DOWN) {
                sendBroadcast();
            }
            return true;
        case KeyEvent.KEYCODE_VOLUME_DOWN:
            if (action == KeyEvent.ACTION_DOWN) {
                sendBroadcast();
            }
            return true;
        default:
            return super.dispatchKeyEvent(event);
    }
}

your broadcast sender:

您的广播发件人:

private void  sendVolumeBroadcast(){
    Intent intent = new Intent("activity-says-hi");
    LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

回答by kjellhaaland

As mentioned by others, the accepted answer results in tight coupling between the activity and its fragments.

正如其他人所提到的,接受的答案导致活动与其片段之间的紧密耦合。

I would suggest using some sort of event-based implementation instead. This is much more reusable and results in a better software architecture. In previous projects I have used one of the following solutions (Kotlin):

我建议改用某种基于事件的实现。这更具可重用性,并产生更好的软件架构。在以前的项目中,我使用了以下解决方案之一(Kotlin):

Broadcasts

广播

Using Android′s LocalBroadcastManager: Documentation

使用 Android 的 LocalBroadcastManager:文档

Create a BroadcastReceiver:

创建一个广播接收器:

class SomeBroadcastReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context?, intent: Intent?) {
        val keyCode = intent?.getIntExtra("KEY_CODE", 0)
        // Do something with the event
    }

}

}

In your activity:

在您的活动中:

class SomeActivity : AppCompatActivity() {

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        val intent = Intent("SOME_TAG").apply { putExtra("KEY_CODE", keyCode) }
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
        return super.onKeyDown(keyCode, event)
    }

}

Then, in any of the fragments (or services, etc..):

然后,在任何片段(或服务等)中:

class SomeFragment : Fragment() {

    val receiver = SomeBroadcastReceiver()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        val filter = IntentFilter().apply { addAction("SOME_TAG") }
        LocalBroadcastManager.getInstance(context!!).registerReceiver(receiver, filter)

        return super.onCreateView(inflater, container, savedInstanceState)
    }

}

EventBus

事件总线

Using EventBus

使用事件总线

Create an event class:

创建事件类:

data class Event(val keyCode: Int)

In your activity:

在您的活动中:

class SomeActivity : AppCompatActivity() {

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        EventBus.getDefault().post(Event(keyCode))
        return super.onKeyDown(keyCode, event)
    }

}

Then, in your fragment:

然后,在您的片段中:

class SomeFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        // Register for events
        EventBus.getDefault().register(this)

        return super.onCreateView(inflater, container, savedInstanceState)
    }

    @Subscribe
    public fun onKeyEvent(event : Event) {
        // Called by eventBus when an event occurs
    }

    override fun onDestroyView() {
        super.onDestroyView()
        EventBus.getDefault().unregister(this)
    }

}

回答by dnhyde

Following @hsu.tw answer to avoid tight coupling I found this gist.

按照@hsu.tw 的回答以避免紧耦合,我发现了这个要点

Avoiding tight coupling comes with a price: you need a focusable view (luckily it was my case since I already had a view in foreground that listens to other touch events, so I just added the View.OnKeyListenerto it).

避免紧密耦合是有代价的:你需要一个可聚焦的视图(幸运的是这是我的情况,因为我已经在前台有一个可以监听其他触摸事件的视图,所以我只是添加了View.OnKeyListener它)。

The steps needed to attach a View.OnKeyListenerto a view in a Fragment independently of the Activity are (check the gist):

View.OnKeyListener独立于 Activity将 a 附加到 Fragment 中的视图所需的步骤是(检查要点):

 view.setFocusableInTouchMode(true);
 view.requestFocus();
 view.setOnKeyListener(pressKeyListener);

I implemented this in the onViewCreatedcallback of my Fragment

我在onViewCreatedFragment的回调中实现了这个

回答by hsu.tw

I have the same problem in developing Android TV app.

我在开发 Android TV 应用时遇到了同样的问题。

And I solve this problem like this:

我是这样解决这个问题的:

In onCreateView method, I call "requestFocus" by some View. (I mark it as ViewA.) Then I set KeyEventListener to ViewA.

在 onCreateView 方法中,我通过一些视图调用“requestFocus”。(我将其标记为 ViewA。)然后我将 KeyEventListener 设置为 ViewA。

In your case, you should do it (set-KeyEventListener) in Adapter and PagerChangeListener.

在您的情况下,您应该在 Adapter 和 PagerChangeListener 中执行此操作(set-KeyEventListener)。

回答by 0neel

I've subclassed Activity and Fragment classes to perform KeyEvents passing. For me, it looks clearer than sending local broadcasts. But this solution can be not so flexible. Choose the preferred way by yourself.

我已经将 Activity 和 Fragment 类子类化来执行 KeyEvents 传递。对我来说,它看起来比发送本地广播更清晰。但是这个解决方案可能没有那么灵活。自行选择首选方式。

Here is the activity:

这是活动:

public abstract class KeyEventPassingActivity extends Activity {

    public interface KeyEventListener extends View.OnKeyListener {
        boolean isVisible();
        View getView();
    }

    private final List<KeyEventListener> keyEventHandlerList = new ArrayList<>();

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        for (KeyEventListener handler : keyEventHandlerList) {
            if (handleKeyEvent(handler, event)) {
                return true;
            }
        }
        return super.dispatchKeyEvent(event);
    }

    void addKeyEventHandler(@NonNull KeyEventListener handler) {
        keyEventHandlerList.add(handler);
    }

    void removeKeyEventHandler(@NonNull KeyEventListener handler) {
        keyEventHandlerList.remove(handler);
    }

    /**
     * @return <tt>true</tt> if the event was handled, <tt>false</tt> otherwise
     */
    private boolean handleKeyEvent(@Nullable KeyEventListener listener, KeyEvent event) {
        return listener != null
                && listener.isVisible()
                && listener.onKey(listener.getView(), event.getKeyCode(), event);
    }
}

And the fragment:

和片段:

public abstract class KeyEventHandlingFragment extends Fragment
        implements KeyEventPassingActivity.KeyEventListener {

    @SuppressWarnings("deprecation")
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (activity instanceof KeyEventPassingActivity) {
            ((KeyEventPassingActivity) activity).addKeyEventHandler(this);
        }
    }

    @Override
    public void onDetach() {
        Activity activity = getActivity();
        if (activity instanceof KeyEventPassingActivity) {
            ((KeyEventPassingActivity) activity).removeKeyEventHandler(this);
        }
        super.onDetach();
    }
}

Gist: https://gist.github.com/0neel/7d1ed5d26f2148b4168b6616337159ed

要点:https: //gist.github.com/0neel/7d1ed5d26f2148b4168b6616337159ed