In my main layout xml file I have this:
<view class="com.mysite.MainActivity$MySeekBar"
android:id="#+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
And in MainActivity.java inside the MainActivity class I have this:
public static class MySeekBar extends SeekBar {
public MySeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
}
In the above case, nothing displays. However it does work if instead of the first snippet I have this:
<SeekBar
android:id="#+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
I have also tried using uppercase 'View' instead of 'view' in the xml.
What am I doing wrong?
Edit: I have tried following this google developer page on custom components which at the bottom gives a simple example of implementing with an inner class.
put this class in its own file
<com.mypackagename.MySeekBar <== this is missing
android:id="#+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Related
What I am trying to achieve (which I do not know if it is even doable, since I'm new to android development) is to have:
A MainActivity class utilize/reference and XML layout (this i know is doable)
Within the MainActivity class also call a custom Layout programmatically (I am uncertain how to do this)
To elaborate on my question
I have the following XML layout which is compromised of two items:
A dropdown menu
A RelativeLayout view (*** this is where I would like to link to my custom class to)
Here is the XML code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/weekDropdown"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:hint="#string/select_week"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:labelFor="#+id/weekSelected">
<AutoCompleteTextView
android:id="#+id/weekSelected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
android:layout_weight="1"
android:padding="14dp"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
<RelativeLayout <-- Here is where I would like to link my custom class
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
</RelativeLayout>
</LinearLayout>
In my custom class, I build my layout/view programmatically:
public class MainActivityLayout extends RelativeLayout {
Context context;
TableLayout tableA;
TableLayout tableB;
TableLayout tableC;
TableLayout tableD;
public MainActivityLayout(Context context) {
super(context);
this.initComponents();
this.setComponentsId();
etc...
}
}
Which I call it from my MainActivity class like so:
setContentView(new MainActivityLayout(this));
So is it possible to link the RelativeLayout in the XML to my custom class? If so can you let me know how to do so?
If you want to use your custom view, the easiest way is to include it in the xml. So this needs to be changed:
<RelativeLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
</RelativeLayout>
into this
<myapp.mypackage.MainActivityLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
</myapp.mypackage.MainActivityLayout>
Change myapp.mypackage based on the real path of MainActitvityLayout.
Then just use it like any other usual layouts.
setContentView(R.layout.main_activity);
Also you must override these constructors:
public MainActivityLayout(Context context)
{
super(context);
}
public MainActivityLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public MainActivityLayout(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
public MainActivityLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
super(context, attrs, defStyleAttr, defStyleRes);
}
However, if you insist on using it like this:
setContentView(new MainActivityLayout(this));
You can not use the main xml layout at all, but you can still inflate xml files onto your custom view. I won't write them here, you can easily search on this site on how implementing them.
I have a java databinding layout that I'm using inside another java databinding layout. The subcomponent renders correctly when viewed alone, but when I look in the design view for the supercomponent it does not render the subcomponent, and has null reference error in the design view errors panel under "Failed to instantiate one or more classes". It compiles and runs as expected. Looking inside the autogenerated BindingImpl file, the reference for any views that use databinding are not being generated, hence the reference error. How can I get the subcomponent to instantiate correctly?
If I remove all of the databinding from the xml and put into code, the component displays correctly in the design view (but defeats half the purpose of using it). Project compiles correctly with no errors, although design view says there are, in addition to the "Failed to instantiate..." error
[Autogenerated] ComponentNumberPickerBindingImpl.java
static {
sIncludes = null;
sViewsWithIds = new android.util.SparseIntArray();
sViewsWithIds.put(R.id.number_picker_minus_button, 2);
sViewsWithIds.put(R.id.number_picker_plus_button, 3);
}
private ComponentNumberPickerBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 1
, (android.widget.ImageButton) bindings[2]
, (android.widget.ImageButton) bindings[3]
, (android.widget.TextView) bindings[1]
);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.numberPickerText.setTag(null); // java.lang.NullPointerException error here
setRootTag(root);
// listeners
invalidateAll();
}
component_number_picker.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="numberPicker"
type="shared.NumberPicker" />
<variable
name="target"
type="androidx.lifecycle.MutableLiveData<Integer>" />
</data>
<LinearLayout 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"
android:orientation="horizontal">
<ImageButton
android:id="#+id/number_picker_minus_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#drawable/border_circular"
android:contentDescription="#string/desc_remove_large_luggage_button"
android:src="#drawable/ic_remove_24px" />
<TextView
android:id="#+id/number_picker_text"
style="#style/TextAppearance.AppCompat.Large"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:minWidth="60dp"
android:textAlignment="center"
tools:text="9"
android:text="#{`` + target}"/>
<ImageButton
android:id="#+id/number_picker_plus_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#drawable/border_circular"
android:contentDescription="#string/desc_add_large_luggage_button"
android:src="#drawable/ic_add_24px" />
</LinearLayout>
</layout>
NumberPicker.java
package shared;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import androidx.lifecycle.MutableLiveData;
import databinding.ComponentNumberPickerBinding;
public class NumberPicker extends LinearLayout {
ComponentNumberPickerBinding mBinding;
public NumberPicker(Context context) {
super(context);
initialiseViews(context);
}
public NumberPicker(Context context, AttributeSet attrs) {
super(context, attrs);
initialiseViews(context);
}
public NumberPicker(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialiseViews(context);
}
private void initialiseViews(Context context) {
mBinding = ComponentNumberPickerBinding.inflate(LayoutInflater.from(context), this, true);
mBinding.setNumberPicker(this);
}
public MutableLiveData<Integer> getTarget() {
return mBinding.getTarget();
}
public void setTarget(MutableLiveData<Integer> value) {
mBinding.setTarget(value);
}
}
Include
<shared.NumberPicker
android:id="#+id/luggage_specifier_number_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
So, I believe that this is a bug in the data binding code when used in the design view. I traced the app code while it was running, and noticed that it was assigning a tag (I couldn't see where this happens, presumably somewhere in the DataBinding setup) to each of the views in the subcomponent, which are used by the ViewDataBinding class to assign to the view properties in the binding implementation.
However, these tags do not appear to get set when using the design view, so the views never get added to the view bindings array, and thus never get assigned to the view properties.
I found a workaround for this, which is to manually assign the tags (these are e.g. binding_1, binding_2 etc.). When the code builds, it finds the tags and assigns them correctly, and the view renders as expected.
UPDATE
After a bit more digging, it seems like the binding tag is in there, but it doesn't get picked up for some reason - these two layouts are from the auto generated component_number_picker.xml
Note that the TextView has an "originalTag" attribute, where as the ImageView does not. In this example, I added a tag in the layout xml to the TextView, but not the ImageView. When viewed in design view, the TextView instantiates correctly, but the ImageView causes a NullReferenceError.
<Target id="#+id/number_picker_text" originalTag="binding_2" tag="binding_2"
view="TextView">
<Expressions>
<Expression attribute="android:text" text="`` + target">
<Location endLine="50" endOffset="40" startLine="50" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="50" endOffset="38" startLine="50" startOffset="28" />
</Expression>
</Expressions>
<location endLine="51" endOffset="36" startLine="40" startOffset="8" />
</Target>
<Target id="#+id/number_picker_plus_button" tag="binding_3" view="ImageButton">
<Expressions>
<Expression attribute="android:onClick" text="()->numberPicker.increment(1)">
<Location endLine="61" endOffset="61" startLine="61" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="61" endOffset="59" startLine="61" startOffset="31" />
</Expression>
</Expressions>
<location endLine="61" endOffset="63" startLine="53" startOffset="8" />
</Target>
I've created a layout for a button format I use multiple times. The button format has a TextView and an ImageView. With the way I'm including this layout in my main activity, I don't think I'm able to change the text of the inner TextView dynamically in Java or in the XML. Is there a different way I can do this such that I can set the text of the inner TextView?
Layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/settingslayout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:layout_marginTop="13dp"
android:textSize="20dp"
android:text="CHANGE ME"
android:id="#+id/text" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginRight="25dp"
android:layout_alignParentRight="true"
android:src="#drawable/left"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:maxHeight="30dp"
android:maxWidth="30dp"/>
</RelativeLayout>
Main Activity:
...
<include layout="#layout/settings_layout"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_below="#+id/accountStaticUnderline"
android:id="#+id/termBegin"
android:text="TEST" /> //DOESNT WORK
...
</RelativeLayout>
Setting the text that way won't work because android:text is not applicable to the <include> tag. More specifically, it's not applicable to the thing being included, which is a RelativeLayout. (You couldn't put the android:text on the RelativeLayout and have it apply to the TextView, nor would you expect that to work.)
My first suggestion (and the easiest immediate solution) is to use TextView's built in support for compound drawables so you can simply use TextViews instead of includes and have a style resource for the attributes you want.
If that's not good enough for your use case, then you might need to make a custom View. This view will replace the RelativeLayout and have the TextView and ImageView as children. The main thing to decide is where and how the children are created and their references are obtained: you can create them manually in Java when the parent is being constructed; or you can use some combination of layouts with <include>s and/or <merge>s. Making the text attribute work then requires some use of a <declare-styleable>.
I assume you want the children to always appear the same and that you want to reuse the layout you already made (i.e. you don't want to set all the attributes manually), so this is what I would probably do:
public class MyButton extends RelativeLayout {
private TextView mTextView;
private ImageView mImageView;
public MyButton(Context context) {
super(context);
init(context, null);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attributeSet) {
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.my_button, this, true); // used with merge tag
mTextView = (TextView) findViewById(R.id.text);
mImageView = (ImageView) findViewById(R.id.image);
int[] attrs = {android.R.attr.text};
TypedArray a = context.obtainStyleAttributes(attributeSet, attrs);
String text = a.getString(0, null);
mTextView.setText(text);
a.recycle();
}
}
In res/layout/my_button.xml:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:layout_marginTop="13dp"
android:textSize="20dp"
android:text="CHANGE ME"
android:id="#+id/text" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginRight="25dp"
android:layout_alignParentRight="true"
android:src="#drawable/left"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:maxHeight="30dp"
android:maxWidth="30dp"/>
</merge>
In your activity layout:
...
<!-- no more include -->
<com.package.MyButton
android:id="#+id/something"
android:layout_width="..."
android:layout_height="..."
android:text="..." />
<!-- you can have multiple instances with different IDs -->
<com.package.MyButton
android:id="#+id/something_else"
android:layout_width="..."
android:layout_height="..."
android:text="..." />
...
You could also use your own <declare-styleable> for the custom view, which will be necessary if later you want to have custom XML attributes for it, but the approach above should be sufficient.
sure, just type this code in your activity
((TextView)findViewById(R.id.text)).setText("enter_text_here");
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.
In my app I have a very basic looking compass which is rendered within my activity through a class. I am trying to display the compass with a layout. So rather than having just a circle with a line pointing north, I can include text box and buttons. How do I render this within a layout? Currently my activity sets the content view like so:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
compassView = new CompassView(this);
setContentView(compassView);
I have tried setContentView(R.layout.activity_display_compass) which is my xml file however it only display "hello world" (the TextView), not the compass. See my xml file below.
xml
<LinearLayout 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:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"/>
<View
class = "com.example.gpsfinder.CompassView"
android:id="#+id/compassView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Any guidance would be greatly appreciated.
To use a custom View subclass in your xml layout file:
<LinearLayout 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:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"/>
<com.example.gpsfinder.CompassView
android:id="#+id/compassView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
And then you probably need to add some of these constructors in your Java code:
// you used this ctor when creating the view programmatically
public CompassView(Context context) {
this(context, null);
// add additional initialization here
}
// this constructor is needed for the class to be used in XML layout files!
public CompassView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
// add additional initialization here
}
// this constructor is needed for the class to be used in XML layout files,
// with a class-specific base style
public CompassView(Context context, AttributeSet attributeSet, int defStyle) {
super(context, attributeSet, defStyle);
// add additional initialization here
}