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

34

我有一个带有Pager的Fragment活动:

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

我捕捉到了onKeyDown事件:

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

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

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

6个回答

42

你可以在你的Fragment类中定义一个自定义的方法。例如:

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

在你的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);
}

8
@JonWillis 欢迎提供更好的解决方案。 - Oleksiy
2
实际上,你可以使用接口、LocalBroadcast,或者只需使用EventBus并通过它向任何想要的人发送KeyEvent。 - Stan
4
"fragments" 的意思是 “片段”,请问你在哪里声明了它? - Choletski
1
@JonWillis,为什么它们不应该是紧密耦合的?任何活动只能使用其设计的那些片段。只有父活动知道显示哪些片段并应该接收事件。 此外,我会将其移动到BaseFragment和BaseActivity中,以成为整个项目的默认行为。 还应该使myOnKeyDown具有相同的签名,特别是返回布尔值以判断事件是否已处理。 - Luten
@JonWillis,Activity 还应该知道事件是否已被处理。 当您可以触发并忘记并且不关心谁会收到事件时,EventBus 和类似工具非常好用,但这里不是这种情况。 - Luten

11
如果有人对如何使用Broadcast感兴趣:
在您的片段中,在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();

 }
};

您的 keyevent - 在活动中放置的代码

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

你的广播发送者:

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

2
我喜欢这个解决方案。比标记为正确的那个耦合度更低。谢谢! - Jvalant Dave

9

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

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

广播

使用Android的LocalBroadcastManager:文档

创建一个BroadcastReceiver:

class SomeBroadcastReceiver : BroadcastReceiver() {

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

}

在您的活动中:
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)
    }

}

然后,在任何片段(或服务等)中:
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

创建一个事件类:

data class Event(val keyCode: Int)

在您的活动中:
class SomeActivity : AppCompatActivity() {

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

}

然后,在您的片段中:
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)
    }

}

广播必须消失!哈哈。顺便说一下,你能在答案中包含广播和事件总线真是太好了。 - Machado

7

在避免紧密耦合的情况下,我发现@hsu.tw的答案后,找到了这个代码片段

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

在Fragment中独立于Activity附加View.OnKeyListener到视图所需的步骤如下(请查看代码片段):

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

我将这个实现放在了Fragment的onViewCreated回调函数中。


1
onViewCreated?这样更有意义。 - hsu.tw

1

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

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

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

在你的情况下,你应该在Adapter和PagerChangeListener中进行设置-KeyEventListener。


1
我已经对Activity和Fragment类进行了子类化,以执行KeyEvent传递。对我来说,这比发送本地广播更清晰。但是这种解决方案可能不够灵活。请自行选择首选方式。
以下是该活动:
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);
    }
}

而且这个片段:

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

概述:https://gist.github.com/0neel/7d1ed5d26f2148b4168b6616337159ed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接