Почему обработчик события не работает через include?

Нужна in-app клавиатура,чтобы спокойно мог из разных активити подключать. Код следующий:

Клавиатура, задаем кнопки и т.д.:

public class MyKeyboard extends ConstraintLayout implements View.OnClickListener {


    private Button button_q,
            button_w,
            button_e,
            button_r,
            button_t,
            button_y,
            button_u,
            button_i,
            button_o,
            button_p,
            button_a,
            button_s,
            button_d,
            button_f,
            button_g,
            button_h,
            button_j,
            button_k,
            button_l,
            button_z,
            button_x,
            button_c,
            button_v,
            button_b,
            button_n,
            button_m,
            buttonDelete, buttonEnter;

   
    private SparseArray<String> keyValues = new SparseArray<>();
    private InputConnection inputConnection;

    public MyKeyboard(Context context) {
        super(context);
        init();
    }


    private void init() {

        button_q = (Button) findViewById(R.id.button_q);
        button_q.setOnClickListener(this);

        button_w = (Button) findViewById(R.id.button_w);
        button_w.setOnClickListener(this);

        button_e = (Button) findViewById(R.id.button_e);
        button_e.setOnClickListener(this);

        button_r = (Button) findViewById(R.id.button_r);
        button_r.setOnClickListener(this);

        button_t = (Button) findViewById(R.id.button_t);
        button_t.setOnClickListener(this);

        button_y = (Button) findViewById(R.id.button_y);
        button_y.setOnClickListener(this);

        button_u = (Button) findViewById(R.id.button_u);
        button_u.setOnClickListener(this);

        button_i = (Button) findViewById(R.id.button_i);
        button_i.setOnClickListener(this);

        button_o = (Button) findViewById(R.id.button_o);
        button_o.setOnClickListener(this);

        button_p = (Button) findViewById(R.id.button_p);
        button_p.setOnClickListener(this);

        button_a = (Button) findViewById(R.id.button_a);
        button_a.setOnClickListener(this);

        button_s = (Button) findViewById(R.id.button_s);
        button_s.setOnClickListener(this);

        button_d = (Button) findViewById(R.id.button_d);
        button_d.setOnClickListener(this);

        button_f = (Button) findViewById(R.id.button_f);
        button_f.setOnClickListener(this);

        button_g = (Button) findViewById(R.id.button_g);
        button_g.setOnClickListener(this);

        button_h = (Button) findViewById(R.id.button_h);
        button_h.setOnClickListener(this);

        button_h = (Button) findViewById(R.id.button_h);
        button_h.setOnClickListener(this);

        button_j = (Button) findViewById(R.id.button_j);
        button_j.setOnClickListener(this);

        button_k = (Button) findViewById(R.id.button_k);
        button_k.setOnClickListener(this);

        button_l = (Button) findViewById(R.id.button_l);
        button_l.setOnClickListener(this);

        button_z = (Button) findViewById(R.id.button_z);
        button_z.setOnClickListener(this);

        button_x = (Button) findViewById(R.id.button_x);
        button_x.setOnClickListener(this);

        button_c = (Button) findViewById(R.id.button_c);
        button_c.setOnClickListener(this);

        button_v = (Button) findViewById(R.id.button_v);
        button_v.setOnClickListener(this);

        button_b = (Button) findViewById(R.id.button_b);
        button_b.setOnClickListener(this);

        button_n = (Button) findViewById(R.id.button_n);
        button_n.setOnClickListener(this);

        button_m = (Button) findViewById(R.id.button_m);
        button_m.setOnClickListener(this);

        buttonDelete = (Button) findViewById(R.id.button_delete);
        buttonDelete.setOnClickListener(this);


        buttonEnter = (Button) findViewById(R.id.button_space);
        buttonEnter.setOnClickListener(this);

        keyValues.put(R.id.button_q, "q");
        keyValues.put(R.id.button_w, "w");
        keyValues.put(R.id.button_e, "e");
        keyValues.put(R.id.button_r, "r");
        keyValues.put(R.id.button_t, "t");
        keyValues.put(R.id.button_y, "y");
        keyValues.put(R.id.button_u, "u");
        keyValues.put(R.id.button_i, "i");
        keyValues.put(R.id.button_o, "o");
        keyValues.put(R.id.button_p, "p");
        keyValues.put(R.id.button_a, "a");
        keyValues.put(R.id.button_s, "s");
        keyValues.put(R.id.button_d, "d");
        keyValues.put(R.id.button_f, "f");
        keyValues.put(R.id.button_g, "g");
        keyValues.put(R.id.button_h, "h");
        keyValues.put(R.id.button_j, "j");
        keyValues.put(R.id.button_k, "k");
        keyValues.put(R.id.button_l, "l");
        keyValues.put(R.id.button_z, "z");
        keyValues.put(R.id.button_x, "x");
        keyValues.put(R.id.button_c, "c");
        keyValues.put(R.id.button_v, "v");
        keyValues.put(R.id.button_b, "b");
        keyValues.put(R.id.button_n, "n");
        keyValues.put(R.id.button_m, "m");
        keyValues.put(R.id.button_space, " ");
    }


    @Override
    public void onClick(View view) {

        if (inputConnection == null)
            return;

        if (view.getId() == R.id.button_delete) {
            CharSequence selectedText = inputConnection.getSelectedText(0);

            if (TextUtils.isEmpty(selectedText)) {
                inputConnection.deleteSurroundingText(1, 0);
            } else {
                inputConnection.commitText("", 1);
            }
        } else {

            String value = keyValues.get(view.getId());
            inputConnection.commitText(value, 1);
        }
    }

    public void setInputConnection(InputConnection ic) {
        inputConnection = ic;
    }
}

Активити, где требуется клавиатура:

package com.tsvetkovopozdal.lalaeng;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.speech.tts.Voice;
import android.text.Editable;
import android.text.InputFilter;
import android.text.Spanned;
import android.text.TextWatcher;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.cardview.widget.CardView;

import android.text.InputType;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.EditText;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.widget.Toast;

import java.util.Locale;

public class WordConstructor extends AppCompatActivity {


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.word_constructor);

        Bundle argumets = getIntent().getExtras();
        final String name = argumets.get("text").toString();
        TextView a = findViewById(R.id.word_constructor_hello);
        a.setText(name);

        final EditText editText = (EditText) findViewById(R.id.editText);
        editText.setRawInputType(InputType.TYPE_CLASS_TEXT);
        editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(name.length())});       this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
        
        MyKeyboard keyboard = new MyKeyboard(getApplicationContext());
        
        InputConnection ic = editText.onCreateInputConnection(new EditorInfo());
        keyboard.setInputConnection(ic);
  
    }


}

Подключается через include

<include
    android:id="@+id/keyboard"
    layout="@layout/keyboard"/>

Суть проблемы: при вызове init() из конструктора класса MyKeyboard студия выдает следующую ошибку

Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference

Порывшись в сети, нашел посты, где у людей данная ошибка возникала при использовании findViewById до вызова методов onCreate() и setContentView(), это вроде бы не мой случай, так как создаю экземпляр класса после отрисовки view и в самом методе onCreate. Спустя половину дня поисков, зачем-то все таки заменил конструктор класса MyKeyboard на метод onCreate и вызвал init() из него, в итоге ошибка пропала, все запускается, но теперь в метод init() не заходит(дебажил toast'ом).

Вопрос: как в данном случае повесить слушаетели на кнопки клавиатуры, чтобы все работало через include? То есть чтобы не переносить логику клавиатуры, слушателей и т.д. в код активити.


Ответы (1 шт):

Автор решения: Lounah

Ты пытаешься обратиться к вьюшкам, которые лежат в .xml, но твоя вьюшка с клавиатурой не знает ничего о том самом .xml, поэтому тебе нужно ее заинфлейтить.

Для этого в начало init() добавь следующее:

LayoutInflater.from(getContext()).inflate(R.layout.твой_айди_разметки, this, true)

Кроме того, лейаут разметки желательно обернуть не в ConstraintLayout, а в merge, это избавит тебя от лишней прослойки из вьюх на этапе инфлейтинга.

И ещё -- использовать Constraint на вьюшке с клавиатурой, наверное, не лучшая идея, ибо получается слишком много связей, и measure/layout pass может занять довольно много времени.

→ Ссылка