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.
Related
I'm trying to figure out why an Android custom view which works when no background is set suddenly stops working when the background is set. It seems the background covers the items added to the view when it is set. I've simplified the view code to the bare minimum which reproduces the problem and to be able to post the code here. The custom view inherits from RelativeLayout and the code is as follow:
public class TestView extends RelativeLayout {
private LayoutInflater mInflater;
private ViewTreeObserver mViewTreeObserber;
private boolean mInitialized = false;
public TestView(Context ctx) {
super(ctx, null);
initialize(ctx, null, 0);
}
public TestView(Context ctx, AttributeSet attrs) {
super(ctx, attrs);
initialize(ctx, attrs, 0);
}
public TestView(Context ctx, AttributeSet attrs, int defStyle) {
super(ctx, attrs, defStyle);
initialize(ctx, attrs, defStyle);
}
private void initialize(Context ctx, AttributeSet attrs, int defStyle) {
mInflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mViewTreeObserber = getViewTreeObserver();
mViewTreeObserber.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (!mInitialized) {
mInitialized = true;
drawItem();
}
}
});
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawItem();
}
private void drawItem() {
if (!mInitialized) return;
removeAllViews();
View item = mInflater.inflate(R.layout.testview_item, null);
TextView txt = (TextView)item.findViewById(R.id.test_view_item);
txt.setText("Test View Text");
txt.setTextColor(Color.BLACK);
txt.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
addView(item);
}
}
The item layout is simple:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<TextView
android:id="#+id/test_view_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:ellipsize="end"
android:maxLines="1" />
</LinearLayout>
And the sample app simply declares two instances of the custom view in XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin">
<TextView android:text="With Background set"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.machado.felipe.TestView
android:background="#android:color/holo_blue_light"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.machado.felipe.TestView>
<TextView android:text="WithOUT Background set"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.machado.felipe.TestView
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.machado.felipe.TestView>
</LinearLayout>
The result looks like the picture bellow:
This is driving me crazy! I'm not used to write custom views in android and this is someone else's code which I'm trying to fix! I don't even know if this is the way it should be done, since I'm inflating views and adding them to the RelativeLayout I don't think I should be adding them in the onDraw, but since the complete code is doing more complex stuff, as laying out the items in multiple rows with wrapping, it is possibly a valid approach... But, anyway, I can't figure out how to fix this!
I customized some cardviews like this:
public class CustomCard extends CardView {
public CustomCard(Context context) {
this(context, null);
}
public CustomCard(Context context, AttributeSet attributeSet) {
this(context, attributeSet, 0);
}
public CustomCard(Context context, AttributeSet attributeSet, int defStyle) {
super(context, attributeSet, defStyle);
//R.layout.card_custom is the custom xml file
inflate(context, R.layout.card_custom, this);
}
}
Then I constructed and added them to ViewGroup like below:
CustomCard card = new CustomCard(this);
someLayout.addView(card);
The problem is I will see two layers of CardView border in the UI like below(it is apparent there was two layer of elevations at the border):
Anyone has an idea? Thanks
Edit:
One xml of the custom CardView:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
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="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="14dp"
android:layout_marginBottom="14dp">
<!--- Some Details --->
</RelativeLayout>
</android.support.v7.widget.CardView>
Some Layout I mentioned above:
<ScrollView 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:background="#color/background_gray">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!--- Details --->
</LinearLayout>
</ScrollView>
Looks like you are inflating a CardView inside your custom card view, which is causing this issue.
To solve this, change your layout/custom_card.xml to have RelativeLayout as the parent instead of CardView
I'm trying to add a ListView and a Custom View in my layout, but I always get this error:
"...Error inflating class com.example..."
Without the Custom View it works and only the ListView is shown.
Activity:
package com.example.training;
public class PracticeActivity extends ActionBarActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.practice_layout);
initListView(); //private function
}
//Other stuff
}
Custom View:
package com.example.training;
public class LinearChart extends View{
LinearChart(Context context, AttributeSet attrs){
super(context,attrs);
}
LinearChart(Context context){
super(context);
}
LinearChart(Context context, AttributeSet attrs, int defStyle){
super(context,attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(10);
canvas.drawLine(10, 10, 30, 30, paint);
}
}
XML-Layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="#+id/practice_listview"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_centerHorizontal="true" >
</ListView>
<view
android:id="#+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="#+id/practice_listview"
class="com.example.training.LinearChart" />
</RelativeLayout>
There are no public constructors on your class. Make sure you override the constructors. It should look like this instead:
public LinearChart(Context context, AttributeSet attrs){
super(context,attrs);
}
public LinearChart(Context context){
super(context);
}
public LinearChart(Context context, AttributeSet attrs, int defStyle){
super(context,attrs, defStyle);
}
Notice the addiction of public modifier.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="#+id/practice_listview"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_centerHorizontal="true" >
</ListView>
<com.example.training.LinearChart
android:id="#+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="#+id/practice_listview"
/>
</RelativeLayout>
Instead of <view in your xml file, you need the fully qualified name of the class (or use a new xml namespace). So, in this example, your xml might look like:
<com.example.training.LinearChart
android:id="#+id/view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
(and therefore remove the class: attribute in your current XML). This should then inflate correctly.
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" />
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
}