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