Android Custom view and canvas size - android

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));
}

Related

Force re-layout on a viewgroup including all its children

I am writing up a custom layout (which extends FrameLayout) that can be zoomed in. All its children are also custom views which would actually get the scale factor from their parent via a getter method and scale accordingly by setting the scaled dimensions like
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
float scaleFactor = ((CustomLayout) getParent()).getCurrentScale();
setMeasuredDimension((int) (getMeasuredWidth() * scaleFactor), (int) (getMeasuredHeight() * scaleFactor));
}
I am using a ScaleGestureDetector to detect the "pinch to zoom" gesture and change the layout's scaleFactor. Then I force a layout on the custom layout by calling requestLayout. Unfortunately this doesn't seem to have any effect on its children. The children's onMeasure & onLayout are never called even though the parent goes through its measure & layout cycle. But, if I directly call requestLayout on one of the children, just that child gets scaled according to the scale factor set in the parent!!
It seems that unless requestLayout is exclusively called on a view, it doesn't actually measure itself again and instead uses some kind of cache. This is evident from the source code for view which says
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
How do I force the children also to go measure themselves again on calling requestLayout on their parent?
This will force relayout the view's children (given that view's own width and height does not need to change)
private static void relayoutChildren(View view) {
view.measure(
View.MeasureSpec.makeMeasureSpec(view.getMeasuredWidth(), View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(view.getMeasuredHeight(), View.MeasureSpec.EXACTLY));
view.layout(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
}

Overriding onMeasure method within view

I am currently overriding the onMeasure() method within a custom view in order to ensure it's width and height are equal and everithing appears to work except the parent linearlayout appears to think its dimensions haven't changed. I am currently using the setMeasuredDimension() method within the onMeasure override, in order to force the width to the measured height. Is there something else I need to do in order for the parent viewgroupto see these changes. Please note that the view is drawn at the correct size, but the parent view group now extends past the screen (it's not seeing the new width set from within the onMeasure() override).
As requested, I have pasted my onMeasure() code below:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
int measuredHeight=getMeasuredHeight();
setMeasuredDimension(measuredHeight,measuredHeight);
if(measuredHeight > m_iconType.largestDimension()){
m_iconType.largestDimension(measuredHeight);
try {
Class<?>clazz = Class.forName(m_iconType.iconClassName());
Constructor<?> constructor = clazz.getConstructor(Integer.TYPE);
m_iconType.icon((CachedBoardElement)constructor.newInstance(measuredHeight));
} catch(Exception e){
e.printStackTrace();
}
}
}
Just to clarify, CachedBoardElement is a bitmap cached vector canvas whose maximum dimension is set during construction; it has to be recreated if view exceeds the current maximum.

Creating a 5x5 box matrix

I have to complete a task in which I have to create a 5x5 box matrix. When user click any of the box, highlight that box a number inside. Can someone guide me how to do this?
I have to use Table Layout or some thing else.
Following is the box.
Square Board
I created this board using onMeasure event.
So you can create custom view which extends old one and override this event for creating square view or layout.
And then you can easily add this view to TableRow inside TableLayout.
If you need I can put all needed code or if you have another question let me know.
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int h = this.getMeasuredHeight();
int w = this.getMeasuredWidth();
int curSquareDim = Math.min(w, h);
// Inside a viewholder or other grid element,
// with dynamically added content that is not in the XML,
// height may be 0.
// In that case, use the other dimension.
if (curSquareDim == 0)
curSquareDim = Math.max(w, h);
if(curSquareDim < squareDim)
{
squareDim = curSquareDim;
}
Log.d("MyApp", "h "+h+"w "+w+"squareDim "+squareDim);
setMeasuredDimension(squareDim, squareDim);
}

use setLayoutParams() but it doesn't work

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.

how to add different buttons dynamically

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);
}

Categories

Resources