Frammento su un altro problema di frammento


271

Quando sto mostrando un frammento (che è a schermo intero con lo #77000000sfondo) su un altro frammento (chiamiamolo principale), il mio frammento principale reagisce ancora ai clic (possiamo fare clic su un pulsante anche se non lo vediamo).

Domanda : come prevenire i clic sul primo frammento (principale)?

MODIFICARE

Sfortunatamente, non posso semplicemente nascondere il frammento principale, perché sto usando uno sfondo trasparente sul secondo frammento (quindi, l'utente può vedere cosa si trova dietro).


Sulla base di ciò con cui ci hai dato di lavorare, dovresti provare a impostare il Visibilitytuo main Fragmentsu GONEquando non lo stai usando.
Adneal,

1
Senza vedere come si implementa il metodo onClicked, immagino che quando si fa clic si restituisce "false".
DeeV,

@DeeV, il onClickmetodo non restituisce nulla. Ma tu dai un'idea, grazie (posterò presto una risposta).
Dmitry Zaytsev il

1
D'oh. Hai ragione. onTouch lo restituisce. Vorrei solo capire perché un evento touch è caduto in un frammento. Non dovrebbe farlo se non si generano eventi di tocco.
DeeV,

@DeeV, sembra che la tua vista (che, ad esempio sopra l'altra) non rileva l'evento Touch, quindi il sistema continua a cercare altre viste con le stesse coordinate.
Dmitry Zaytsev il

Risposte:


578

Impostare la clickableproprietà nella vista del secondo frammento su true. La vista catturerà l'evento in modo che non venga passato al frammento principale. Quindi se la vista del secondo frammento è un layout, questo sarebbe il codice:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true" />

4
Questo ha funzionato per me. Sembra più facile della soluzione che @Dmitry Zaitsev ha dato sopra. C'è qualche motivo per cui questa sarebbe una cattiva idea? Non riesco a pensare a nessuno, ma voglio solo esserne sicuro.
Ariete

1
Questo non ha funzionato per me. Ho un RelativeLayoutframmento all'interno e ho impostato l'intera vista con la clickeableproprietà. La soluzione di @Dmitry risolve il mio problema.
4gus71n,

3
Questo ha funzionato per me. "cliccabile" in Android apparentemente è un po 'come "userInteractionEnabled" di iOS
mvds

17
Perché Android ci sottopone a dure condizioni di codifica ?!
Lo-Tan,

1
Sto riscontrando lo stesso problema con ViewPager. Quando scorro sulla prima pagina, passa anche alla seconda pagina e questa soluzione non ha funzionato per me. Qualche idea?
Gokhan Arik,

72

La soluzione è piuttosto semplice. Nel nostro secondo frammento (che si sovrappone al nostro frammento principale) dobbiamo solo catturare l' onTouchevento:

@Override
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstance){
    View root = somehowCreateView();

    /*here is an implementation*/

    root.setOnTouchListener(new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            return true;
        }
    });
    return root;
}

la tua soluzione ha funzionato +1 per questo, ma puoi dirmi perché dobbiamo farlo esplicitamente?
Caccia il

@Hunt non devi. Questo è solo un altro modo per farlo (vedi risposta accettata)
Dmitry Zaytsev,

android: clickable = "true" sul layout principale xml del tuo secondo frammento.
Rishabh Srivastava,

C'è un problema con questa soluzione, quando si attiva la modalità talkback di accessibilità, non legge i singoli elementi ma si concentra sulla vista principale.
Amit Garg,

Versione di Kotlin: root.setOnTouchListener {_, _ -> true}
Gal Rom,

21

Basta aggiungere clickable="true"e focusable="true"al layout principale

 <android.support.constraint.ConstraintLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:clickable="true"
      android:focusable="true">

      <!--Your views-->

 </android.support.constraint.ConstraintLayout>

Se stai usando AndroidX, prova questo

 <androidx.constraintlayout.widget.ConstraintLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:clickable="true"
      android:focusable="true">

          <!--Your views-->

 </androidx.constraintlayout.widget.ConstraintLayout>

È in qualche modo diverso dalla risposta accettata? focuseablenon è veramente necessario.
Dmitry Zaytsev,

2
Penso che focusable="true"qui sia solo per evitare un avviso in Android Studio.
Artem M,

9

Dovresti nascondere il primo frammento quando mostri il secondo frammento se due frammenti sono posizionati nella stessa vista contenitore.

Se vuoi sapere altre domande su come risolvere i problemi su Fragment, puoi vedere la mia biblioteca: https://github.com/JustKiddingBaby/FragmentRigger

FirstFragment firstfragment;
SecondFragment secondFragment;
FragmentManager fm;
FragmentTransaction ft=fm.beginTransaction();
ft.hide(firstfragment);
ft.show(secondFragment);
ft.commit();

1
Questa dovrebbe essere la risposta giusta! Grazie amico. Ho risolto questa domanda su un altro progetto. Ma ho dimenticato come l'ho risolto e alla fine ho ricevuto la tua risposta. Grazie.
Licat Julius,

1
Non penso che questa sia la soluzione giusta. Frammenti / Attività funzionano in una pila di viste. Dovrai chiamare di nuovo .show dopo che il frammento superiore è stato estratto dallo stack, il che significa che il frammento inferiore deve essere informato che quello superiore è sparito. È appena stata aggiunta una logica aggiuntiva da mantenere.
XY

4

Devi aggiungere android:focusable="true"conandroid:clickable="true"

Clickable significa che può essere cliccato da un dispositivo puntatore o essere toccato da un dispositivo touch.

Focusablesignifica che può ottenere lo stato attivo da un dispositivo di input come una tastiera. I dispositivi di input come le tastiere non possono decidere a quale vista inviare i propri eventi di input in base agli input stessi, quindi li inviano alla vista che ha lo stato attivo.


2
and focusable = "true" è necessario per evitare un avviso nel nuovo Android Studio
Hossam Hassan,

2

C'è più di una soluzione che alcuni di noi hanno contribuito a questo thread, ma vorrei anche menzionare un'altra soluzione. Se non ti va di mettere selezionabili e focalizzabili è uguale a ViewGroup radice di ogni layout in XML come me. Puoi anche metterlo alla tua base se ne hai uno proprio come sotto;

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

        val rootView = inflater.inflate(layout, container, false).apply {
            isClickable = true
            isFocusable = true
        }

        return rootView
    }

Puoi anche usare la variabile inline ma non l'ho preferita per i miei motivi.

Spero che sia d'aiuto per coloro che odiano gli XML di layout.


1

La risposta accettabile "funzionerà", ma causerà anche costi di prestazione (sovraccarico, rimisurazione in caso di cambio di orientamento) poiché il frammento sul fondo è ancora in fase di disegno. Forse dovresti semplicemente trovare il frammento per tag o ID e impostare la visibilità su GONE o VISIBLE quando devi mostrarlo di nuovo.

A Kotlin:

fragmentManager.findFragmentByTag(BottomFragment.TAG).view.visibility = GONE

Questa soluzione è preferibile all'alternativa hide()e ai show()metodi di FragmentTransactionutilizzo delle animazioni. Lo chiami semplicemente da onTransitionStart()e onTransitionEnd()di Transition.TransitionListener.


1

Metod 1:

È possibile aggiungere a tutti i layout dei frammenti

android:clickable="true"
android:focusable="true"
android:background="@color/windowBackground"

Metod 2: (programmaticamente)

Estendi tutto il frammento da FragmentBaseecc. Quindi aggiungi questo codice aFragmentBase

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    getView().setBackgroundColor(getResources().getColor(R.color.windowBackground));
    getView().setClickable(true);
    getView().setFocusable(true);
}

0

Quello che puoi fare è dare un clic vuoto al layout del frammento precedente usando la proprietà onClick per il layout principale di quel frammento principale e nell'attività puoi creare una funzione doNothing(View view)e non scrivere nulla al suo interno. Questo lo farà per te.


0

Sembra un caso per DialogFragment. Altrimenti con Fragment Manager commetti uno da nascondere e l'altro da mostrare. Questo ha funzionato per me.


0

L'aggiunta di android:clickable="true"non ha funzionato per me. Questa soluzione non funziona su CoordinatorLayout quando si tratta di un layout padre. Ecco perché ho creato RelativeLayout come layout principale, aggiunto android:clickable="true"ad esso e posizionato CoordinatorLayout su questo RelativeLayout.


0

Ho avuto più frammenti con lo stesso xml.
Dopo aver trascorso ore, ho rimosso setPageTransformere ha iniziato a funzionare

   //  viewpager.setPageTransformer(false, new BackgPageTransformer())

Avevo una logica spaventosa.

public class BackgPageTransformer extends BaseTransformer {

    private static final float MIN_SCALE = 0.75f;

    @Override
    protected void onTransform(View view, float position) {
        //view.setScaleX Y
    }

    @Override
    protected boolean isPagingEnabled() {
        return true;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.