Search code examples
androidxmlkotlinlayoutmobile-development

Trying to Change src of an ImageButton in another layout folder other than activity_main


to sum up I am trying to change the src of ImageButtons inside my dialog_colors.xml file from MainActivity. However whatever I do I couldnt manage to change it. I tried the same code with ImageButtons inside my activity_main.xml but it doesnt work for buttons inside dialog_colors.xml file.

activity_main.xlm

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

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

dialog_colors.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">

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

    <ImageButton
        android:id="@+id/red"
        android:layout_width="40sp"
        android:layout_height="40sp"
        android:src="@drawable/pallet_normal"
        android:background="@color/Red"
        android:layout_margin="1sp"
        android:tag="redTag"
        android:clickable="true"
        android:onClick="selectColor"

        />...

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    colorDialog=findViewById<LinearLayout>(R.id.dialog_colors)
    firstRowColors=colorDialog.findViewById<LinearLayout>(R.id.ll_paint_colors)
    secondRowColors=colorDialog.findViewById<LinearLayout>(R.id.ll_paint_colors2)
    drawingView=findViewById<DrawingView>(R.id.drawingView)

    pressedColor=secondRowColors[0] as ImageButton
    pressedColor!!.setImageResource(R.drawable.pallet_pressed)

}...

I tried the same thing with TextViews etc too. It seems like I cannot change anything inside the dialog_colors.xml file.

Do not get confused about the firstRows, secondRows etc, there are many ImageButtons, it doesnt work for any of them.


Solution

  • I found a solution to define an attribute in the MainActivity.kt through activity_main.xml to content_main.xml (included layout). The key word here is DataBinding. The project is completely reproducible and I provide first Kotlin and at the very end the JAVA files.


    KOTLIN:

    To enable DataBinding you need to go to your build.gradle(Module) and add following code:

    //...
    dataBinding{
            enabled true
        }
    //...
    

    You define a container called DrawableContainer as a Kotlin class. There you define a Drawable called customDrawable.

    Thus DrawableContainer.kt:

    import android.graphics.drawable.Drawable
    
    data class DrawableContainer(val customDrawable: Drawable)
    

    Now we will define our MainActivity.kt which will bind our chosen Drawable and pass it through our Container (DrawableContainer).

    Our MainActivity.kt:

    import android.app.Activity
    import android.os.Bundle
    import androidx.databinding.DataBindingUtil
    import com.example.imagebuttonexperiment.databinding.ActivityMainBinding
    
    class MainActivity : Activity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
    
            val binding: ActivityMainBinding = DataBindingUtil.setContentView(
                this, R.layout.activity_main)
            binding.drawable = DrawableContainer(resources.getDrawable(R.drawable.my_image))
    
        }
    }
    

    The missing parts are our XML files. The code below shows our content_main.xml. It contains a variable (in <data>) we will define named drawable. The type guides to our DrawableContainer. So this is the first bridge between our Container and our layout we will <include. In the ImageButton you can see that as android:src we refer over our variable to our Drawable in our Container. That's why android:src="@{drawable.customDrawable}".

    Thus content_main.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <layout
        xmlns:android="http://schemas.android.com/apk/res/android">
        
        <data>
            <variable
                name="drawable"
                type="com.example.imagebuttonexperiment.DrawableContainer" />
        </data>
    
        <ImageButton
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:src="@{drawable.customDrawable}"/>
    </layout>
    

    Now it's important to build our second bridge. Yet, we have DrawableContainer -> content_main. This will be the bridge content_main -> MainActivity. Therefor we have our <data/> and variable defined again. As you can see in <include we bind:drawable the exact same variable which is in both XML files. The missing piece in our puzzle is the activity_main.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <layout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:bind="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        tools:context=".MainActivity">
    
        <data>
            <variable
                name="drawable"
                type="com.example.imagebuttonexperiment.DrawableContainer" />
        </data>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
            <include
                android:id="@+id/include_content"
                layout="@layout/content_main"
                bind:drawable = "@{drawable}"/>
        </LinearLayout>
    </layout>
    

    RESULT:

    Result

    The final result is an ImageButton in the MainActivity that was included by another Layout. But the Drawable was chosen from the MainActivity and passed a Container to be shown in the included Layout. Magic isn't it!? ;)


    JAVA:

    And I will keep my word and provide you the JAVA version also. The XML files and gradle will be the same, you only need to use MainActivity.java instead of MainActivity.kt, the same for DrawableContainer.java instead of DrawableContainer.kt.

    Here is the same in green, DrawableContainer.java:

    import android.graphics.drawable.Drawable;
    
    public class DrawableContainer {
    
        public final Drawable customDrawable;
    
        public DrawableContainer(Drawable customDrawable) {
            this.customDrawable = customDrawable;
        }
    }
    

    And of course our MainActivity.java:

    import androidx.appcompat.app.AppCompatActivity;
    import androidx.databinding.DataBindingUtil;
    
    import android.annotation.SuppressLint;
    import android.graphics.drawable.Drawable;
    import android.os.Bundle;
    
    import com.example.imagebuttonexperiment.databinding.ActivityMainBinding;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main);
            mainBinding.setDrawable(new DrawableContainer(getDrawable(R.drawable.my_image)));
        }
    }
    

    DOWNLOAD PROJECT:

    In addition I provide the project with both, JAVA and Kotlin classes in it. You just need to comment the content of the 2 classes grey you don't want. In example, if you want to use Kotlin, grey the content of .java files.

    Github Project


    Documentation & Tutorial: