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?
Related
I have made class called ProgressButton that extended RelativeLayout.Now in main xml i added this class:
<com.tazik.progressbutton.ProgressButton
android:id="#+id/pb_button"
android:layout_width="200dp"
android:layout_height="wrap_content"/>
As you can see i added android:layout_width="200dp", now in ProgressButton class i want to get this size to create a button with this size:
public class ProgressButton extends RelativeLayout {
private AppCompatButton button;
public ProgressButton(Context context) {
super(context);
initView();
}
private void initView() {
initButton();
}
private void initButton() {
button = new AppCompatButton(getContext());
LayoutParams button_params = new LayoutParams(????, ViewGroup.LayoutParams.WRAP_CONTENT);
button_params.addRule(RelativeLayout.CENTER_IN_PARENT,RelativeLayout.TRUE);
button.setLayoutParams(button_params);
button.setText("click");
addView(button);
}
I want to create button exactly to size of relativeLayout, so how can i get layout_width in my custom view to set button_params width?
now in ProgressButton class i want to get this size to create a button with this size
As #MikeM. suggested in a comment. It could be as easy as giving that child view a width of MATCH_PARENT. See below...
LayoutParams button_params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
With that in place you don't need to worry about the actual size because MATCH_PARENT will stretch your child view to occupy the whole parent's width...obviosuly respecting margins and paddings.
However, if you do need to know the parent's width, you should query that in onMeasure. I strongly suggest you to stay away from onMeasure whenever possible because it is a bit complex and it might take a lot of your development time.
Either way, in onMeasure you can know what measurements the parent view wants to give to its child views, this is based on the space available to render inside the parent and the layout params specified...
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int childWidth = 0;
if(widthSpecMode == MeasureSpec.AT_MOST){
//The parent doesn't want the child to exceed "childWidth", it doesn't care if it smaller than that, just not bigger/wider
childWidth = MeasureSpec.getSize(widthMeasureSpec);
}
else if(widthSpecMode == MeasureSpec.EXACTLY){
//The parent wants the child to be exactly "childWidth"
childWidth = MeasureSpec.getSize(widthMeasureSpec);
}
else {
//The parent doesn't know yet what its children's width will be, probably
//because it's still taking measurements
}
//IMPORTANT!!! set your desired measurements (width and height) or call the base class's onMeasure method. Do one or the other, NOT BOTH
setMeasuredDimension(dimens, dimens);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Add a few Log.d calls inside onMeasure for a better understanding of what's happening. Be aware that this method will be called multiple times.
Again, this is an unnecessary overkill for your case scenario. Setting MATCH_PARENT to the button should produce the results you want
I need to create a View Placeholder: a View that doesn't draw nothing, but has the same dimension of a given View. I'm trying to achieve that by doing so:
public class ViewPlaceHolder extends View {
View target;
public ViewPlaceHolder(View target) {
super(target.getContext());
this.target = target;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
target.measure(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(
MeasureSpec.makeMeasureSpec(target.getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(target.getMeasuredHeight(), MeasureSpec.EXACTLY));
}
}
By now it doesn't work as expected. Any suggestions?
Thanks a lot
PS: I need a placeholder to make same animations and effects on a scrollView
PPS: additional explanation:
the result of target.getMeasuredWidth() and target.getMeasuredHeight() is correct, but the ViewPlaceholder has a complete random height (the width seem to be ok)
Sorry, I made an error on the layout hierarchy, now it works fine!
If the target view is drawn , then you can read its dimensions by calling the getWidth() and getHeight() methods. So, if these methods does not return 0 , you can apply the setLayoutParams on the ViewPlaceHolder. The only thing that you have to keep in mind is the Layout that will lay your ViewPlaceHolder. Suppose it is a RelativeLayout you can do something like this inside the ViewPlaceHolder constructor body :
public ViewPlaceHolder(View target) {
super(target.getContext());
this.target = target;
int height = target.getHeight();
int width = target.getWidth();
this.setLayoutParams(new RelativeLayout.LayoutParams(width,height));
this.invalidate();
}
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.
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.
I have an array of Buttons (different sizes etc) which are configured from and xml file (written by me). I want to add those buttons on the bottom of the screen and when the row of buttons ends, just start a new row and add buttons until the array ends. I want to mention that I do not set the size of the buttons in the xml file so I don't know the size from the beginning. Another problem is that after or before I add the button to the layout programatically with layout.addView(button) the method button.getWidth() returns 0 because the UI elements are not drawn in the UI yet. I also overrided the onLayout() method but still wasn't able to redraw the buttons.
If you have any ideas, please help.
Thanks
Well you could make the button take the same size using weight property. In that case, decide how many buttons you need in a row and use a for loop to do it accordingly. If you need the width you could use something like
Add this to your onCreate
ViewTreeObserver vto = layout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
You should be able to get the width and height
over here.
layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
This is how I did it. I overrided the onMeasure() method of the layout containing the buttons.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int specSize = MeasureSpec.getSize(widthMeasureSpec);
this.width = specSize;
specSize = MeasureSpec.getSize(heightMeasureSpec);
this.height = specSize;
//now you can use the sizes before the layout is drawn
this.webview.setLayoutParams(new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT,this.height - this.menuBarHeight) );
//don't forget for the parent method!
//but at the end, after the measures are done
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}