Use XML Layout as view for View Subclass? - android

I feel as though I used to know how to do this, but I'm currently drawing a blank. I have a class that extends from View (Card) and I wrote a layout for it in XML. What I want to do is set the View of Card to be the XML View in the constructor, so I can use the methods in Card to set TextViews and whatnot. Any suggestions? Code below:
Card.java:
(I have View.inflate(context, R.layout.card_layout, null); there as an example of what I want to do, but it's not working. I basically want the class to be the interface for the View, and in order to do that I need to somehow assign the XML layout to the View. Do I use something along the lines of setContentView(View view)? There is no such method in the View class, but is there something like it?)
public class Card extends View {
TextView tv;
public Card(Context context) {
super(context);
View.inflate(context, R.layout.card_layout, null);
tv = (TextView) findViewById(R.id.tv);
}
public Card(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
View.inflate(context, R.layout.card_layout, null);
tv = (TextView) findViewById(R.id.tv);
}
public Card(Context context, AttributeSet attrs) {
super(context, attrs);
View.inflate(context, R.layout.card_layout, null);
tv = (TextView) findViewById(R.id.tv);
}
public void setText(String text) {
tv.setText(text);
}
}
card_layout.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="336dp"
android:layout_height="280dp"
android:layout_gravity="center"
android:background="#drawable/card_bg"
android:orientation="vertical" >
<TextView
android:id="#+id/tv"
android:layout_height="fill_parent"
android:layout_width="wrap_content"
android:textSize="24dp"
/>
</LinearLayout>

What you're trying to do is not possible with the current setup. A View(or a direct subclass of it) represent a single view it doesn't have the notion of child views, what you're trying to do. The LayoutInflater can't be used with a simple View because the simple View class doesn't have the methods to actually add the child to it(like the addView() method).
On the other hand, the correct class to use to be able to have children is the ViewGroup(or one of the direct subclasses like LinearLayout, FrameLayout etc) which accept adding Views or other ViewGroups to it, by supplying the addView methods. In the end your class should be:
public class Card extends ViewGroup {
TextView tv;
public Card(Context context) {
super(context);
View.inflate(context, R.layout.card_layout, this);
tv = (TextView) findViewById(R.id.tv);
}
public Card(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
View.inflate(context, R.layout.card_layout, this);
tv = (TextView) findViewById(R.id.tv);
}
public Card(Context context, AttributeSet attrs) {
super(context, attrs);
View.inflate(context, R.layout.card_layout, this);
tv = (TextView) findViewById(R.id.tv);
}
public void setText(String text) {
tv.setText(text);
}
}
If I recall you have to override onLayout if you extend ViewGroup, so instead(and because of your layout file), you should look at extending LinearLayout and replace the LinearLayout from the xml layout with a merge tag.

You should be able to use your class name in the layout. Something like:
<your.package.name.Card
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="336dp"
android:layout_height="280dp"
...
Just call findViewById to get child views.
To use your Card class, you use the inflator to get an instance, then attach it to a parent view.
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
Card card = (Card) inflater.inflate(R.layout.card_layout, null);
parentView.addView(card);
Have a look at the docs for more info.

Related

Custom Relative layout not rendering any children

Intro:
I am attempting to add various Views to my custom RelativeLayout, i.e. Buttons, ImageViews, etc however none of them render/show.
Documentation:
As shown on numerous SO questions: here, here, here, here and many more,
I have the standard requirements for extending a layout, i.e. the 3 constructors, that being:
public RelativeLayout(Context context) {}
public RelativeLayout(Context context, AttributeSet attrs) {}
public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr){}
referred to here on Android Developer site.
Implementation:
My RelativeLayout named DiceRoller has the following implementation:
public class DiceRoller extends RelativeLayout {
private DieContainer dieContainer;
private Context mContext;
private int speed;
private Runnable moveDies;
private Handler handler;
private Timer timer;
public DiceRoller(Context context) {
super(context);
mContext = context;
init();
}
public DiceRoller(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
}
public DiceRoller(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
private void init() {
//source : https://stackoverflow.com/questions/28265286/custom-relative-layout-not-showing-child-views
setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
setGravity(Gravity.CENTER);
ImageView mainImage = new ImageView(mContext);
mainImage.setId(1994);
LayoutParams params = new LayoutParams(100, 100);
mainImage.setImageResource(R.drawable.die1);
mainImage.setLayoutParams(params);
addView(mainImage);
RelativeLayout.LayoutParams crossParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
crossParams.addRule(RelativeLayout.ALIGN_TOP | RelativeLayout.ALIGN_LEFT, mainImage.getId());
ImageView crossImage = new ImageView(mContext);
crossImage.setImageResource(R.drawable.die6);
crossImage.setLayoutParams(crossParams);
addView(crossImage);
TextView tv = new TextView(mContext);
tv.setText("hello world");
addView(tv);
}
}
Please Note: the contents of the init() method was purely to test if views were infact rendered. This was my last attempt at debugging the issue, previously I added views from my MainActivity aswell, obviously without success
With an associated layout file
<?xml version="1.0" encoding="utf-8"?>
<com.myapp.DiceRoller
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="1000px"
android:layout_height="1000px"
android:background="#color/colorAccent"
android:id="#+id/rollerBack">
</com.myapp.DiceRoller>
What is the problem:
The problem is simple. No child of the layout is rendered/shown/visible.
I attempted adding a child in my MainActivity, programmatically. It did not render. I attempted adding a child within this RelativeLayout class, it did not render.
Additional Info:
note: When adding views, I always added text or some image, I also set the X, Y values, also included RelativeLayout.LayoutParams() with the wrap option set.
When debugging this issue, if I added a view (ImageView, Button, etc), the layout has each child stored, and each child's parent is this RelativeLayout. Each child has a width, height, X, Y value and some content (either an image or text), thus the problem does not lie with the children.
I am at a loss, I have no idea why it doesn't render, any help would be greatly appreciated!

Custom Views don't show up in Custom ViewGroup

I'm trying to create a card game for Android, and I'm stuck on a confusing Issue.
I have a Custom View called CardBG that extends from the ViewFlipper class, so that i can flip the card around and show the front and the back. this works fine.
But i need to add some other things to the Cards, like a Textfield for example. So i created a Viewgroup in the belief that i can simply add Views to it. Adding this ViewGroup to my Activity results in nothing though.
What am I doing wrong? Is this a wrong approach alltogether?
I've also tried having Card extend a layout-class, such as RelativeLayout, but it gives me the same result.
Here is the relevant code, adding the cards has to be done dynamically, so no xml shenanigans:
TestActivity.java
public class TestActivity extends Activity {
RelativeLayout menuLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
menuLayout = (RelativeLayout) findViewById(R.id.layout_menu);
Card c = new Card(this, null);
c.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
menuLayout.addView(c);
}
}
Card.java
public class Card extends ViewGroup{
CardBG background;
TextView text1;
public Card(Context context, AttributeSet attrs) {
super(context, attrs);
Log.w("Card", "Constructor");
background = new CardBG(context, null);
background.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
this.addView(background);
}
(protected void onLayout is also in this file, but i do nothing in that method except calling super.onLayout)
}
CardBG.java
public class CardBG extends ViewFlipper{
ImageView blue;
ImageView red;
public CardBG(Context context, AttributeSet attrs) {
super(context, attrs);
Log.w("CardBG", "Constructor");
blue = new ImageView(context);
blue.setImageResource(R.drawable.card_blue);
blue.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
this.addView(blue);
red = new ImageView(context);
red.setImageResource(R.drawable.card_red);
red.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
this.addView(red);
//from here on out there are only onclick listener to test the flipping animations
}
While extending ViewGroup. You must have to implement onLayout method. In onLayout you need to call layout method on each child of this ViewGroup and provide desired position (relatively to parent) for them. You can check source code of FrameLayout (one of the simpliest subclasses of ViewGroup) to find out how it works.
Although, you may extend your view from RelativeLayout, LinearLayout or simple FrameLayout instead. RelativeLayout would give onLayout implementation by itself and provide relative positions to its children.
EDIT:
You might need to inflate layout in current view.
Sample Code:
public class Card extends RelativeLayout {
public Card(Context context, AttributeSet attr) {
super(context, attr);
View.inflate(context, R.layout.my_card_layout, this);
}
}

How to treate some xml structure as a View?

I have a LinearLayout with some Views in it. Then I want to treat this element as a View, so I created a new class extending from LinearLayout. Now when I dynamically add a new instance of this class into the layout I see nothing. I believe I have to get the View somehow from my class, but don't know how. Is it possible to somehow assocciate this new class with an xml?
Update:
public class Task extends LinearLayout {
public Task(Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.task_view, this, false);
}
public Task(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.task_view, this, false);
}
public Task(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
LayoutInflater.from(context).inflate(R.layout.task_view, this, false);
}
}
Then:
Task newTask = new Task(getActivity());
someLinearLayout.addView((View) newTask); // happens nothing
You can use different approaches like:
Inflating it into a View
Using <include> tag
Inflating:
public class InflatedView extends LinearLayout
{
public InflatedView(Context c)
{
super(c);
LayoutInflater.from(this).inflate(R.layout.your_other_layout, this);
}
//override other constructors too.
}
Now you can use this in your xmls like this:
<com.your.package.InflatedView android:layout_height="etc" other:attribute="here" />
Include:
Very simple, use include tag:
<include layout="#layout/your_other_layout"/>
Here are RomainGuy's layout tricks.

resize relative layout after adding child view

I got a custom view extending RelativeLayout.
After inflating it with an xml layout I want to add another view to it.
I want to add the view above the existing one.
That seems to work but the RelativeLayout does not resize itself.
It looks like the older contents get overwritten, or maybe scroll down, where they
are not visible.
I create my custom RelativeLayout with:
public class BottomBarEdit extends RelativeLayout {
private Context context;
public BottomBarEdit(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
View.inflate(context, R.layout.lay_bottom_bar_edit, this);
I try to add a view dynamically with:
// create layout parameters
RelativeLayout.LayoutParams lparams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.FILL_PARENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
lparams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
lparams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
lparams.addRule(RelativeLayout.ABOVE, R.id.bottom_bar_buttons);
// RelativeLayout editMain = (RelativeLayout)
// findViewById(R.id.bottom_bar_edit);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View noteBar = inflater.inflate(R.layout.lay_bottom_bar_notes, null);
this.addView(noteBar, lparams);
Any idea whats going wrong?
Thanks!

Android: using LayoutInflater.inflate to pass custom parameters to a constructor

This has been bothering me for a while, and none of my searching has yielded results. If I have a custom GUI element, I can use a LayoutInflater to inflate it as I would a normal component. The inflation call results in a call to my custom GUI element's constructor, and all is well.
However, what if I want to add a custom parameter to my element's constructor? Is there a way I can pass this parameter in using LayoutInflater?
For example:
In main xml, I have a holder for my layout:
<LinearLayout
android:id="#+id/myFrameLayoutHolder"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
</LinearLayout>
and a MyFrameLayout.xml file:
<com.example.MyFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/MyFLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1 >
<!-- Cool custom stuff -->
</com.example.MyFrameLayout>
and an inflater call:
LayoutInflater MyInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout myFLayoutHolder = (LinearLayout) findViewById(R.id.myFrameLayoutHolder);
MyFrameLayout L = ((MyFrameLayout) MyInflater.inflate(R.layout.MyFLayout, myFLayoutHolder, false));
myFLayoutHolder.addView(L);
If, in my class that extends FrameLayout, I add a parameter to my constructor, I get a crash:
public class MyFrameLayout extends FrameLayout {
private int myInt;
public MyFrameLayout(Context context) {
this(context, null);
}
public MyFrameLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0, 0);
}
public MyFrameLayout(Context context, AttributeSet attrs, int defStyle, int myParameter) {
super(context, attrs, defStyle);
myInt = myParameter;
//Amazing feats of initialization
}
}
Now, it's easy enough to work around this issue by defining a custom init method that I call right after layout inflation, but that seems clumsy to me. Is there a better way?
You cant define a constructor with your own parameter because your constructor signature conflicts with FrameLayout's own constructor signature and you are not calling super(context, attrs, defStyle);, instead you are calling super(context, attrs); which is incomplete for this constructor.
You must need to define all three native constructors exactly as they are:
FrameLayout(Context context)
FrameLayout(Context context, AttributeSet attrs)
FrameLayout(Context context, AttributeSet attrs, int defStyle)
What you can do is to use your own (custom) attributes in xml and then retrieve them in your MyFrameLayout's attrs object
If the custom component is inflate by XML file or inflate method. You dont´t pass elemnts in the construct because this is not support in android.

Categories

Resources