【问题标题】:Fragment activity catch onKeyDown and use in fragment片段活动捕获 onKeyDown 并在片段中使用
【发布时间】:2012-08-26 00:13:48
【问题描述】:

我有寻呼机的片段活动:

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

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

【问题讨论】:

    标签: android android-activity fragment onkeydown


    【解决方案1】:

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

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

    【讨论】:

    • @JonWillis 随时提供更好的解决方案
    • 其实你可以使用接口、LocalBroadcast 或者只使用 EventBus 并使用它发送 KeyEvent 给任何想要它的人。
    • fragments 是什么意思,你在哪里声明的?
    • @JonWillis,他们为什么不应该通过紧耦合?任何活动都只能使用它设计的那些片段。并且只有父活动知道显示哪些片段并且应该接收事件此外,我会将其移至 BaseFragment 和 BaseActivity 作为整个项目的默认行为。还应该使myOnKeyDown 具有相同的签名,尤其是返回布尔值以判断是否处理了事件
    • @JonWillis,Activity 也应该知道是否处理了事件。当您可以触发并忘记并且不在乎谁将获得事件时,EventBus 和仪器都很好,但事实并非如此
    【解决方案2】:

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

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

    【讨论】:

    • 我喜欢这个解决方案。比标记为正确的耦合少。谢谢!
    【解决方案3】:

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

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

    广播

    使用 Android 的 LocalBroadcastManager:Documentation

    创建一个广播接收器:

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

    【讨论】:

    • 广播必须死!哈哈。顺便说一句,您很高兴在分析器中包含广播和事件总线。
    【解决方案4】:

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

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

    View.OnKeyListener 附加到独立于活动的片段中的视图所需的步骤是(检查gist):

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

    我在 Fragment 的 onViewCreated 回调中实现了这一点

    【讨论】:

    • onViewCreated?这似乎更有意义。
    【解决方案5】:

    我对 Activity 和 Fragment 类进行了子类化以执行 KeyEvents 传递。对我来说,它看起来比发送本地广播更清晰。但是这个解决方案可能不是那么灵活。自己选择喜欢的方式。

    这是活动:

    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

    【讨论】:

      【解决方案6】:

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

      我这样解决这个问题:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多