findViewById () retorna null para o componente personalizado no layout XML, não para outros componentes

91

Eu tenho um res/layout/main.xmlincluindo estes elementos e outros:

<some.package.MyCustomView android:id="@+id/foo" (some other params) />
<TextView android:id="@+id/boring" (some other params) />

No onCreate da minha atividade, eu faço o seguinte:

setContentView(R.layout.main);
TextView boring = (TextView) findViewById(R.id.boring);
// ...find other elements...
MyCustomView foo = (MyCustomView) findViewById(R.id.foo);
if (foo == null) { Log.d(TAG, "epic fail"); }

Os outros elementos são encontrados com sucesso, mas fooretorna nulo. MyCustomView tem um construtor MyCustomView(Context c, AttributeSet a)e um Log.d(...)no final desse construtor aparece com sucesso no logcat pouco antes da "falha épica".

Por que é foonulo?

Chris Boyle
fonte

Respostas:

182

Porque no construtor, eu tinha em super(context)vez de super(context, attrs).

Faz sentido, se você não passar os atributos, como o id, então a visualização não terá id e, portanto, não poderá ser encontrada usando esse id. :-)

Chris Boyle
fonte
1
É sempre bom ser capaz de responder às suas próprias perguntas :) Certifique-se de marcar a sua como a resposta aceita também.
MattC
De fato. Farei isso quando o SO permitir ("Você pode aceitar sua resposta em 2 dias.")
Chris Boyle
4
Além disso, vocês não deveriam gostar de (MyCustomView) foo = findViewById(R.id.foo);ser MyCustomView foo = (MyCustomView) findViewById(R.id.foo);?
Jeremy Logan
3
Tive o mesmo problema, no meu caso tinha esquecido o setContentView () .. XD
Tom Brito
Há um bom exemplo para fazer essas coisas em vogella.com: vogella.com/articles/AndroidCustomViews/article.html
Wolkenjaeger
27

Eu tenho o mesmo problema porque em minha visualização customizada eu substituí construtor, mas invoquei o parâmetro super contructor withot attrs. Isso é copiar e colar)

Minha versão anterior do construtor:

    public TabsAndFilterRelativeLayout(Context context, AttributeSet attrs) {
            super(context);
}

Agora eu tenho:

    public TabsAndFilterRelativeLayout(Context context, AttributeSet attrs) {
            super(context, attrs);}

E isso funciona!

Alexander Korolchuk
fonte
Mesmo problema aqui. Aliás, foi um erro de copiar e colar para mim também.
KurtCobain
A mesma coisa aconteceu comigo. A resposta mais correta no Stackoverflow. Quando adicionar AttributeSet atrai de volta, está tudo bem.
spikeyang
Acho que todos nós procuramos o mesmo tutorial com visualização personalizada;) esse erro me levou 0,5h de depuração inútil ...
KrwawyKefir
Isso funcionou para mim também! Eu lutei contra isso por algum tempo. Obrigado!
us_david
18

Eu tive o mesmo problema. Meu erro foi este: eu escrevi

        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View layout=inflater.inflate(R.layout.dlg_show_info, null);
        alertDlgShowInfo.setView(layout);
        TableRow trowDesc=(TableRow)findViewById(R.id.trowDesc);

e como usei um inflador para "carregar" a visualização de um arquivo XML, a última linha estava errada. Para resolvê-lo, tive que escrever:

TableRow trowDesc=(TableRow)layout.findViewById(R.id.trowDesc);

Escrevi minha solução, caso alguém tenha o mesmo problema.

Vincent
fonte
Cara, você é um gênio. Essa foi a única solução que funcionou para mim de todas as inúmeras que li no SO
IgorGanapolsky
Esse foi o problema para mim também. Uau, que pedaço de ... Teria levado dias para descobrir isso sozinha. Obrigado!
poshaughnessy
18

Parece que há vários motivos. Acabei de usar "Clean ..." no Eclipse para resolver um problema semelhante. (FindViewByID havia funcionado antes e por algum motivo começou a retornar nulo.)

medusa
fonte
1
aparentemente, o problema subjacente é que os IDs do R.java de alguma forma são corrompidos ou talvez não atualizados. Eu percebi isso não apenas com os IDs, mas também em outros casos, por exemplo, string errada exibida em um TextView etc. No entanto, não sei realmente por que isso acontece.
água
Isso estava me deixando triste por muito tempo - uma limpeza realmente consertou para mim.
Nicholas MT Elliott
1
Limpeza, de fato. Uau, isso é péssimo!
Tim Büthe,
11

Mesmo problema, mas solução diferente: eu não liguei

setContentView(R.layout.main)

ANTES de tentar encontrar a vista conforme declarada aqui

Daniel
fonte
Acho que esta é a solução se você estiver obtendo um elemento em outra visão em vez da visão relacionada atual.
StarCub
4

Se você tiver várias versões de layout (dependendo das densidades de tela, versões do SDK), certifique-se de que todas elas incluam o elemento que você está procurando.

MQ
fonte
2

No meu caso, findViewById estava retornando nulo porque minha visualização personalizada se parecia com isto no XML principal:

        <com.gerfmarquez.seekbar.VerticalSeekBar  
            android:id="@+id/verticalSeekBar"
            android:layout_width="wrap_content" 
            android:layout_height="fill_parent" 
            />

e descobri que quando adicionei as coisas do xmlns funcionou assim:

        <com.gerfmarquez.seekbar.VerticalSeekBar  
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/verticalSeekBar"
            android:layout_width="wrap_content" 
            android:layout_height="fill_parent" 
            />
Gerfmarquez
fonte
2

Certifique-se de que a setContentView(R.layout.main)instrução chame antes da findViewById(...)instrução;

Pedreiro
fonte
1

Para mim, o problema foi resolvido quando adicionei a pasta res ao Source in Java Build Path nas configurações do projeto.

Jiwon Park
fonte
1

Eu tive o mesmo problema há algum tempo, quando adicionei uma Visualização personalizada por meio do XML de layout e tentei anexar um retorno de chamada em outro lugar no aplicativo ...

Eu criei uma visualização personalizada e a adicionei ao meu "layout_main.xml"

public class MUIComponent extends SurfaceView implements SurfaceHolder.Callback {
    public MUIComponent (Context context, AttributeSet attrs ) {
        super ( context, attrs );
    }
    // ..
}

E na Activity principal, eu queria anexar alguns retornos de chamada e obter referências aos elementos da IU do XML.

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ...

        MUIInitializer muiInit = new MUIInitializer();
        muiInit.setupCallbacks(this);
        muiInit.intializeFields(this);
    }       
}

O initilizer não estava fazendo nada extravagante, mas as alterações que tentava fazer na visualização personalizada (MUIComponent) ou em outros elementos de interface do usuário não personalizados simplesmente não apareciam no aplicativo.

public class MUIInitializer {

    // ...

    public void setupCallbacks ( Activity mainAct ) {


        // This does NOT work properly
        // - The object instance returned is technically an instance of my "MUICompnent" view
        //   but it is a *different* instance than the instance created and shown in the UI screen
        // - Callbacks never get triggered, changes don't appear on UI, etc.
        MUIComponent badInst = (MUIComponent) mainAct.findViewById(R.id.MUIComponent_TESTSURF);


        // ...
        // This works properly

        LayoutInflater inflater = (LayoutInflater) mainAct.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View inflatedLayout = inflater.inflate ( R.layout.activity_main, null );

        MUIComponent goodInst = (MUIComponent) inflatedLayout.findViewById(R.id.MUIComponent_TESTSURF);


        // Add callbacks
        // ...
    }

}

A diferença entre "badInst" e "goodInst" é:

  • badInst usa o findViewByID da Activity
  • goodInst infla o layout e usa o layout inflado para fazer a pesquisa
DevByStarlight
fonte
Notou que Vincent tem a mesma solução ... e sua resposta é mais curta ... +1 em vez dele :)
DevByStarlight
1

Isso aconteceu comigo com um componente personalizado para o Wear, mas é um conselho genérico. Se você estiver usando um Stub (como o que eu estava usando WatchViewStub), não poderá simplesmente fazer a chamada para findViewById()qualquer lugar. Tudo dentro do stub deve ser inflado primeiro, o que não acontece depois setContentView(). Portanto, você deve escrever algo assim para esperar que isso aconteça:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_wear);
    final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
    stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
        @Override
        public void onLayoutInflated(WatchViewStub stub) {
            myCustomViewInst = (MyCustomView) findViewById(R.id.my_custom_view);
            ...
Stephen Wylie
fonte
0

Meu problema foi um erro de digitação. Eu tinha escrito android.id(ponto) em vez de android:id. : P

Aparentemente, não há verificação de sintaxe em meu xml de componente personalizado. :(

JOG
fonte
0

Tive o mesmo problema.

Tive layout com poucos filhos. Do construtor de um deles, eu estava tentando obter referência (usando context.findViewById) para outra criança. Não estava funcionando porque o segundo filho foi definido posteriormente no layout.

Eu resolvi assim:

setContentView(R.layout.main);
MyView first = findViewById(R.layout.first_child);
first.setSecondView(findViewById(R.layout.second_child));

Funcionaria bem se a ordem dos filhos fosse oposta, mas acho que geralmente deve ser feito como acima.

Kangur
fonte
1
Em geral, você não deve usar findViewByIdno construtor de a View, mas sim colocar o código de inicialização em OnFinishInflate?
Sanjay Manohar
0

O findViewById()método às vezes retorna nullquando a raiz do layout não tem android:idatributo. O assistente do Eclipse para gerar arquivo xml de layout não gera android:idatributos automaticamente para o elemento raiz.

Santosh
fonte
0

No meu caso, a visão estava no pai, NÃO na visão que eu estava tentando chamar. Então, na visão filho, eu tive que chamar:

RelativeLayout relLayout = (RelativeLayout) this.getParent();
View view1 = relLayout.findViewById(R.id.id_relative_layout_search);
Mike6679
fonte
0

A opção 'limpar' funcionou para mim.

No meu caso, a causa raiz é que o código-fonte reside em um compartilhamento de rede e minha estação de trabalho e servidor de arquivos não foram sincronizados corretamente e sofreram uma variação de 5 segundos. Os registros de data e hora nos arquivos criados pelo Eclipse estão no passado (porque são atribuídos pelo servidor de arquivos) em relação ao relógio da estação de trabalho, fazendo com que o Eclipse resolva as dependências entre os arquivos gerados e de origem incorretamente. Nesse caso, uma 'limpeza' parece funcionar, porque força uma reconstrução completa em vez de uma construção incremental que depende de carimbos de data / hora errados.

Depois de corrigir as configurações de NTP em minha estação de trabalho, o problema nunca mais ocorreu. Sem as configurações de NTP adequadas, isso aconteceria a cada poucas horas, já que os relógios mudam rapidamente.

diter
fonte
Deveria adicionar isto ao comentário da resposta acima
Trung Nguyen
0

Para adicionar outro erro trivial às respostas a serem observadas:

Verifique se você está realmente editando o arquivo XML de layout correto ...

blub
fonte
0

Eu tive o mesmo problema porque esqueci de atualizar o id de visualização em todas as minhas pastas de layout.

Shanij PS
fonte