I want to make a custom ConstraintLayout so I can trigger some functions from the activity where its initialized.
I have the following code:
First initialized my custom view in layout:
<package.com.app.Main.LavadasView
android:id="#+id/main_autolavados_lavadas_lavadas_view"
android:layout_width="0dp"
android:layout_height="25dp"
...
/>
This is my custom class LavadasView, constraint layout initialized from another XML file:
Java class
public class LavadasView extends ConstraintLayout {
public LavadasView(Context context,AttributeSet attrs) {
super(context);
//Inflate view from XML layout file
LayoutInflater inflater =(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.lavadas_view, this);
}
public void resetView(){
//Some ui updates
}
}
LavadasView xml file, just a normal constraint layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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.support.constraint.ConstraintLayout>
And in my Activity I get the instance with findViewByIdmehtod, then I want to call the resetView function and that gives me a null pointer exception related to LavadasView:
LavadasView lavadasView = (LavadasView) findViewById(R.id.main_autolavados_lavadas_lavadas_view);
//Call this method later on
lavadasView.resetView();
SO what am I doing wrong? I've looked up and thats the correct way to get layout instance isnt?
Thanks.
The problem is with the constructor of your LavadasView. When your Activity is inflated it will call the LavadasView(Context context, AttributeSet attrs) constructor on you custom layout. In this constructor you need to call the super(context, attrs) for the ConstraintLayout to be properly inflated, but you're only calling super(context) which is why you get the NullPointerException.
Related
I have faced an issue after creating custom view class in android. The issue is simply, when I create an object of the custom view, its constructor is executed multiple times.
Code of the main Activity Class
public class MainActivity2 extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
CustomView customView = new CustomView(this, null);
}
}
Code of the customView
package com.example.myapplication;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
public class CustomView extends View {
public CustomView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
System.out.println("Custom View is executed");
}
}
Code of main Activity's xml layout 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"
android:weightSum="4"
tools:context=".MainActivity2">
<com.example.myapplication.CustomView
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.example.myapplication.CustomView>
</androidx.constraintlayout.widget.ConstraintLayout>
The expected result is: the constructor of the view is executed one time
Actual Result: the constructor of the view is executed two times!
You can put a breakpoint in the constructor to examine the call stack and to learn where it is called exactly.
The first invocation comes from setContentView when the layout containing your custom view is inflated.
The second invocation is your explicit constructor call new CustomView(...) in your activity code.
I am still learning Java and how to use Eclipse but I have a XML main activity file (the graphical layout) and a Java file named Mainscreen that extends Activity.
Inside the Mainscreen file I have a Class named myView which extends View and I can get it to display on the device screen by calling setContentView(new MyView(this)); from the Mainscreen onCreate method. How do I make it so I can have the MyView be a view within the XML file?
I can also change it to setContentView(R.layout.activity_main_screen);and then it sets it to what the XML file is but how do I have it so both are displayed.
I want it to display the screen as if it was following the setContentView(R.layout.activity_main_screen); layout but then also have the MyView displayed as well inside a seperate View on the screen. I have tried setting it as setContentView(R.id.view1); but if I am being completely honest I am not 100% sure what I am doing as I am still learning.
Can someone please point me in the right direction or help me out? I have been Googling trying to figure this out and I'm a little lost.
Thanks
EDIT: added code below
XML
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainScreen" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:contentDescription="#string/hello_world"
android:src="#drawable/title_plate" />
<View
android:id="#+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/imageView1"
android:layout_centerHorizontal="true" />
</RelativeLayout>
The start of the MyView class
public class MyView extends View {
public MyView(Context context) {
super(context);
//setContentView(R.id.view1);
setContentView(R.layout.activity_main_screen);
The onCreate for the main file itself
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main_screen);
setContentView(new MyView(this));
//View circleView = (View)findViewById(R.id.view1);
//circleView = (new MyView(this));
//circleView = findViewById(R.id.view1);
}
I won't post all the other stuff the file contains as it would seem pretty pointless as it is just code to do what I want it to do.
Take this Class as your view class
package com.example.utils.views;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.Button;
public class FontButton extends Button {
public static Typeface FONT_NAME;
public FontButton(Context context) {
super(context);
if (FONT_NAME == null)
FONT_NAME = Typeface.createFromAsset(context.getAssets(),
"fonts/Signika-Regular.ttf");
this.setTypeface(FONT_NAME);
}
public FontButton(Context context, AttributeSet attrs) {
super(context, attrs);
if (FONT_NAME == null)
FONT_NAME = Typeface.createFromAsset(context.getAssets(),
"fonts/Signika-Regular.ttf");
this.setTypeface(FONT_NAME);
}
public FontButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (FONT_NAME == null)
FONT_NAME = Typeface.createFromAsset(context.getAssets(),
"fonts/Signika-Regular.ttf");
this.setTypeface(FONT_NAME);
}
}
**Now in the Xml file**
<com.example.utils.views.FontButton
android:id="#+id/register_user_code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="17sp"
android:background="#drawable/enter_pin"
android:onClick="#string/countryCodeClick"
android:text="#string/default_country_code" />
The XML view is a description of a class hierarchy. Each object in the XML represents a java class that inherits from View. The instances that contain other class inherit from a subclass, ViewGroup.
When you make a call like this: myActivity.setContentView(R.layout.activity_main_screen);
What you are in effect doing is instantiating all of the objects defined in the view and associating them with your Activity. You can use your custom View subclasses in the XML by supplying the full package name (e.g. com.myapp.MyViewSubclass) in the XML definition. Here is a concrete example:
<com.myapp.MyViewSubclass
android:id="#+id/myView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Now, within the java code, if you wanted to access this view, after calling myActivity.setContentView you would do something like this:
MyViewSubclass instance = (MyViewSubclass)myActivity.findViewById(R.id.myView1);
So R.java is a file that is created automatically by parsing all your XML files. R.id.myView1 is an integer that resolves to the item inside your XML file, and it will reference the object that you loaded once you load it using setContentView.
I've got a little piece of xml, which I'll be using in a lot of places in my app. For this reason I want to store it in a separate file. So I created mywidget.xml in which I have my xml. I then try to inflate this in mywidget.java, after which I want to include it in a different xml file like so:
<com.mycom.android.ui.widget.AmountWidget android:layout_width="fill_parent" android:layout_height="wrap_content"></com.mycom.android.ui.widget.AmountWidget>
In my java file, I try to inflate the initial xml like this:
public class AmountWidget extends LinearLayout {
public AmountWidget(Context context) {
super(context);
LinearLayout ll = (LinearLayout) findViewById(R.layout.amount_widget);
addView(ll);
}
}
But with the code above I get an error saying that there's an error inflating class com.mycom.android.ui.widget.AmountWiget.
My question: Does anybody know how I can inflate a layout so that I can use it as a class in another xml layout file?
The xml from the widget looks like this:
<?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="wrap_content"
android:layout_margin="10dp"
android:padding="10dp"
android:background="#layout/border"
>
<EditText
android:id="#+id/payment_amount_major"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="35sp"
android:textStyle="bold"
android:inputType="number"
android:digits="0,1,2,3,4,5,6,7,8,9"
android:maxLength="9"
android:gravity="right"
/>
</LinearLayout>
Try this:
// ViewGroup to add views
LinearLayout list = (LinearLayout) findViewById(R.id.linear_layout_list);
// Inflate you custom xml to view
// Ex: res/layout/item_data.xml
View item = getLayoutInflater().inflate(R.layout.item_data, null);
// Link item in list here
list.addView(item);
mContainerView is LinearLayout which contain your EditText and row is your xml filename.
The View class has an inflate method which wraps LayoutInflater.inflate. You should be able to use:
LinearLayout ll = (LinearLayout) inflate(context, R.layout.amount_widget, this);
to inflate your widget from xml. The call to addView() won't be needed, as inflate will add the newly inflated view for you!
Edit: Just a note, because this View is already a LinearLayout, there's no need to have the root of the xml you're inflating also be a LinearLayout. It can increase your performance if you inflate only the EditText and just add that to the parent, rather than nesting a second LinearLayout within the parent. You can set the LinearLayout attributes (such as background and padding) directly on the AmountWidget wherever it's added in xml. This shouldn't matter too much in this specific case, but may be good to know going forward if you have a situation with many nested views.
Edit2: The View class has three constructors: View(Context), View(Context, AttributeSet), and View(Context, AttributeSet, int). When the system inflates a view from xml, it will use one of the latter two. Any custom View will need to implement all three of these constructors. An easy way to do this while reusing your code is like this:
public AmountWidget(Context context) {
super(context);
LinearLayout ll = (LinearLayout) inflate(context, R.layout.amount_widget, this);
}
public AmountWidget(Context context, AttributeSet attrs) {
this(context);
}
public AmountWidget(Context context, AttributeSet attrs, int defStyle) {
this(context);
}
This will work if you don't care what the attributes or style arguments are, and just want the AmountWidget created the same any time it's inflated.
Easiest Solution
LinearLayout item = (LinearLayout )findViewById(R.id.item);//where you want to add/inflate a view as a child
View child = getLayoutInflater().inflate(R.layout.child, null);//child.xml
item.addView(child);
ImageView Imgitem = (ImageView ) child.findViewById(R.id.item_img);
Imgitem.setOnClick(new ...
I know there are dozens similar post, but it looks to me everything is correct here:
The custom widget:
public class DoubleTextItem extends LinearLayout {
private TextView txtMain;
private TextView txtDescription;
public DoubleTextItem(Context context) {
super(context);
}
public DoubleTextItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
((Activity)getContext()).getLayoutInflater().inflate(R.layout.widget_double_text_item, this);
setupViewItems();
}
private void setupViewItems() {
txtMain = (TextView) findViewById(R.id.txtMain);
txtDescription = (TextView) findViewById(R.id.txtDecription);
}
public void setDescription(String text) {
txtDescription.setText(text);
}
}
The custom widget layout xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/txtMain"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/txtDecription"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
ANd here inside an activity function i get a casting error,
LayoutInflater inflater = LayoutInflater.from(this);
DoubleTextItem item = (DoubleTextItem) inflater.inflate(R.layout.widget_double_text_item, layout);
item.setText(som-txt);
item.setDescription("#"+athlete.getString("position"));
Here, the root View is a LinearLayout but you try to cast it your custom class:
DoubleTextItem item = (DoubleTextItem) inflater.inflate(R.layout.widget_double_text_item, layout);
The standard advice is:
All DoubleTextItems are LinearLayouts, but not all LinearLayouts are DoubleTextItems.
Meaning you cannot downcast objects from a LinearLayout to a DoubleTextItem, there are too many assumptions and Java won't let you do it.
If you want a DoubleTextItem in your layout you need to use:
<your.package.name.DoubleTextItem
... />
(Also, calling inflate inside onFinishInflate() seems a little silly especially since you don't save the inflated item... If you want to inflate a different layout, don't inflate the first one.)
Overall it looks like you are trying to recreate the now deprecated TwoLineListItem, perhaps you can learn some pointers from it's source code (or just use the TwoLineListItem.)
I've been reading a lot of similar questions to try and find the solution for this but with no luck.
MainActivity.java
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.game);
}
game.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<View
android:id="#+id/adView"
android:background="#drawable/ad"
android:layout_width="320dp"
android:layout_height="50dp"
/>
<my.package.MainGamePanel
android:id="#+id/gameView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/> </RelativeLayout>
MainGamePanel.java
public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback {
private MainThread thread;
public MainGamePanel(Context context, AttributeSet a) {
super(context, a);
View.inflate(context, R.layout.game, null);
onFinishInflate();
thread = new MainThread(getHolder(), this);
setFocusable(true);
thread.setRunning(true);
etc. etc.
And then outside the MainGamePanel constructor is the function:
#Override
protected void onFinishInflate() {
getHolder().addCallback(this);
}
There is also a MainThread.java file but I don't think that is the problem.
This is the runtime exception:
java.lang.RuntimeException: Unable to start activity ComponentInfo{my.package/my.package.MainActivity}: android.view.InflateException: Binary XML file line #13: Error inflating class my.package.MainGamePanel
If I change the setContentView in MainActivity to setContentView(new MainGamePanel(this)) and remove the AttributeSet parameter from the constructor, and delete the View.Inflate(context, R.layout.game, null); , then it works, but I want to figure out how to use the custom view in the xml file.
it looks like you are circularly inflating layouts. you setContentView on R.layout.game, which contains a MainGamePanel, which inflates R.layout.game, which contains a MainGamePanel, etc.
you should take the View.inflate line out of the onCreate method. It's not doing anything anyway, as far as I can see. you also shouldn't explicitly call onFinishInflate. That will be called automatically when the inflation of the MainGamePanel instance is actually finished.