use setLayoutParams() but it doesn't work - android

I create a custom view-MyImageView-to draw a bitmap in it,i use setLayoutParams() to set my view's width and height,but it doesn't work,i use log to track my view's width and height,i found that both of them are 0,why the width and height are not both 300? here's part of my main activity code:
myCanvas=new MyImageView(CanvasTest3Activity.this,Path);
LinearLayout.LayoutParams p=new LinearLayout.LayoutParams(300,300);
myCanvas.setLayoutParams(p);
here's part of my MyImageView 's code:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentwidth=MeasureSpec.getSize(widthMeasureSpec);
int parentHeight=MeasureSpec.getSize(heightMeasureSpec);
int mywidth=(int)(parentHeight*0.5);
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),mywidth);
}

You need to get the parent View and use the add method to add your view + your view params. Something like:
"ParentViewInstance.add(this_is_my_child_view, this_is_the_layout_params_for_my_child_view)"
This means that the type of the LayoutParams of your child view, should be the same type as the ParentView LayoutParams. Take a look at this answers for code samples.

Related

How to square a custom view when layout_height and layout_width are set to have different magnitudes?

As per my understanding when user specifies a views height/width in pixels/dp, the view receives EXACTLY spec mode. EXACTLY spec mode means that the view must use this size. Suppose I want to make sure my view remains a square. How can that be achieved if the user sets different values for layout_height and layout_width.
Override onMeasure for your custom view, determine what the smallest dimension is, and set both your width and height to that minimum dimension.
Something like:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int specWidth = MeasureSpec.getSize(widthMeasureSpec);
int specHeight = MeasureSpec.getSize(heightMeasureSpec);
int lesserDimension = (specWidth > specHeight)?specHeight:specWidth;
setMeasuredDimension(lesserDimension, lesserDimension);
}

Override onMeasure after getting ImageView by Id

I need to find a view by id and then override its onMeasure method. Does anyone know how to do that?
The following will not work in Java, but conceptually it's what I need:
ImageView myImage = (ImageView) findViewById(R.id.some_pic);
myImage.onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
showOther(width, height);
};
myImage.setImageBitmap(bmp);
Java offers this
ImageView myImage = new ImageView(this) {
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
showOther(width, height); }
};
or this
ImageView myImage. = (ImageView) findViewById(R.id.some_pic);
myImage.setImageBitmap(bmp);
I thought of using setOnMeasureListener but no such method is defined for ImageView. Any thoughts on how to get this going?
I'm guessing that the some_pic ImageView was declared in XML and then inflated, probably by calling setContentView in your Activity class. Inflation means that Android reads the XML and translates it into Java objects. Once you call setContentView, all the objects get instantiated, and after that it's too late to make changes to their classes, such as overriding ImageView's onMeasure method.
I can think of two options:
(preferred) Create a new class that extends ImageView and overrides onMeasure. When you declare some_pic in XML, instead of using an ImageView tag, name the tag after your new class (fully qualified name):
<com.mycompany.myproject.MyImageView
android:id="#+id/some_pic"
...
/>
Don't declare some_pic in XML. In your Activity's onCreate method, after the call to setContentView, instantiate a new ImageView and override its onMeasure method, as you did in your question's first Java option. You'll have to manually insert this ImageView into your layout; use the Google to learn how to insert a view into another view (here's an example).
I just had the same problem like yours.
Maybe you already solved this, but there is no explicit solution here.
Just extend the ImageView in a new class for example MyImageView and use that in the layout definition. This way you can use:
MyImageView myImage = (MyImageView) findViewById(R.id.some_pic);
Example class and xml can be found here in the second answer:
ImageView be a square with dynamic width?

Android Custom view and canvas size

I've created a custom view which creates a bitmap and then in the onDraw() method I display an objects data.
Also in the XML file I have made the view horizontally scrollable.
My question is after I've drawn all the data on the canvas from the onDraw() method, how can I increase the view's size to show all the data.
i.e I want the view to scroll just enough until the data has finished and not any more.
I do have the y-coordinate for the last item displayed.
Currently I'm setting the width of the view to 800dp in the XML file, but for some object's it cut's off some of the data.
Thanks
You can override the onMeasure() method to adjust the size of your View:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int parentViewWidth = MeasureSpec.getSize(widthMeasureSpec);
    int parentViewHeight = MeasureSpec.getSize(heightMeasureSpec);
// ... take into account the parent's size as needed ...
super.onMeasure(
MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY));
}

force onLayout in a subView whose dimensions doesn't change

I have a Custom ViewGroup with some views inside.
In response to an event, a requestLayout is called and in OnLayout some of the views will
get a new layout position.
One of them maintain its position but needs to be rearranged inside. However this view's onlayout method will not be called since its layout position doesn't change. Trying to call requestLayout, forceLayout or invalidate in this view doesn't work.
The only dirty trick that I have found is to change some of its value position to force the layout change, but I think there should be a better way. So, by now I do something (horrible) like:
int patch = 0;
#Override protected void onLayout(boolean changed, int l, int t, int r, int b) {
...
_myView.patchedLayout(patch);
_myview.layout(l1, t1 , r1, t1+patch);
patch = (patch+1) % 2;
...
}
Any way to get the same result in a better way?
I finally got the solution: I need to override onMeasure and be sure to call mesure in my view:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
...
_myview.measure(widthMeasureSpec, heightMeasureSpec);
...
It will set the LAYOUT_REQUIRED flag in the view's private field mPrivateFlags so it will force to call onLayout

Custom Viewgroup works inside LinearLayout not RelativeLayout

I wanted to create a custom LinearLayout (and later a custom ImageButton) that could take percentage values for both dimensions of size based on its parent's size regardless of the parent type (Relative or Linear). I was following this post: How to size an Android view based on its parent's dimensions, and it was very helpful, but I have a problem that those answers don't address.
When I place my Custom LinearLayout inside another LinearLayout, everything works as expected. My Custom LinearLayout covers the expected space (80% of the parent's width in the example below).
However if I place it inside a RelativeLayout, my screen always shows empty, I am not sure why this happens.
Here is my class:
public class ButtonPanel extends LinearLayout {
public ButtonPanel(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int newWidth = (int) Math.ceil(parentWidth * 0.8);
this.setMeasuredDimension(newWidth, parentHeight);
this.setLayoutParams(new RelativeLayout.LayoutParams(newWidth,parentHeight));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
And here is my testing layout for the activity.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.android.tests.views.ButtonPanel
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/inner_panel"
android:gravity="center_horizontal"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true">
</com.android.tests.views.ButtonPanel>
</RelativeLayout>
In my activity all I do is set the Content View to the above layout.
(Incidentally, does anybody now how I could get the type of the parent dynamically for setting the new LayoutParameters? Above you'll see the parent type (RelativeLayout) hard-coded into the Custom View onMeasure function)
Thanks in advance for any help!
Is this exposed to be a problem?
this.setLayoutParams(new RelativeLayout.LayoutParams(newWidth,parentHeight)); // <-- a RelativeLayout params?
In the onMeasure function you could use something like this to know what class is the parent of the view.
this.getParent().getClass().getName()
This should also work
a instanceof B
or
B.class.isAssignableFrom(a.getClass())
When using "instanceof", you need to know the class of "B" at compile time. When using "isAssignableFrom" it can be dynamic and change during runtime.
If you are not compfortable with string comparison, you could also use enums.
Turns out my two inquiries in this post were more related than expected.
I realized that by setting my view's LayoutParams to a completely new instance, I was overwriting the layout positioning information needed by the Relative Layout to position my view.
By 'zeroing out' that information, my view has the right dimensions, but the layout doesn't know where to place it, so it simply doesn't.
The following code for the new onMeasure shows how just directly modifying the height and width of the LayoutParams already attached to my view I avoid both overwriting the layout position information and having to create new LayoutParams based on the parent's type.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int specWidth = MeasureSpec.getSize(widthMeasureSpec);
int specHeight = MeasureSpec.getSize(heightMeasureSpec);
int newWidth = (int) Math.ceil(parentWidth * 0.8);
int newHeight = (int) Math.ceil(parentHeight * 0.8);
this.setMeasuredDimension(newWidth, newHeight);
this.getLayoutParams().height = newHeight;
this.getLayoutParams().width = newWidth;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Now, I'll be honest and say that this code is still not bug-free. Bringing the activity to the foreground and background multiple times constantly reduces the size of this custom view. The 0.8 reduction factor gets applied over and over each time the activity is brought up (I suspect the setting of the LayoutParams has to do with it, it might actually be unnecessary, but I haven't has time to test).
BUT, this still answered the question concerning this post, namely, why was my view not appearing at all despite having the right dimensions.

Categories

Resources