Ricardo
Ricardo

Reputation: 8291

findviewbyid - custom view with nested views

I have the following code and my problem is findviewbyid is returning null.

    <es.ric.firebase.chat.core.views.chatbox.ChatBox
        android:id="@+id/chatbox"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true">

        <LinearLayout
            android:id="@+id/ll_text"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <RelativeLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1">

                <EditText
                    android:id="@+id/messageEditText"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="5dp"
                    android:layout_gravity="center_vertical"
                    android:paddingLeft="15dp"
                    android:hint="Escribir mensaje"
                    android:background="@drawable/background_chat"/>

                <ImageButton
                    android:id="@+id/bt_upload_picture"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:src="@drawable/ic_insert_photo_black_24dp"
                    android:background="@android:color/transparent"
                    android:layout_alignParentEnd="true"
                    android:layout_centerInParent="true"
                    android:layout_marginRight="10dp"/>

            </RelativeLayout>




            <ImageButton
                android:id="@+id/sendButton"
                android:layout_width="36dp"
                android:layout_height="36dp"
                android:src="@drawable/ic_send_white_24dp"
                android:background="@drawable/background_circle"
                android:layout_gravity="bottom"/>

        </LinearLayout>

    </es.ric.firebase.chat.core.views.chatbox.ChatBox>

Java class.

public class ChatBox extends LinearLayout {

    enum State { MODE_MESSAGE, MODE_AUDIO, MODE_RECORDING }

    private EditText messageEditText;
    private ImageButton bt_upload_picture;
    private ImageButton sendButton;
    private WeakReference<ChatBoxListener> weak_listener;

    private State state;

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

    public ChatBox(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ChatBox(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public ChatBox(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }


    private void init(){

        messageEditText = findViewById(R.id.messageEditText);
        bt_upload_picture = findViewById(R.id.bt_upload_picture);
        sendButton = findViewById(R.id.sendButton);
    }
}

Upvotes: 0

Views: 666

Answers (3)

MrJM
MrJM

Reputation: 1214

It can't find the view because your view doesn't exist yet. At the point of your init function only the object is already created.

You will need to provide a globalLayoutListener which will notify you when your layout is ready. To do this implement ViewTreeObserver.OnGlobalLayoutListener

In your init function attach the listener

private void init(){
    this.getViewTreeObserver().addOnGlobalLayoutListener(this)
}

And finally call findViewById in onGlobalLayout

@Override
public void onGlobalLayout(){
    messageEditText = findViewById(R.id.messageEditText);
    bt_upload_picture = findViewById(R.id.bt_upload_picture);
    sendButton = findViewById(R.id.sendButton);
}

Upvotes: 0

laalto
laalto

Reputation: 152817

When your custom view constructor runs and you call init() the child views are not yet instantiated nor added to your ChatBox layout.

Having dependencies from viewgroup to specific children declared in the same layout is not a good idea anyway. You have unnecessary coupling between ChatBox and where it is used.

You could wait for inflation to finish but for dependency reasons I'd refactor it towards the following:

  1. Pull out the child views from the layout where you're using ChatBox and move them to a layout of their own.

  2. Make ChatBox inflate that layout and add the views to itself. (Use this as the second root view argument to inflate().)

  3. After that you can find the children inside your ChatBox.

Upvotes: 0

artkoenig
artkoenig

Reputation: 7257

You can access the subviews after the onFinishInflate method is called:

@Override
protected void onFinishInflate() {
  init();
}

Upvotes: 1

Related Questions