Fragmento sobre outro problema de fragmento

271

Quando estou mostrando um fragmento (que é tela cheia com #77000000plano de fundo) sobre outro fragmento (vamos chamá-lo de principal), meu fragmento principal ainda reage aos cliques (podemos clicar em um botão, mesmo que não o possamos ver).

Pergunta : como evitar cliques no primeiro fragmento (principal)?

EDITAR

Infelizmente, não posso apenas ocultar o fragmento principal, porque estou usando um fundo transparente no segundo fragmento (para que o usuário possa ver o que está localizado atrás).

Dmitry Zaytsev
fonte
Com base no que você nos deu para trabalhar, tente definir o Visibilityseu main Fragmentpara GONEquando não o estiver usando.
adneal
1
Sem ver como você implementa seu método onClicked, acho que você está retornando "false" quando clicado.
Deev
@DeeV, o onClickmétodo não retorna nada. Mas você dá uma ideia, obrigado (postarei resposta em breve).
Dmitry Zaytsev
1
D'oh. Você está certo. onTouch retorna. Eu só queria entender por que um evento de toque caiu em um fragmento. Isso não deve ser feito se você não estiver emitindo eventos de toque.
DeeV 30/04
@DeeV, parece que sua visualização (que, por exemplo, uma sobre a outra) não captura o evento onTouch, e o sistema continua procurando outras visualizações com as mesmas coordenadas.
Dmitry Zaytsev

Respostas:

578

Defina a clickablepropriedade na exibição do segundo fragmento como true. A visualização capturará o evento para que ele não seja passado para o fragmento principal. Portanto, se a visualização do segundo fragmento for um layout, este seria o código:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true" />
Jure Vizjak
fonte
4
Isso funcionou para mim. Parece mais fácil do que a solução que @Dmitry Zaitsev deu acima. Existe alguma razão para que isso seria uma má idéia? Não consigo pensar em nada, mas só quero ter certeza.
Ariets
1
Isso não funcionou para mim. Eu tenho um RelativeLayoutdentro do fragmento e defino toda a exibição com a clickeablepropriedade A solução do @Dmitry resolve meu problema.
precisa saber é o seguinte
3
Isso funcionou para mim. "clicável" no Android aparentemente parece um pouco com o "userInteractionEnabled" do iOS
mvds 30/01
17
Por que o Android nos sujeita a condições severas de codificação ?!
Lo-Tan
1
Estou tendo o mesmo problema com o ViewPager. Quando rolar na primeira página, ela passa para a segunda página também, e esta solução não funcionou para mim. Alguma ideia?
Gokhan Arik
72

A solução é bem simples. Em nosso segundo fragmento (que se sobrepõe ao nosso fragmento principal), apenas precisamos pegar o 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;
}
Dmitry Zaytsev
fonte
sua solução funcionou +1 para isso, mas você pode me dizer por que precisamos fazer isso explicitamente?
Caça
@ Caça você não precisa. Essa é apenas outra maneira de fazê-lo (consulte a resposta aceita) #
687 Dmitry Zaytsev
android: clickable = "true" no layout principal xml do seu segundo fragmento.
Rishabh Srivastava
Há um problema com esta solução: quando você entra no modo de retorno de acessibilidade, ele não lê os elementos individuais, mas o foco na visualização raiz.
Amit Garg
Versão do Kotlin: root.setOnTouchListener {_, _ -> true}
Gal Rom
21

Basta adicionar clickable="true"e focusable="true"ao layout pai

 <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 você estiver usando AndroidX, tente isto

 <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>
Miravzal
fonte
Isso é de alguma forma diferente da resposta aceita? focuseablenão é realmente necessário.
Dmitry Zaytsev
2
Eu acho que focusable="true"aqui é apenas para evitar um aviso no Android Studio.
Artem M
9

Você deve ocultar o primeiro fragmento quando estiver exibindo o segundo fragmento se dois fragmentos forem colocados na mesma exibição de contêiner.

Se você quiser saber mais perguntas sobre como resolver problemas sobre o Fragment, poderá ver minha biblioteca: https://github.com/JustKiddingBaby/FragmentRigger

FirstFragment firstfragment;
SecondFragment secondFragment;
FragmentManager fm;
FragmentTransaction ft=fm.beginTransaction();
ft.hide(firstfragment);
ft.show(secondFragment);
ft.commit();
JingYeoh
fonte
1
Esta deve ser a resposta certa! Obrigado cara. Eu resolvi essa pergunta em outro projeto. Mas eu esqueci Como resolvi isso e, finalmente, recebi sua resposta. Obrigado.
Jul18
1
Não acho que essa seja a solução certa. Fragmentos / Atividades funcionam em uma pilha de vistas. Você precisará chamar .show novamente depois que o fragmento superior for retirado da pilha, o que significa que o fragmento inferior deve ser informado de que o fragmento superior se foi. É apenas adicionado lógica extra para manter.
XY
4

Você precisa adicionar android:focusable="true"comandroid:clickable="true"

Clickable significa que ele pode ser clicado por um dispositivo apontador ou toque em um dispositivo de toque.

Focusablesignifica que ele pode obter o foco de um dispositivo de entrada como um teclado. Dispositivos de entrada como teclados não podem decidir para qual exibição enviar seus eventos de entrada com base nas próprias entradas; portanto, eles os enviam para a exibição em foco.

Hossam Hassan
fonte
2
e focusable = "true" é necessária para evitar um aviso no novo Estúdio Android
Hossam Hassan
2

Há mais de uma solução que alguns de nós contribuíram para esse segmento, mas também gostaria de mencionar uma outra solução. Se você não gosta de colocar cliques e foco, é igual ao ViewGroup raiz de cada layout em XML como eu. Você também pode colocá-lo em sua base se tiver um como abaixo;

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
    }

Você também pode usar a variável inline, mas eu não a preferi por meus motivos.

Espero que ajude aqueles que odeiam XMLs de layout.

Yekta Sarıoğlu
fonte
1

A resposta aceitável "funcionará", mas também causará custo de desempenho (excesso, re-medição na mudança de orientação), pois o fragmento na parte inferior ainda está sendo desenhado. Talvez você deva simplesmente encontrar o fragmento por tag ou ID e definir a visibilidade como GONE ou VISIBLE quando precisar mostrar novamente.

Em Kotlin:

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

Essa solução é preferível à alternativa hide()e aos show()métodos de FragmentTransactionquando você usa animações. Você acabou de chamá-lo de onTransitionStart()e onTransitionEnd()de Transition.TransitionListener.

Allan Veloso
fonte
1

Método 1:

Você pode adicionar a todo o layout de fragmentos

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

Método 2: (programaticamente)

Estenda todos os fragmentos de FragmentBaseetc. Em seguida, adicione este código 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);
}
MartePessoas
fonte
0

O que você pode fazer é clicar em branco no layout do fragmento anterior usando a propriedade onClick para o layout pai desse fragmento principal e na atividade você pode criar uma função doNothing(View view)e não escrever nada nele. Isso fará isso por você.

Satish Silveri
fonte
0

Isso soa como um caso para DialogFragment. Caso contrário, com o Fragment Manager, comprometa um para ocultar e o outro para mostrar. Isso funcionou para mim.

Juan Mendez
fonte
0

A adição de android:clickable="true"não funcionou para mim. Esta solução não funciona no CoordinatorLayout quando é um layout pai. Por isso, criei RelativeLayout como layout pai, adicionei android:clickable="true"e coloquei CoordinatorLayout nesse RelativeLayout.

Zhebzhik Babich
fonte
0

Eu tinha vários fragmentos com o mesmo xml.
Depois de passar horas, eu removi setPageTransformere ele começou a funcionar

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

Eu tinha uma lógica assustadora.

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;
    }
}
AskQ
fonte