How to define your own widget using both XML and java code

I want to define my own widget that contains some other controls and has its own logic. I want to use an XML file to define the user interface and Java code to define the logic. But I do not know how to do this.

I followed this article Combining custom Android components / widgets using Java class and XML layout , but not working. Below is my code.

XML:

<com.example.MyView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="custom button 1"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="custom button 2"/> </com.example.MyView> 

Java Code:

 public class MyView extends LinearLayout { public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); } public MyView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onFinishInflate() { super.onFinishInflate(); LayoutInflater.from(getContext()).inflate(R.layout.test_view, this); // **line 32** } } 

And in the main layout, I call it like this:

 <com.example.MyView android:layout_width="wrap_content" android:layout_height="wrap_content"> </com.example.MyView> 

Unfortunately, on startup, it throws an exception:

 05-26 14:11:25.199: ERROR/AndroidRuntime(15799): FATAL EXCEPTION: main java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.MyActivity}: android.view.InflateException: Binary XML file line #7: Error inflating class <unknown> at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1651) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1667) at android.app.ActivityThread.access$1500(ActivityThread.java:117) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:130) at android.app.ActivityThread.main(ActivityThread.java:3687) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:507) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625) at dalvik.system.NativeStart.main(Native Method) Caused by: android.view.InflateException: Binary XML file line #7: Error inflating class <unknown> at android.view.LayoutInflater.createView(LayoutInflater.java:518) at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:568) at android.view.LayoutInflater.rInflate(LayoutInflater.java:623) at android.view.LayoutInflater.inflate(LayoutInflater.java:408) at android.view.LayoutInflater.inflate(LayoutInflater.java:320) at android.view.LayoutInflater.inflate(LayoutInflater.java:276) at com.example.MyView.onFinishInflate(MyView.java:32) at android.view.LayoutInflater.rInflate(LayoutInflater.java:631) at android.view.LayoutInflater.inflate(LayoutInflater.java:408) at android.view.LayoutInflater.inflate(LayoutInflater.java:320) at android.view.LayoutInflater.inflate(LayoutInflater.java:276) at com.example.MyView.onFinishInflate(MyView.java:32) at android.view.LayoutInflater.rInflate(LayoutInflater.java:631) at android.view.LayoutInflater.inflate(LayoutInflater.java:408) at android.view.LayoutInflater.inflate(LayoutInflater.java:320) at android.view.LayoutInflater.inflate(LayoutInflater.java:276) at com.example.MyView.onFinishInflate(MyView.java:32) at android.view.LayoutInflater.rInflate(LayoutInflater.java:631) at android.view.LayoutInflater.inflate(LayoutInflater.java:408) at android.view.LayoutInflater.inflate(LayoutInflater.java:320) at android.view.LayoutInflater.inflate(LayoutInflater.java:276) at com.example.MyView.onFinishInflate(MyView.java:32) at android.view.LayoutInflater.rInflate(LayoutInflater.java:631) at android.view.LayoutInflater.inflate(LayoutInflater.java:408) at android.view.LayoutInflater.inflate(LayoutInflater.java:320) at android.view.LayoutInflater.inflate(LayoutInflater.java:276) .... 
+4
source share
1 answer

You cannot declare a custom view like you. If you use your regular view in the main layout, the onFinishInflate method will be called to create a custom view, and the layout above will be called in this method. The problem is that you already have a custom view link in this layout (which will be bloated again when LayoutInflater encounters a custom view tag) so that you get into a circular bloat situation.

You probably want something like this:

 <merge xmlns:android="http://schemas.android.com/apk/res/android> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="custom button 1"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="custom button 2"/> </merge> 

so when your custom view is inflated from the layout file, it will add the contents of the xml layout above. The onFinishInflate method will remain the same:

 @Override protected void onFinishInflate() { super.onFinishInflate(); LayoutInflater.from(getContext()).inflate(R.layout.test_view, this); // **line 32** } 

Change It seems that inflating the content layout in the onFinishInflate method calls the method again (at least in older versions). You can either change the layout to use LinearLayout as follows:

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="custom button 1"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="custom button 2"/> </LinearLayout> 

and then in the onFinishInflate method:

 @Override protected void onFinishInflate() { super.onFinishInflate(); View v = LayoutInflater.from(getContext()).inflate(R.layout.aaaaaaaaaaa, this, false); addView(v); } 

This will cause another useless item to appear in the layout hierarchy, so you can simply inflate the layout with the merge tag in the custom view constructors:

 public MyView(Context context) { super(context); LayoutInflater.from(getContext()).inflate(R.layout.test_view, this); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater.from(getContext()).inflate(R.layout.test_view, this); } public MyView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); LayoutInflater.from(getContext()).inflate(R.layout.test_view, this); } 
+4
source

Source: https://habr.com/ru/post/1439620/


All Articles