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
Related
I am a new developer in Android. I am trying to develop an application in Android that displays data in a table format. My data scrolls both horizontally and vertically. My parent class contains FrameLayouts which acts as rows. Inside each framelayout the CustomViews(extended from viewGroup) are populated which act as columns. Am maintaining the FrameLayouts in the parent panel in a list FrameLayoutList and the customViews inside that FrameLayout in a List CustomViewsList in the child class. Initially I have created the views in each FrameLayoutClass(rows) that will fit into the view. All works fine. But Upon scrolling horizontally I am creating a new CustomView and storing it in a list. Then adding the newly created items from the list into the FrameLayout in OnMeasure method. But the problem is the OnMeasure is called for the parent, but is not called for the child FrameLayouts. But I have called view.Measure(int,int) in my parent's OnMeasure. I tried all possible Invalidate(), postInvalidate(), InvalidateChild(view,Rect) and requestLayout. Nothing solved this. Can anyone help me on this?
This the code in my Parent's OnMeasure:
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int totalwidth = {some calculation...};
int totalheight = {some calculation...};
foreach (CustomFrameLayout row in this.FrameLayoutList) {
if (this.IndexOfChild(row) == -1)
this.AddView(row);
MeasureChild (row, 450, 750);
//row.Measure(450,750);
}
SetMeasuredDimension(totalwidth,totalheight);
}
When the MeasureChild is hit I expect the ChildLayout's OnMesure to be called. It is called for the first time when loading. But when scrolling horizontally the code is hit but the ChildLayout's OnMeasure is not called.
This is the code in my child's OnMeasure:
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int totalwidth = {some calculation...};
int totalheight = {some calculation...};
foreach (CustomView column in this.CustomViewsList) {
if (column.IsNew) {
if (this.IndexOfChild(row) == -1)
this.AddView(column);
column.Measure(50,50);
}
}
SetMeasuredDimension(totalwidth,50);
}
Can anyone help me in this? how can get the OnMeasure called for the child class upon scrolling horizontally.
I am currently optimising a complex view containing many nested views. therefor i have created a custom layout extending the relativelayout class.
Based on data set from outside the class i further build the view with childs.
The child building is done within the custom layout. I got it working and the performance gain is enourmous. But before i can create and add the childs i need to know the width of the view.
There are several ways of getting the width of the view:
1 add global layout listener
public void init(){
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//width is known -> create child views
}
});
}
This solution does not always fire an event. For example when the view is inside a fragment and restored from a backstack state. Also there seem to be a 100 to 500ms delay before this event is triggered.
in onMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
w = MeasureSpec.getSize(widthMeasureSpec);
h = MeasureSpec.getSize(heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(w, h);
post(new Runnable() {
#Override
public void run() {
//width is known -> create child views
MyCustomViewGroup.this.postInvalidate();
}
});
}
The difficulty here is that adding views inside onmeasure will result into a call to onmeasure again. And endless loop is the result. Logic needed to prevent this. Could't figure out how.
3 add views in onLayout
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//width is known -> create child views
}
Various unexpected layout problems. Views not respecting layoutparams and showing weird behaviour. Not really sure how to solve it.
hacky timer implementation
private Handler ha = new Handler();
private Runnable r;
public void init() {
if (w > 0) {
//width is known ->create childs
return;
}
r = new Runnable() {
private long time = 0;
#Override
public void run() {
Log.d(TAG, "run");
if (w > 0) {
//width is known ->create childs
} else {
init(); //width is not known -> check later
}
}
};
ha.postDelayed(r, 5);
}
Ironically the last solution works best for me.
I know its a hell of a hack.
is there is anyone out there knowing alternatives? or can give me tips.
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 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.