web-dev-qa-db-fra.com

Activité de fragment prise sur KeyDown et utilisation dans le fragment

J'ai une activité Fragment avec 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);

J'attrape l'événement onKeyDown:

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

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

La question est: comment utiliser l'événement dans tous les fragments que j'ai instanciés dans cette activité. Merci

26
FlorinD

Ce que vous pouvez faire est de définir une méthode personnalisée dans vos classes de fragments. Par exemple:

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

et appelez cette méthode chaque fois qu'un événement de frappe est déclenché dans votre classe Activity. Par exemple:

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

Si quelqu'un veut savoir comment faire avec Broadcast:

Dans votre fragment dans 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();

 }
};

votre événement clé - code pour mettre en activité

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

votre expéditeur de diffusion:

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

Comme mentionné par d'autres, la réponse acceptée se traduit par un couplage étroit entre l'activité et ses fragments.

Je suggère d'utiliser à la place une sorte d'implémentation basée sur les événements. Ceci est beaucoup plus réutilisable et se traduit par une meilleure architecture logicielle. Dans les projets précédents, j'ai utilisé l'une des solutions suivantes (Kotlin):

Diffusions

Utilisation de LocalBroadcastManager d'Android: Documentation

Créez un BroadcastReceiver:

class SomeBroadcastReceiver : BroadcastReceiver() {

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

}

Dans votre activité:

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

}

Ensuite, dans l'un des fragments (ou 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

Utilisation de EventBus

Créez une classe d'événement:

data class Event(val keyCode: Int)

Dans votre activité:

class SomeActivity : AppCompatActivity() {

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

}

Ensuite, dans votre 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)
    }

}
2
kjellhaaland

J'ai le même problème lors du développement de l'application Android TV.

Et je résous ce problème comme ceci:

Dans la méthode onCreateView, j'appelle "requestFocus" par certains View. (Je le marque comme ViewA.) Ensuite, je définis KeyEventListener sur ViewA.

Dans votre cas, vous devez le faire (set-KeyEventListener) dans Adapter et PagerChangeListener.

1
hsu.tw

J'ai sous-classé les classes d'activité et de fragmentation pour effectuer la réussite des KeyEvents. Pour moi, cela semble plus clair que d'envoyer des émissions locales. Mais cette solution peut ne pas être aussi flexible. Choisissez vous-même la voie préférée.

Voici l'activité:

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

Et le 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

1
0neel

Suite à la réponse @ hsu.tw pour éviter un couplage serré, j'ai trouvé cela Gist .

Éviter un couplage serré a un prix: vous avez besoin d'une vue focalisable (heureusement, c'était mon cas car j'avais déjà une vue au premier plan qui écoutait d'autres événements tactiles, alors j'ai juste ajouté le View.OnKeyListener).

Les étapes nécessaires pour attacher un View.OnKeyListener à une vue dans un fragment indépendamment de l'activité sont (vérifiez Gist ):

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

J'ai implémenté cela dans le rappel de onViewCreated de mon fragment

1
dnhyde