binding = DataBindingUtil.setContentView(this, layoutId);
But the binding is null.
This sometimes has a value, other times null.
The activity:
public class WellcomeActivity extends Activity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WellcomeBinding binding = DataBindingUtil.setContentView(this, R.layout.wellcome);
vvv = (ViewFlipper) findViewById(R.id.main_vf);
}
}
vvv can be found; WellcomeBinding can be found; binding is null.
My English is not very good.
请多担待 Please pay more attention
XML file no error
It is an old project
Compile no error
it has a RuntimeException only.
The xml like this:
<?xml version="1.0" encoding="utf-8"?>
<layout>
<!--<data>-->
<!--<variable-->
<!--name="vm"-->
<!--type="com.aaron.lazy.base.activity.BaseViewModel"></variable>-->
<!--</data>-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/launcher_default">
<ViewFlipper
android:id="#+id/main_vf"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="向右滑动3次以上"
android:textColor="#color/white"
android:textSize="30dp" />
</RelativeLayout>
</layout>
Have you tried using:
WellcomeBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.wellcome, null, false);
setContentView(binding.getRoot());
This is how you do it:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
Double check that you have databinding enabled in your build.gradle
android {
buildFeatures {
dataBinding true
}
}
Especially in libraries this is easily forgotten. Remember that all modules that depend on a library using databinding needs to enable it (even if that module doesn't use databinding itself)!
Related
I'm trying to get viewBinding to work.
This is the code:
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view: ConstraintLayout = binding.root
setContentView(view)
}
I manually declared the type ConstraintLayout because it's what I'm using for that activity called AskProfileImage:
<?xml version="1.0" encoding="utf-8"?>
<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"
android:background="#303030"
tools:context=".AskProfileImage">
<ImageView
android:id="#+id/imageView97545"
android:layout_width="108dp"
android:layout_height="64dp"
android:layout_marginTop="8dp"
android:background="#00FFFFFF"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/logo2" />
But I'm getting the error
Type mismatch: inferred type is DrawerLayout but ConstraintLayout was expected
on
binding.root
how to fix this?
viewBinding creates a class for each activity. So in my case for this activity it's:
ActivityAskProfileImageBinding
So the correct code is:
private lateinit var binding: ActivityAskProfileImageBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAskProfileImageBinding.inflate(layoutInflater)
setContentView(binding.root)
}
Since viewBinding is probably broadly used now, I don't understand why the hell nobody could answer that and why google docs are as always useless
I've tried multiple solutions and read some other answers talking about the visibility of the class, but I've no added any private or protected modifier.
The point is, that I'm just have a very simple ViewBinding configuration, but it's showing the following message when I try to run it.
e: /app/src/main/java/com/adalpari/example/view/MainActivity.kt: (16,40): Cannot access '': it is private in 'ActivityMainBinding'
e: /app/src/main/java/com/adalpari/example/view/MainActivity.kt: (16,60): No value passed for parameter 'rootView'
e: /app/src/main/java/com/adalpari/example/view/MainActivity.kt: (16,60): No value passed for parameter 'progressBar'
e: /app/src/main/java/com/adalpari/example/view/MainActivity.kt: (16,60): No value passed for parameter 'storiesView'
The code, as you can see is pretty simple..
MainActivity:
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
val binding: ActivityMainBinding = ActivityMainBinding()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
onObserve()
}
...
}
xml file:
<?xml version="1.0" encoding="utf-8"?>
<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=".view.MainActivity"
>
<com.adalpari.storiesview.view.StoriesView
android:id="#+id/stories_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<ProgressBar
android:id="#+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="#+id/stories_view"
app:layout_constraintEnd_toEndOf="#+id/stories_view"
app:layout_constraintStart_toStartOf="#+id/stories_view"
app:layout_constraintTop_toTopOf="#+id/stories_view"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
And finally the enabling of ViewBinding in app's gradle:
apply plugin: 'kotlin-kapt'
android {
...
buildFeatures {
viewBinding true
}
...
}
Any help is very appreciated, thanks in advance!
You're not supposed to use the constructors of data binding classes (they're private, as your error message says). Use the method ActivityMainBinding.inflate() to create your binding object
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
This is shown in the official docs for ViewBinding
I would recommend using dataBinding which includs viewBinding and other powerful features you may need in the future.
1. Enable dataBinding in Build Gradle File:
apply plugin: 'kotlin-kapt'
android {
...
buildFeatures {
dataBinding true
}
...
}
2. Wrap the Whole Layout inside <layout> Tag:
<layout //key point
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="#+id/xxx"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
...
</LinearLayout>
</layout>
3. Setup Activity/Fragment:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding //may need rebuild project
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
//binding.xxx.xxx
}
}
For Fragment:
binding: FragmentFirstBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_first, container, false)
I am using ButterKnife to bind view element.
Here is my fragment layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<Button
android:id="#+id/ok_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/ok" />
</LinearLayout>
My fragment:
class MyFragment : Fragment() {
#BindView(R.id.ok_btn)
lateinit var okBtn: Button
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
okBtn.setOnClickListener { view ->
Log.d("mytag", "ok clicked!")
}
}
}
When I run my simple app, I get error:
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property okBtn has not been initialized
E/AndroidRuntime( 5435): at com.my.app.MyFragment.onViewCreated(MyFragment.kt:35)
Why I get this error? It sounds like the #BindView(R.id.ok_btn) does't initialize the button instance variable. What do I miss?
where is the line like?
Butterknife.bind(this, rootView)
not sure how it should look on Kotlin
and don't forget to add unbinder
you should use it with fragments according documentation
finally it will look something like...
Unbinder unbinder;
onViewCreated(){
unbinder = Butterknife.bind(this, rootView);
//here you can set your onClickListener
}
onDestroyView(){
unbinder.unbind()
}
adapt it for Kotlin and bravely use))
While working with view binding, I came across a couple of undocumented cases.
First: How do I get binding for included view layout parts? The main binding only sees items defined in the main layout.
Second: How do I get binding for merged layout parts. Again, the main binding only sees items in the main layout?
In case of:
Include with generic layout (not merge node), we need to assign ID to included part, this way in binding we will have access to included sub part
<include
android:id="#+id/your_id"
layout="#layout/some_layout" />
This way in your activity code:
private lateinit var exampleBinding: ActivityExampleBinding //activity_example.xml layout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
exampleBinding = ActivityExampleBinding.inflate(layoutInflater)
setContentView(exampleBinding.root)
//we will be able to access included layouts view like this
val includedView: View = exampleBinding.yourId.idOfIncludedView
//[...]
}
Include with merge block in external layout. We can't add ID to it because merge block is not a view.
Let's say we have such eternal merge layout (merge_layout.xm):
<?xml version="1.0" encoding="utf-8"?>
<merge 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"
tools:showIn="#layout/activity_example">
<TextView
android:id="#+id/some_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World" />
</merge>
To properly bind such merge layout we need to:
In your activity code:
private lateinit var exampleBinding: ActivityExampleBinding //activity_example.xml layout
private lateinit var mergeBinding: MergeLayoutBinding //merge_layout.xml layout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
exampleBinding = ActivityExampleBinding.inflate(layoutInflater)
//we need to bind the root layout with our binder for external layout
mergeBinding = MergeLayoutBinding.bind(exampleBinding.root)
setContentView(exampleBinding.root)
//we will be able to access included in merge layout views like this
val mergedView: View = mergeBinding.someView
//[...]
}
Regarding your first question, you can get a view binding for the included layout.
Here is a sample main_fragment.xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view_main"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" />
</LinearLayout>
And MainFragment.java can be like this:
public class MainFragment extends Fragment {
private MainFragmentBinding binding;
private ToolbarBinding toolbarBinding;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = MainFragmentBinding.inflate(inflater, container, false);
toolbarBinding = binding.toolbar;
return binding.getRoot();
}
#Override
public void onDestroy() {
super.onDestroy();
toolbarBinding = null;
binding = null;
}
}
Now you have two bindings: One corresponds to the main layout and the other corresponds to the included layout.
If you want to bind included layout then,
For Activity
YourMainLayoutBinding mainLayoutBinding = MainLayoutBinding.inflate(getLayoutInflater);
View view = mainLayoutBinding.getRoot();
YourIncludedLayoutBinding includedLayoutBinding = YourIncludedLayoutBinding.bind(View);
For Fragment
YourMainLayoutBinding mainLayoutBinding = MainLayoutBinding.inflate(inflater,container,false);
View view = mainLayoutBinding.getRoot();
YourIncludedLayoutBinding includedLayoutBinding = YourIncludedLayoutBinding.bind(View);
Make Sure if your main layout binding parent root is LinearLayout then,
includedLayoutBinding parent layout also be a linear layout
Suppose I included a layout in my activity_main.xml file as follows:
<include
android:id="#+id/ll_layout1"
layout="#layout/layout1"
android:visibility="gone" />
And suppose I wanted to change its visibility. I can do so as follows:
activityMainBinding.llLayout1.root.visibility = View.VISIBLE
In my case, I forgot to assign id to the include tag
Now, when you've assigned the id, you'll be able to get the binding object as,
YourMainLayoutBinding.YourIncludeTagIDLayoutBinding
Follow steps:-
private val binding : FragmentBinding
by viewBinding(FragmentBinding::bind)
make sure to do the following in "onViewCreated(view: View, savedInstanceState: Bundle?)"
val binding2 = binding.root.include_layout_id
e.g. val binding2 = binding.root.tool_bar_layout
Now access your include layout, views here. e.g.
binding2.textView.text = "your text"
Using the data binding library. Then wrap your XML layout with <layout> tag
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" />
...
</LinearLayout>
</layout>
toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/ivImage"
... />
<TextView
android:id="#+id/tvTitle"
... />
</LinearLayout>
MainActivity.kt
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
// Access include layout views
binding.toolbar.rootView.ivImage.setImageResource(R.drawable.ic_back_arrow)
binding.toolbar.rootView.tvTitle.text = getString(R.string.home)
...
}
In the include layout you must create a Container layout and put here the id.
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/example"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
I want to call onButtonclick(View v) using android binding in the layout xml file.
How to achieve button click using android binding in this case?
I followed as below but it didn't work. Any suggestions ? Thanks in Advance.
Layout1.xml
<data>
<variable
name="myFrag"
type="com.myapp.Fragment1" />
</data>
...
<Button
android:id="#+id/step_button"
style="#style/button_style"
android:onClick="#{myFrag :: onButtonclick}"/>
Fragment1.java
public class Fragment1 extends Fragment {
.....
public void onButtonclick(View v)
{
myStdent.setId("No ID");
}
.....
}
Layout1Binding myBinding = DataBindingUtil.inflate(inflater, R.layout.layout1.xml, container, false);
myBinding. setMyFrag(this);
Above lines of code has solved the problem. I have added these lines in onCreateView.
I think you are missing
http://prntscr.com/fm6yih (init variable for databinding object)
In your fragment layout xml, define a fragment variable. Then in your fragment class, map the xml variable to the fragment using binding.setVariable. Now you should be able to use all fragment variables and methods in xml.
In fragment layout xml:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.MyFragment">
<data>
<variable
name="frag"
type="com.example.MyFragment" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="#{() -> frag.yourMethod()}" />
</RelativeLayout>
</layout>
In fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val inflater = context?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val binding = DataBindingUtil.inflate<ViewDataBinding>(inflater, R.layout.fragment_my, myFragmentContainer, true)
binding.setVariable(BR.frag, this#MyFragment)
}
}