Dynamically change SlidingDrawer content width - android

I'm currently extending the SlidingDrawer class and want to resize the width of the content and can't quite get it to work right. Right now I'm setting the width of the whole view (handle and content) and it works for resizing purposes but also introduced a visual glitch when I move the handle it jumps to the new size for a split second and then returns to the handles position. I'm thinking that the problem is stemming from the onMeasure() or onLayout() calls that are happening in the base SlidingDrawer that are preventing the content area to be resized but am not completely sure.
I'm using getLayoutParams().width = newWidth; to resize the whole view but would like to use something like mContent.getLayoutParams().width = newWidth;.
The source code for the onMeasure() is here and for the onLayout() here.
Any insight into why the content area can't be resized would be great. Thanks!

So I finally figured it out if anyone else was having an issue with this. Basically when you want to resize the layout you need to measure() the layouts after the size change. Without the offsetLeftAndRight() call the handle will "jump" to the new size for a split second so setting the offset eliminates that "jump".
A simplified version of what I did was essentially:
public void resize() {
int previousPosition = mHandle.getLeft();
//Set the new size of the content area
mContent.getLayoutParams().width = width;
//Measure the newly sized content area and adjust the layout
mContent.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(getBottom() - getTop(), MeasureSpec.EXACTLY));
mContent.layout(handleWidth + mTopOffset, 0, mTopOffset + handleWidth + content.getMeasuredWidth(), content.getMeasuredHeight());
/* Remeasure any other views that were resized also here */
//Not required but helps position the handle correctly
mHandle.offsetLeftAndRight(previousPosition);
}

Related

How to know LayoutParams of View before measurement?

I have custom view with child items, which are configureable via xml. However they are can be configurable in runtime via something like a Configuration class. After that I just notify parent view about changes and all is ok.BTW. My question in fact touches measurement: I can change child items size in runtime, but for the first launch I want to set to all of them size (width and height) accordingly to layout params defined in xml.
Maybe some code will add more clarification to you.
protected int getItemWidth() {
if (cell != null) {
int width = cell.getWidth();
return width == 0 ? <layout_width_defined_in_xml> : ScreenUtils.convertToDp(context, width);
} else {
return canvasWidth;
}
}
So, I want to know. Is possible to get layout params before measurement? And how to that?
The size defined in XML isn't always the right size the view should have when it's actually laid out. For example, a child of a LinearLayout may have android:layout_width="0", but might have nonzero width because of android:layout_weight. (There are other examples as well with other kinds of layouts.) Additionally, the values match_parent and wrap_content map to negative integer values in java code, which I don't think is helpful to you here.
If your custom view is interested in measuring and positioning child views, you should be overriding onMeasure() and onLayout(). If you aren't doing that or if you don't need to do that, getWidth() and getHeight() will tell you a view's actual size, and getMeasuredWidth() and getMeasuredHeight() will tell you the measured size (which can differ from the actual size). THe only caveat is that you have to wait for the first measure/layout before calling those 4 methods because otherwise those methods all return zero.
If you do want to inspect the layout parameters of a view, you can do
LayoutParams params = view.getLayoutParams();
int width = params.width;
int height = params.height;
As noted, either or both of those may be negative. You can compare them to LayoutParams.MATCH_PARENT and LayoutParams.WRAP_CONTENT to check for these cases, but again I'm not sure this is helpful to you if you aren't implementing onMeasure() and onLayout().
You can do this in callback ViewTreeObserver.OnGlobalLayoutListener. You can get view dimensions here and set size other views with these dimensions.

Android- Image Rendering

Is there a way the this problem can be fixed? I tried invalidate() but, it still displays the same problem. What happens is that, after opening the page/ the Activity, the images behaves like the one in Figure A. It only renders to my desired layout (Figure B) after scrolling it back and forth.
What I'm trying to do is set the width and heigth of the image during runtime. So this is also in relation to my previous questions : Images in my HorizontalListView changes it size randomly and ImageView dynamic width and height which received a very little help.
Any tips regarding this matter, please?
EDIT: btw, my classes are:
MyCustomAdapter (extends baseadapter, this calls the displayimage() from ImageLoader ),
MyActivity and
ImageLoader (this is where my image url are loaded, decoded, displayed asynchronously)
Im also confused as to where i will set the height and width of the imageView. For now, i set it at ImageLoader. It was okay. but i dont know if i did the right thing.
If you want to set the width and height manually at runtime, grab a reference to the ImageView's LayoutParams AFTER the View has been measured by the layout system. If you do this too early in the rendering phase, your view's width and height as well as its parent view and so on will be 0.
I have some code in an open source library that might help you. The process is two parts:
Set up an OnPreDrawListener attached to the ViewTreeObserver for your control. My example does this inside of a custom control, but you can do this in your activity as well.
Inside the onPreDraw method, your image and it's parent will now have their width and height values assigned to them. You can make your calculations and then set your width and/or height manually to the LayoutParams object of your view (don't forget to set it back).
Check out this example where I'm applying an aspect ratio to a custom ImageView just before it's rendered to the screen. I don't know if this exactly fits your use case, but this will demonstrate how to add an OnPreDrawListener to a ViewTreeObserver, removing it when you're done, and applying dynamic sizing to a View at runtime
https://github.com/aguynamedrich/beacon-utils/blob/master/Library/src/us/beacondigital/utils/RemoteImageView.java#L78
Here's a modified version that removes my particular resizing logic. It also grabs the ViewTreeObserver from the imageView, which is a more likely scenario if you're not implementing a custom control and you only want to do this in the Activity
private void initResizeLogic() {
final ViewTreeObserver obs = imageView.getViewTreeObserver();
obs.addOnPreDrawListener(new OnPreDrawListener() {
public boolean onPreDraw() {
dynamicResize();
obs.removeOnPreDrawListener(this);
return true;
}
});
}
protected void dynamicResize() {
ViewGroup.LayoutParams lp = imageView.getLayoutParams();
// resize logic goes here...
// imageView.getWidth() and imageView.getHeight() now return
// their initial layout values
lp.height = someCalculatedHeight;
lp.width = someCalculatedWidth;
imageView.setLayoutParams(lp);
}
}

How to make a curved onscreen keyboard for Android

I would like to make a curved on-screen keyboard for Android. I have examined the softkeyboard, and various other on screen keyboards on google code. None that I have found involve a keyboard shape that is anything other than a rectangle. Ideally, I would like to create a keyboard that consists of keys distributed across two semi-circles on opposite sides of the screen (i.e., imagine holding a tablet by the sides and being able to hit the keys with your thumbs).
Of the code I have examined, onscreen keyboards are created as views (usually extending KeyboardView) and appear as a continuous bar across the bottom of the screen. As an approximation to my goal, I have tried to alter code I found on google code (dotdash-keyboard-android) to only draw its keys in the lower left-hand corner and leave the lower-right hand corner transparent. I have been able to override onMeasure to affect the dimensions of the view (see below), but this only seems to alter the positions of the keys and not the positions of the container. In other words, there is still a black bar filling the bottom of the screen.
//Located within KeyboardView
#Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
this.setMeasuredDimension(200, 200);
}
Is what I want to do even possible? Is there a more correct term for this? Are there projects I can use as examples?
I have also tried to set the view's dimensions using this.setLayoutParams -- but, these calls seem to have no effect. I have also tried to use this.getParent to access the parent view (if one even exists) and change it's dimensions but this approach does not work (or, I'm just doing it wrong). Any help would be much appreciated.
Thank you
UPDATE: 12/21/2012 - I think I need to override the parent class's onDraw method. Looking here , it looks like KeyboardView's onDraw method draws to a canvas that is equal to the size of the screen using the following code:
final int width = Math.max(1, getWidth());
final int height = Math.max(1, getHeight());
mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBuffer);
I think I can override onDraw and draw whatever I want on the canvas.
UPDATE: 12/21/2012 - I've overridden onDraw and it's now clear that the keyboardView is the dimensions I'm setting it to (200x200). Using Hierarchyview, I can see that the keyboardview is inside a framelayout with the id InputArea. So, the horizontal bar that fills the entire width is this framelayout. But, I'm not creating it -- where does it come from and how can I alter its dimensions?
UPDATE: 12/22/2012 - After more testing, it seems like the behavior (dimensions) of a keyboardview are in-part determined by the activity that calls it. In the browser, I get the behavior I've been describing: the height of the browser window shrinks to accommodate a bar across the bottom of the screen that holds the keyboard, even if the width of the keyboard is less than the width of the screen. In the calendar app, the keyboard size appears as I have set it (as a square in the lower-left hand corner) with the calendar appearing unchanged beneath it. So, it seems impossible to reach my goal with most apps using this approach. An alternative approach might be to have the IME service create a popupwindow or dialog. One problem is that popupwindows need a parent view or anchor to attach to and I don't think it's possible to find the top-most view from the IME service. Perhaps I can create a transparent view over the current activity and place the popup on top of that?
UPDATE: 12/23/2012 - Progress. I've figured out how to display a popup from the keyboard IME. The next step is to figure out how to make the popups a little round/organic. Here's a screenshot of what I accomplished followed by source.
Source. The following method is in the service IME class and called by the child (of the service) view's onMeasure method so that the popups open at the same time the keyboard is drawn. I've set the dimensions of the keyboard to 1x1 so it isn't visible. The log statements are there to help me figure out how to position the popups.
public void initiatePopupWindow()
{
try {
WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
//display.getSize(p);
Log.i("dotdashkeyboard","initiatePopupWindow (from IME service)");
LayoutInflater inflater = (LayoutInflater) this.getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layoutLeft = inflater.inflate(R.layout.popup_layout_left,null);
View layoutRight = inflater.inflate(R.layout.popup_layout_right, null);
// create a 300px width and 470px height PopupWindow
int popupHeight = 300;
int popupWidth = 200;
if (popUpLeft == null) popUpLeft = new PopupWindow(layoutLeft, popupWidth, popupHeight, false);
if (popUpRight == null) popUpRight = new PopupWindow(layoutRight, popupWidth, popupHeight, false);
int ypos = 0;
int xposRight = 0;
if (display.getRotation() == Surface.ROTATION_0) {
ypos = -(dm.heightPixels / 2 + popupHeight/2);
xposRight = (dm.widthPixels - popupWidth);
Log.i("dotdashkeyboard","test rotation=normal");
} else if (display.getRotation() == Surface.ROTATION_90) {
ypos = -(dm.heightPixels / 2 + popupHeight/2)/2;
xposRight = (dm.widthPixels - popupWidth)*2-popupWidth;
Log.i("dotdashkeyboard","test rotation=90-degrees");
} else {
Log.i("dotdashkeyboard","test rotation=unknown=" + display.getRotation());
}
popUpLeft.showAtLocation(inputView, Gravity.NO_GRAVITY, 0, ypos);
popUpRight.showAtLocation(inputView, Gravity.NO_GRAVITY, xposRight, ypos);
Log.i("dotdashkeyboard","test created popup at ypos="+ypos + " xposRight=" + xposRight);
Log.i("dotdashkeyboard","test screenWidth=" + dm.widthPixels + " screenHeight=" + dm.heightPixels);
Button cancelButton = (Button) layoutLeft.findViewById(R.id.popup_cancel_button);
//cancelButton.setOnClickListener(inputView.cancel_button_click_listener);
cancelButton.setOnClickListener(cancel_button_click_listener);
} catch (Exception e) {
e.printStackTrace();
}
}
You seem to be on the right track here. As you've noted, most activities will shrink their views to provide space for the keyboard window, so if you want the calling activity to fill the screen you need to use a secondary window, such as a PopupWindow. You can set the dimensions of your main window to 0x0.
Have you tried calling popUpLeft.setBackgroundDrawable(null) / popUpRight.setBackgroundDrawable(null)? This should remove the semi-transparent background and show only whatever you draw on top.

Setting LayoutParams doesn't get applied quick enough

I currently have this UI structure:
Activity (ACT)
LinearLayout (LL)
HorizontalScrollView (HSV)
RelativeLayout (RL)
When I set the layout parameters of RL, they don't get applied until later, when it is too late.
var TotalWidth = GetNewWidth() // eg returns 1000...
var lp = this.LayoutRoot.LayoutParameters;
lp.Width = TotalWidth;
this.LayoutRoot.LayoutParameters = lp;
// this.LayoutRoot.LayoutParameters.Width == 1000
// this.LayoutRoot.Width == 0
this.LayoutRoot.RequestLayout();
this.LayoutRoot.ForceLayout();
this.RequestLayout();
this.ForceLayout();
// this.LayoutRoot.LayoutParameters.Width == 1000
// this.LayoutRoot.Width == 0
This is not a problem if I wasn't using the width, but I am :)
I want to do this:
this.ScrollTo(500, 0);
This has no effect as the total width of the HSV children is currently 0.
But, if I do this:
this.PostDelayed(() => this.ScrollTo((int) percent, 0), 500);
It works, after a 500ms delay.
The HSV is a derived class that manages its contents itself - just an empty RL that I use to set the width so that I can have a huge area for painting images. Sort of an massive scrolling canvas.
Also, what I am trying to do is in the HSV's OnSizeChanged overridden member. I want to make it so that if the control is re-sized, the control is scrolled to the same position. The width is dependent on the height (keeps aspect ratio)
You'll have to wait till your layout has been recalculated. When you call forceLayout layouting doesn't happen immediately, it just gets added to the queue in the UI thread. onLayout or onSizeChanged will be called for all involved views as soon as they get their new positions and sizes assigned. Nothing really you can do about that.

PopupWindow Width and Height

OK, so I am starting to get a hang of building Android apps, well at least as much a programmer can after a few days - I am proud of what I have learned so far.
Anyways, I want to force login on the main activity - this I am doing by fetching a SharedPrefernece and than checking if that piece of information is null and than getting a PopupWindow which holds the "login" fields and options.
This PopupWindow has a Flipper inside, which is fine and I got working fine when the certain options are choosen.
I am having problems displaying this PopupWindow to just be the size of the content (wrap_content) as when I set the PopupWindow.setAtLocation()
Now, here is what I have been trying to do to get the size of the popup - as mentioned a few times on here:
popup.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
final PopupWindow pw = new PopupWindow(popup,popup.getMeasuredHeight(),popup.getMeasuredWidth(), true);
Note that popup is the inflator of the actual layout of the Popup, pw is the actual PopupWindow object.
Now, I want to get the actual size of just the popup window (so that way it isn't streched out over the page, but rather just in the center looking like a normal popup should.
Also, with the ViewFlipper. I want it to update the size of the popup when it switch pages (so the page should be sizing up and down per page) is there a way to make this work as well? I tried pw.update() but that didn't work out very well.
You want to measure the actual layout you will be adding to your PopupWindow. I just solved a similar problem of putting a ListView inside a PopupWindow. The trick is to override onMeasure() in your View. Remeasure that ViewFlipper everytime it changes.
#Override
public void onMeasure(int x, int y)
{
super.onMeasure(x, y);
if(x == View.MeasureSpec.UNSPECIFIED && y == View.MeasureSpec.UNSPECIFIED)
{
//measure your child views here
//tell the view it has been measured
this.setMeasuredDimension(width, height);
}
}

Categories

Resources