I've an implementation of a translucent to opaque ActionBar effect (Google Play's App Pages, Google Music) in an app I'm developing, I've made a ScrollView that notifies when the scrolling changes, my listener then calculates a ratio of opacity for the ActionBar relative to some max height. This is working great, but now I've implemented something like this in my scroll change listener.
float ratio = (float) Math.min(Math.max(t, 0), limitHeight) / limitHeight;
toolBarDrawableAlpha = (int) (ratio * 255);
toolBarTypeColorDrawable.setAlpha(toolBarDrawableAlpha);
int dexEntryPictureNewSize = Math
.max(dexEntryPictureMinSize, (int) ((1 - ratio / 2) * dexEntryPictureSize));
dexEntryPicture.getLayoutParams().height = dexEntryPictureNewSize;
dexEntryPicture.getLayoutParams().width = dexEntryPictureNewSize;
dexEntryPicture.requestLayout();
int dexEntryPictureNewBottomMargin = Math.max(dexEntryPictureMarginBottom,
Math.min(dexEntryPictureMaxMarginBottom,
(int) ((ratio * 1.5) * dexEntryPictureMaxMarginBottom)));
((FrameLayout.LayoutParams) dexEntryPicture
.getLayoutParams()).bottomMargin = dexEntryPictureNewBottomMargin;
dexEntryPicture.requestLayout();
int dexEntryNameNewTopPadding = Math.max(dexEntryNamePaddingTop,
Math.min(dexEntryNameMaxPaddingTop, (int) ((ratio) * dexEntryNameMaxPaddingTop)));
dexEntryName.setPadding(dexEntryName.getPaddingLeft(), dexEntryNameNewTopPadding,
dexEntryName.getPaddingRight(), dexEntryName.getPaddingBottom());
As you can see, I use the same ratio calculated for the opacity to change some height, width, margin, padding values in my views, this is working great too, but I've been wondering if there's a way to accomplish this in a more efficient way or if I'm making some horrible performance mistakes, I only have one device to test this and it's running fairly snappy, really smooth but would like to know if lesser devices won't whine about it.
If you are wondering what's the final effect that I'm going for, it's some sort of parallax (?) scrolling where a fixed background view has another centered view in it (like a profile image circle) and this centered view shrink and expands relative to the opacity ratio, also some paddings are moved so the view seems to be pushed to top.
Related
I'm trying to create the control, that allows moving view with the finger. To do this, I follow recommendations from this post.
But presented method needs some modification, to prevent my view from being moved beyond the screen. I found out how to get maxY and maxY coordinates - for my Samsung Galaxy A6 it's 1080x1920. But the problem is, that my maxY is deep beyond the visible bottom edge of the device.
So my control almost disappears, when reaches Y about 1650. What 300 more pixels go for. I can suppose, that this is NavigationBar height + my control view height, but this also doesn't place my control as expected.
I define max coordinates with this method.
private void setMaxCoordinates(int viewWidth, int viewHeight) {
Display display = getWindowManager().getDefaultDisplay();
Point displaySize = new Point();
display.getSize(displaySize);
maxX = displaySize.x - viewWidth;
maxY = displaySize.y - viewHeight;
}
Please, help me to define the correct formula to detect bottom edge coordinate.
I've found a solution. First of all I was wrong with getting bottom coordinates with the help of WindowManager. This just gives you height of your screen in pixels, that is not related to the container. So to detect your bottom coordinate this way, you have to consider:
StatusBar height.
NavigationBar height.
Height of all views that lokated higher, then your container.
Height of your own control view (ImageView in my case).
So formula will look like this.
maxY = windowHeight - (statusBarHeight + navBarheight + allUpperViewsHeight + yourViewHeight)
The correct way to define the bootom of your container, is to get the container's height and deduct your control height.
maxY = containerHeight - yourViewHeight;
Is there a way to manually scale a view while making sure the position translates properly? The only way I can scale it right now is to update the LayoutParams by essentially multiplying the width and height my a scale factor. This works okay but I'm not sure about how to translate the position properly. Further, I want to scale the view with the pivot being the center as well. I'm actually able to perform the behavior I want with by using the ScaleAnimation like so,
ScaleAnimation anim = new ScaleAnimation(fromX, toX,
fromY, toY, ScaleAnimation.RELATIVE_TO_SELF,
0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
However, the actual view's bounds are not adjusted. The majority of scaling implementations and help always deal with an ImageView so I figured it's worth asking for a View only. I'd be greatly appreciative if any has any ideas on this matter.
It sounds like you want to scale a View, but scale it from the center of the View. This is tricky because the View is positioned based on it's top and left offset from the top left corner of it's parent.
So -- if you have a view that is 100px from the top, and 100px from the left, and the size is 50px by 50px - and you want to scale it by +10%:
width = 50px * 1.1 = 55px;
height = 50px * 1.1 = 55px;
but those extra 5px will be added to the right and bottom of the View, right? so we need to change the offset of the View to adjust:
top = 100px - ((100px * .1) / 2) = 97.5px
(same for the left, obviously).
using this pattern, you can scale the view in a way that keeps the center point consistent.
I am programming an app that shows a lot of verses/poems so text wrapping is not an option for me. I would like the text to be as big as possible (doesn't have to recalculate each time a new text is shown, should just allow the biggest text to fit on the screen) without extending screen size. It should not visually scale or take longer for the text to appear.
Is this possible?
Thanks
I would suggest a simple search for the best point size using the largest text that you need to fit. This can be done once at start-up. (Well, maybe twice—once for landscape and once for portrait). The first step would be to initialize a Paint with the typeface you want to use for display. Then call this function to
public void setBestTextSize(String longestText, int targetWidth, Paint paint) {
float size = paint.getTextSize(); // initial size
float w = paint.meaasureText(longestText);
size = targetWidth * size / w;
paint.setTextSize(size);
// test if we overshot
w = paint.measureText(longestText);
while (w > targetWidth) {
--size;
paint.setTextSize(size);
w = paint.measureText(longestText);
}
A binary search in the loop might be theoretically faster, but this should do pretty well since text width does scale approximately linearly with font size and the first step before the loop should get the size pretty close.
An alternative approach, which deals nicely with view size changes, is shown in this thread.
I'm making an app widget for Android, which due to being composed of custom elements such as graphs, must be rendered as a bitmap.
However, I've run into a few snags in the process.
1) Is there any way to find the maximum available space for an app widget? (OR: Is it possible to calculate the dimensions correctly for the minimum space available in WVGA (or similar wide) cases?
I don't know how to calculate the maximum available space for an app widget. With a conventional app widget it is possible to fill_parent, and all the space will be used. However, when rendering the widget as a bitmap, and to avoid stretching, the correct dimensions must be calculated. The documentation outlines how to calculate the minimum dimensions, but for cases such as WVGA, there will be unused space in landscape mode - causing the widget to look shorter than other widgets which stretch naturally.
float density = getResources().getDisplayMetrics().density;
int cx = ((int)Math.ceil(appWidgetInfo.minWidth / density) + 2) / 74;
int cy = ((int)Math.ceil(appWidgetInfo.minHeight / density) + 2) / 74;
int portraitWidth = (int)Math.round((80.0f * cx - 2.0f) * density);
int portraitHeight = (int)Math.round((100.0f * cy - 2.0f) * density);
int landscapeWidth = (int)Math.round((106.0f * cx - 2.0f) * density);
int landscapeHeight = (int)Math.round((74.0f * cy - 2.0f) * density);
Calculating cx and cy gives the number of horizontal and vertical cells. Subtracting - 2 from the calculated dpi (e.g. 74 * cy - 2) is to avoid cases where the resulting number of pixels is rounded down. (For example in landscape mode on Nexus One, the height is 110, not 111 (74 * 1.5).
2) When assigning a bitmap to an ImageView which is used as part of the RemoteViews to view the image, there are 2 methods:
2.1) By using setImageViewUri, and saving the bitmap to a PNG file. The image is then served using an openFile() implementation in a ContentProvider:
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException
// Code to set up imageFileName
File file = new File(getContext().getCacheDir(), imageFileName);
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
This works, and it's the approach I'm currently using. However, if I set the scaleType of the ImageView to "center", which by the documentation is supposed to "Center the image in the view, but perform no scaling.", the image is incorrectly scaled. Setting the density of the bitmap to DENSITY_NONE or getResources().getDisplayMetrics().densityDpi doesn't make any difference when saving the bitmap to PNG, it seems to be ignored when the file is loaded by the ImageView. The result is that the image is scaled down, due to some dpi issue. This seems to describe the case:
http://code.google.com/p/android/issues/detail?id=6957&can=1&q=widget%20size&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars
Because it is not possible to use scaleType:center, the only way I've found to work is to set the layout_width and layout_height of the ImageView statically to a given number of dpis, then rendering the bitmap to the same dpi. This requires the use of scaleType:fitXY. This approach works, but it is a very static setup - and it will not work for resizable 3.1 app widgets (I haven't tested this yet, but unless onUpdate() is called on each resize, this is true).
Is there any way to load an image to an ImageView unscaled, or is this impossible due to a bug in the framework?
2.1) By using setImageViewBitmap directly. Using this method with the Bitmap.DENSITY_NONE setting on the bitmap, the image can be shown without scaling correctly. The problem with this approach is that there is a limitation to how large images can be set through the IPC mechanism:
http://groups.google.com/group/android-developers/browse_thread/thread/e8d84920b999291f/d12eb1d0eaca93ac#01d5c89e5e7b4060
(not allowed more links)http://groups.google.com/group/android-developers/browse_thread/thread/b11550601e6b1dd3#4bef4fa8908f7e6a
I attempting a bit of a hack to get past this issue, by splitting the widget into a matrix of images which could be set in 100x100 pixel blocks. This did allow for larger widgets to work, but ended up being very heavy and failed on large widgets (4x4).
Sorry for a very long post. I've tried to explain a few of the different issues when attempting to use a bitmap rendered app widget. If anyone has attempted the same and have found any more solutions to these issues, or have any helpful comments, this will be highly appreciated.
An approach that worked for us for a similar situation was to generate our graph as a 9-patch png, with the actual graph part as the scalable central rectangle, and the caption text and indication icons (which we did not want all stretched out of shape) and border effects, placed on the outer rectangles of the image.
I'm trying to get a background in a live wallpaper to behave like a regular wallpaper with regard to scrolling when the user changes homescreens. I know the method required for this is onOffestsChanged, but I can't seem to get it working.
Does anyone have advice or a code snippet to get this working?
Have your engine implement onOffsetsChanged. The xOffset variable is a float value from 0 to 1 with 0 being the leftmost screen and 1 being the rightmost. Use the width of the screen (from onSurfaceChanged) and the width of your image to determine the left x coordinate with which to draw your image.
(screenWidth - yourImageWidth) * (1 - xOffset);
This should work with both screenWidth > yourImageWidth and screenWidth < yourImageWidth.