Are these two the same?
A.
my_custom_view.xml
<?xml version="1.0" encoding="utf-8"?>
<com.abc.views.MyCustomView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
MyCustomView.java
public class MyCustomView extends LinearLayout {
public MyCustomView(Context context) {
super(context);
init();
}
public static MyCustomView inflate(ViewGroup parent) {
MyCustomView view = (MyCustomView) LayoutInflater.
from(parent.getContext()).inflate(R.layout.my_custom_view, parent, false);
return view;
}
B.
MyCustomView.java
public class MyCustomView extends LinearLayout {
public MyCustomView(Context context) {
super(context);
init();
}
public static MyCustomView inflate(ViewGroup parent) {
MyCustomView view = new MyCustomView(parent.getContext());
parent.addChild(view);
return view;
}
When we run,
MyCustomView.inflate(parent);
Not exactly. Long story short, the differences here is in instance A, the LayoutParams will be set to "MATCH_PARENT" for the width and "WRAP_CONTENT" for the height. In instance B, the LayoutParams will be whatever the default the parent View applies (usually WRAP_CONTENT for both width and height).
Additionally, in instance A, the view is not attached to the parent view. In instance B, the view is attached to the parent view.
Long story long, here's other differences.
public static MyCustomView inflate(ViewGroup parent) {
MyCustomView view = (MyCustomView) LayoutInflater.
from(parent.getContext()).inflate(R.layout.my_custom_view, parent, false);
return view;
}
This does a few things:
Inflates the view giving it layout parameters of the parent. If the
parent is a FrameLayout, then the LayoutParams instance will be a
FrameLayout.LayoutParams. If it is a LinearLayout, the
LayoutParams will be a LinearLayout.LayoutParams. The layout
parameters are assigned by the xml "layout_width" and
"layout_height". In this particular case, the width is set to MATCH_PARENT and the height is set to WRAP_CONTENT (although this can be overridden by the parent View).
The view is not added or attached to the parent view as shown by
the false parameter. (true will attach the view to the parent).
Any other attributes you eventually apply to the XML will be applied to the
View.
The alternate constructors will be applied so the different
attributes will be filled. Which constructor is called is dependent on which attributes you apply.
View#(Context context, AttributeSet attrs)
View#(Context context, AttributeSet attrs, int defStyleAttr)
View#(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
On the other hand:
public static MyCustomView inflate(ViewGroup parent) {
MyCustomView view = new MyCustomView(parent.getContext());
parent.addChild(view);
return view;
}
Default LayoutParams will be applied to the child view. In most cases this is set to WRAP_CONTENT for both width and height, but it's up to the parent view to determine which.
The view is attached to the parent view. If the caller were to try to add it to another parent view, it would result in a crash.
Absolutely no additional attributes are applied to the View. They will have to be added manually.
Only the View#(Context context) constructor is used in creation.
Related
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!
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);
}
}
Here's a little background into what I'm trying to achieve, in order to help make my question a bit more clear...I'm creating a Navigation Drawer where each item in the ListView looks similar to the following:
However, I need to be able to change the color of the right side border (the blue) to a variety of different colors programmatically, so while playing around with a solution, I decided to extend a RelativeLayout and draw the line in the onDraw(Canvas c); method. My RelativeLayout code is as follows:
public class CustomRelativeLayout extends RelativeLayout {
private final Paint paint = new Paint();
public CustomRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
//Other Constructors
private void init() {
setPaintColor(Color.argb(128, 0, 0, 0));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(getMeasuredWidth() - 1, 0, getMeasuredWidth() - 1, getMeasuredHeight(), paint);
}
public void setPaintColor(int color){
paint.setColor(color);
invalidate();
}
}
My NavigationDrawer's ListView also contains a header that uses this class, and it works fine as a header view. However, for each individual ListView item, the border isn't present. I've debugged my solution, and found that my subclassed RelativeLayout's onDraw(Canvas c); method is called for the header view, but isn't called for each of the ListView's child views provided by my ArrayAdapter<String>.
I know there are other ways to handle this, such as using a default View, setting it's background to the color I want, and aligning it to the right - but that's not my question. My question is why is my CustomRelativeLayout's onDraw(Canvas c); method is called for the ListView's header view, and not for each of the Views provided by my adapter? Any insight into this behavior would be appreciated. Also, here are the CustomArrayAdapter and nav_drawer_item.xml used with the ListView in case they're helpful:
public class SimpleDrawerAdapter extends ArrayAdapter<String> {
public SimpleDrawerAdapter(Context context, int resource,
String[] sections) {
super(context, resource, sections);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
RelativeLayout container = null;
if(convertView == null){
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
container = (CustomRelativeLayout) inflater.inflate(R.layout.nav_drawer_item, parent, false);
} else {
container = (CustomRelativeLayout) convertView;
}
((TextView)container.findViewById(R.id.nav_item_text)).setText(getItem(position));
return container;
}
}
nav_drawer_item.xml
<?xml version="1.0" encoding="utf-8"?>
<com.mypackage.views.CustomRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="#dimen/navigation_drawer_width"
android:layout_height="wrap_content">
<TextView
android:id="#+id/nav_item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/nav_drawer_grey"
android:textSize="#dimen/text_large"
android:layout_margin="#dimen/navigation_drawer_item_margin"
android:paddingTop="4dp"
android:paddingBottom="4dp" />
</com.mypackage.views.CustomRelativeLayout>
Have you tried clearing the WILL_NOT_DRAW flag by calling setWillNotDraw method in your custom layout?
If this view doesn't do any drawing on its own, set this flag to allow
further optimizations. By default, this flag is not set on View, but
could be set on some View subclasses such as ViewGroup. Typically, if
you override onDraw(android.graphics.Canvas) you should clear this
flag.
just call it yourself each time you iterate over the list
if(convertView == null){
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
container = (CustomRelativeLayout) inflater.inflate(R.layout.nav_drawer_item,
parent, false);
if(container!=null){
container.draw()
}
} else {
container = (CustomRelativeLayout) convertView;
if(container!=null){
container.draw()
}
}
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!
I have created a custom view named MyDraw ,this is my MyDraw code,
public class MyDraw extends View {
public MyDraw(Context context) {
super(context);
}
public MyDraw(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyDraw(Context context, AttributeSet attrs) {
super(context, attrs);
}
........................................
}
I have added the view in XML file using package name. It is working fine. Now I want to set height and width for the MyDraw in run time,for that i have used following code,
mMyDraw.setLayoutParams(new LayoutParams(220, 300));
but i got Exception like,
java.lang.ClassCastException: android.view.ViewGroup$LayoutParams
How to solve this exception?
please help me..
You must override the onMeasure() method of the View.
For a nice example you can check here: http://kahdev.wordpress.com/2008/09/13/making-a-custom-android-button-using-a-custom-view/
And a very cool video that I would recommend is here: http://marakana.com/forums/android/general/563.html
Hope this helps!
Override the onMeasure() method, have a look here
There is a simple way:
based on our custom view parent class we can use layout param.
for example if our custom view is extended from FrameLayout:
FrameLayout.LayoutParams params = (LayoutParams) findViewById(R.id.root).getLayoutParams();
params.width = newwidth;
params.height = newHeight;
setLayoutParams(params);
where "R.id.root" is id of our root view in custom_view.xml that in this case is FrameLayout.