I'm adding a LinearLayout as a child of a custom layout. The LinearLayout has margins, but the margins never get into the LayoutParams.
Here's the xml for the LinearLayout
<com.perinote.widgets.LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="#dimen/popup_margin_LR"
android:layout_marginRight="#dimen/popup_margin_LR"
android:layout_marginTop="#dimen/popup_margin_TB"
android:layout_marginBottom="#dimen/popup_margin_TB"
>
Here's the code that inflates and adds the LinearLayout to the custom layout, where "this" is the custom layout, which is an extension of ViewGroup:
public View setContentView (int layoutId)
{
LayoutInflater inflater = ((Activity) getContext()).getLayoutInflater();
ViewGroup v = (ViewGroup) inflater.inflate (layoutId, this, false);
contentView = v;
ViewGroup.LayoutParams params = v.getLayoutParams ();
this.addView (contentView, params);
return contentView;
}
And, finally, here's a piece of onMeasure in the custom layout.
#Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
{
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) contentView.getLayoutParams();
int contentWidth = MeasureSpec.getSize (widthMeasureSpec);
int contentHeight = MeasureSpec.getSize (heightMeasureSpec);
... some other stuff ...
// measure contentView.
int contentWidthSpec = MeasureSpec.makeMeasureSpec (contentWidth, MeasureSpec.AT_MOST);
int contentHeightSpec = MeasureSpec.makeMeasureSpec (contentHeight, MeasureSpec.AT_MOST);
contentView.measure (contentWidthSpec, contentHeightSpec);
setMeasuredDimension (MeasureSpec.getSize (widthMeasureSpec), MeasureSpec.getSize (heightMeasureSpec));
}
The app crashes on the first line of onMeasure() -- params =. The error is:
java.lang.ClassCastException: android.view.ViewGroup$LayoutParams cannot be cast to android.view.ViewGroup$MarginLayoutParams
which indicates that, during inflation, the inflator created LayoutParams instead of MarginLayoutParams.
What can I do to get MarginLayoutParms or some other variant that includes the layout_margin values?
The class extending ViewGroup must also override generateLayoutParams(). In my case:
#Override
public LayoutParams generateLayoutParams (AttributeSet attrs)
{
return new ViewGroup.MarginLayoutParams (getContext(), attrs);
}
but you can extend LayoutParams to contain whatever you need instead of using MarginLayoutParams. During inflation of a child view, generateLayoutParams() gets called. This (aha) is why inflate needs to know the parent view group, even if you aren't adding the new child to the parent at this time.
Credit really goes to #Mike M. Thanks!
Related
I have a HorizontalScrollView with a vertical LinearLayout in it. There I add some custom views of the same type. By default there is a lot of spacing between the views. So I guessed I have to set the margin of my views to 0 or something. But there ist absolutely no result.
First I tried to change the margin in the xml
<gui.CardUi
android:id="#+id/cardUi"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="0dp"
</gui.CardUi>
Than I tried it to change the margin in code:
private void setMargins ( int left, int top, int right, int bottom) {
if (getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) getLayoutParams();
p.setMargins(left, top, right, bottom);
requestLayout();
}
}
setMargins(0, 0, 0, 0);
Not sure if the info is important but I add the views programmatically with LayoutInflater.
Your margins would not be set because of the way you inflate the views.
I guess you used such a way:
View child = getLayoutInflater().inflate(R.layout.mylayout, null);
The point is when you want to inflate a view and add it to another view, You should inform the inflater about the container view (in your example Linear layout). So your layout parameter such as margins and weight and gravity would be set correctly.
So use this method instead:
View child = getLayoutInflater().inflate(R.layout.child, item, false);
There is no need to add margin in your code.
I am a starter in android programming. I am wondering is it possible for me to customize the size of a customized layout in program?
Here is the solution I am trying:
1. I created a customized Layout Class called MyLayout and write the onMeasure and onScale method as
MyLayout extends ViewGroup {
public double childWidth, childHeight;
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (getChildCount() > 0) {
//In the current version, we should only have one child view
View childView = getChildAt(0);
measureChild(childView, (int)(childWidth), (int)(childHeight));
}
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (getChildCount() > 0) {
View childView = getChildAt(0);
childView.layout(0, 0, childView.getMeasuredWidth(), childView.getMeasuredHeight());
}
}
}
create the XML file of this layout activity_mylayout, inside the layout I included a imageview as the child layout
<com.example.MyLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/android" />
</com.example.MyLayout>
Write the code for drawing the view and set the size of the child view it contains
setContentView(R.layout.activity_mylayout);
MyLayout usl = (MyLayout)findViewById(R.layout.mylayout);
if(usl == null) System.out.println("SSSS");
usl.childWidth = 200;
usl.childHeight = 200;
Now I am having the problem of having MyLayout as null and throws a null pointer exception. I am probably doing wrong in many places I guess, but any suggestions on the reason why the MyLayout is null?
Well the thing that immediately pops into my mind is that onMeasure must call setMeasuredDimension(int, int) before it exits or later calls to measure will fail. Even if the width and height are measured to 0 you need to do that.
The second thing is- if there's only one child, why does this view exist at all? All you're doing is adding overhead. Its probably the wrong design to have this layout at all, rather than just using the child view directly.
The third is that you're probably hitting the usl equals null case causing the null pointer exception, but its impossible to tell without the stack trace and xml being used.
I have spent the whole day debugging various ways to add custom ViewGroup into another custom ViewGroup and nearly went crazy because none of them works, and there is no official documentation or sample that shows how it can be done...
Basically, I have 2 custom ViewGroup:
HorizontalDockView extends ViewGroup
GameEntryView extends FrameLayout
HorizontalDockView overrides onDraw, onMeasure, etc and everything is called normally and works perfectly.
However, when I create GameEntryView from inside HorizontalDockView's constructor and call addView(gameEntryView), the gameEntryView will never ever show regardless of the layoutParams, addView called from whatever thread, or however I call, load, and setContentView on the parent HorizontalDockView. If I list through the horizontalDockView.getChildAt(); all the gameEntryView objects are still there.
Hopeless, I try to debug through GameEntryView's onDraw, onMeasure, dispatchDraw methods and realized none of them actually get called! No.. not even once!
Do I need to iterate through all the child view in the parent (HorizontalDockView's) on* call and call the children's on* explicitly? I was just calling super.on*() on the parent.
I did call setWillNotDraw( false ); on both the parent and the child class.
How do I get the child to show up inside the parent's view? simple sample or existing small open source project is highly appreciated!
Thank you very much!
Did you overwrite onLayout? When Android lays out your ViewGroup, your ViewGroup is responsible for laying out the children.
This code is from a custom ViewGroup that lays out all children on top of each other:
#Override
protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) {
int count = this.getChildCount();
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
}
}
For completeness, the onMeasure override:
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth, parentHeight);
int count = this.getChildCount();
for (int i = 0; i < count; i++) {
View child = this.getChildAt(i);
this.measureChild(
child,
MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.EXACTLY));
}
}
I wrote a class that extends ViewGroup and override the method as follow:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for(int index = 0; index < getChildCount(); index++){
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
View child = getChildAt(index);
child.measure(View.MeasureSpec.makeMeasureSpec(width,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(height, View.MeasureSpec.UNSPECIFIED));
}
}
When I add subView to this viewGroup as follow:
String word = words[random.nextInt(words.length)];
RelativeLayout layout = new RelativeLayout(DraggableGridViewSampleActivity.this);
TextView text = new TextView(DraggableGridViewSampleActivity.this);
text.setText(word);
ImageView view = new ImageView(DraggableGridViewSampleActivity.this);
view.setImageBitmap(getThumb(word));
layout.addView(view);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layout.addView(text, params);
dgv.addView(layout);
The effect is like: The textView can't be displayed completely. It seems that the parent view doesn't provide enough space for the textview to display.
How to have the textView displayed completely?
The textView can't be displayed completely. It seems that the parent
view doesn't provide enough space for the textview to display.
What exactly are you trying to do with the onMeasure method? Right now you're letting the super class handle an initial measure of the children(super.onMeasure) and then you remeasure the children again, this time giving them a MeasureSpec.UNSPECIFIED, basically telling the children they could be as big as they want.
You should always respect the MeasureSpec when you're measuring the children(or the parent should allow scrolling). If the parent says that the children should be AT_MOST 500 px then don't potentially step over this value when measuring the children by giving them a MeasureSpec of UNSPECIFIED.
In my application i have a list view and i am inflating a web view into the list view. I am able to load data and images into the list view item. The problem is the list items are not of same height due to different contents. I want to make all list items having same height
How to calculate the height of the list item that has the maximum data and set that height to rest of the list items in the list.
How would i do that.. i have no clue on that.. Say if the 10th item has the maximum height i want to apply that height to the rest. help me implement this please and thank you for your support
<?xml version="1.0" encoding="utf-8"?>
<WebView
android:id="#+id/wv1"
android:layout_width="fill_parent" android:paddingRight="20dp"
android:layout_height="wrap_content" />
Have an array of flags, say
boolean[] flags=new boolean[szieOfList];
fill this array to false;
Have a variable max height in your activity or adapter, say max, initialize it with 0 in onCreate method, now in getView method where you are inflating xml, write the following code:
LayoutInflater layoutInflater = (LayoutInflater) getActivity()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view= (WebView) layoutInflater
.inflate(R.layout.web_view, null);
if(flag[position])
{
LayoutParams params = view.getLayoutParams();
params.height = max;
view.setLayoutParams(params);
}
ViewTreeObserver observer = view.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//in here, place the code that requires you to know the dimensions.
flag[position]=true;
int height= view.getHeight();
if(max<height)
{
max=height;
notifyDataSetInvalidated();
}
else
{
LayoutParams params = view.getLayoutParams();
params.height = max;
view.setLayoutParams(params);
}
//this will be called as the layout is finished, prior to displaying.
}
}