Gridview in Android, with dynamic width & height of blocks - android

I want to create a Gridview just like a GooglePlay. The only difference in our case is the size of width & height should be the based on the popularity.
For example, If I have a array list of categories along with number of popularity. The order of category is fixed, so it will appear on the grid in same order, the only thing is going to change its width and height.
So, If my grid has 2 columns, then blocks should be adjusted automatically.
I have seen the demo of stagerred view but unable to replicate as per my requirement. So If any one has any suggestion will be appreciated.
Thanks

If you want to adjust the width of your block automatically you have to get Display Size.
This code Works for me.
public static void calculateSize(Activity ac) {
DisplayMetrics dMetrics = new DisplayMetrics();
ac.getWindowManager().getDefaultDisplay().getMetrics(dMetrics);
float density = dMetrics.density;
int w = Math.round(dMetrics.widthPixels / density);
int h = Math.round(dMetrics.heightPixels / density);
YourBlock.WIDTH = Math.round((w - (YourBlock.PADDING * (YourBlock.COLUMN + 1))) / YourBlock.COLUMN);
YourBlock.HEIGHT = Math.round(YourBlock.WIDTH * YourBlock.RATIO);
}
In this case:
public static final int YouBlock.COLUMN = 2;
public static float YourBlock.RATIO = 2.0f/3;
public static int YouBlock.PADDING = 4;
Example:
Each item will automacticaly adjusts width with each device (each size)

Related

Use x/y coordinates to set bounds of a moving particle

I have two bitmaps that I draw onto the center of a canvas:
One is only a background, it's a spirit level in top view which doesnt move. The second one is a bitmap looking like a "air bubble". When the user tilts the phone, the sensors read the tilt and the air bubble moves according to the sensor values along the x-axis. However, I need to make sure that the air bubble doesnt move too far, e.g out of the background-bitmap.
So I tried to which x coordinate the bubble can travel to,
before I have to set xPos = xPos -1 using trial and error
This works fine on my device.
To clarify: On my phone, the air bubble could move to the coordinate x = 50 from the middle of the screen. This would be the point, where the bitmap is at the very left of the background spirit level.
On a larger phone, the position x = 50 is too far left, and therefore looking like the air bubble travelled out of the water level.
Now I've tried following:
I calculated the area in % in which the air bubble can move. Let's say that
is 70% of the entire width of the bitmap. So I tried to calculate the two x boundary values:
leftBoundary = XmiddlePoint - (backgroundBitmap.getWidth() * 0.35);
rightBoundary = XmiddlePoint + (backgroundBitmap.getWidth() * 0.35);
...which doesnt work when testing with different screen sizes :(
Is it possible to compensate for different screen sizes and densities using absolute coordinates or do I have to rethink my idea?
If you need any information that I forgot about, please let me know. If this question has already been answered, I would appreciate a link :) Thanks in advance!
Edit:
I load my bitmaps like this:
private Bitmap backgroundBitmap;
private static final int BITMAP_WIDTH = 1898;
private static final int BITMAP_HEIGHT = 438;
public class SimulationView extends View implements SensorEventListener{
public SimulationView(Context context){
Bitmap map = BitmapFactory.decodeResource(getResources, R.mipmap.backgroundImage);
backgroundBitmap = Bitmap.createScaledBitmap(map, BITMAP_WIDTH, BITMAP_HEIGHT, true;
}
and draw it like this:
protected void onDraw(Canvas canvas){
canvas.drawBitmap(backgroundBitmap, XmiddlePoint - BITMAP_WIDTH / 2, YmiddlePont - BITMAP_HEIGHT / 2, null);
}
backgroundBitmap.getWidth() and getHeight() prints out the correct sizes.
Calculating like mentioned above would return following boundaries:
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
//which prints out width = 2392
xMiddlePoint = width / 2;
// = 1196
leftBoundary = xMiddlePoint - (BITMAP.getWidth()* 0.35);
// = 531,7
However, when I use trial and error, the right x coordinate seems to be at around 700.
I've come across a great explanation on how to fix my issue here.
As user AgentKnopf explained, you have to scale coordinates or bitmaps like this:
X = (targetScreenWidth / defaultScreenWidth) * defaultXCoordinate
Y = (targetScreenHeight / defaultScreenHeight) * defaultYCoordinate
which, in my case, translates to:
int defaultScreenWidth = 1920;
int defaultXCoordinate = 333;
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
displayWidth = displayMetrics.widthPixels;
leftBoundary = (displayWidth / defaultScreenWidth) * defaultXCoordinates

RelativeLayout for Android?

So I have been experimenting today with making an Android Application, but I have tried the LineairLayout to make a welcom screen for my application, but I cannot get it right..
So I tried RelativeLayout and I saw I can move my ImageViews and buttons to everywhere. So my question is if I will move the items to places like center, bottom left and bottom right. Would this be a problem or all phones since not all phones have the same dimensions?
public class WelcomeActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.welcome_screen_relative);
final ImageView logo = (ImageView) findViewById(R.id.myImageView);
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
int displayHeight = metrics.heightPixels;
int displayWidth = metrics.widthPixels;
float scaledDensity = metrics.scaledDensity;
BitmapFactory.Options dimensions = new BitmapFactory.Options();
dimensions.inJustDecodeBounds = true;
Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.log, dimensions);
int imageHeight = dimensions.outHeight;
int imageWidth = dimensions.outWidth;
float percentageToMoveViewDown = (float) 20.0;
float viewY_float = (float) ((displayHeight / 100.0) * percentageToMoveViewDown);
int viewY_int = Math.round(viewY_float);
RelativeLayout.LayoutParams view_Layout_params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
view_Layout_params.topMargin = viewY_int;
logo.setLayoutParams(view_Layout_params);
logo.getLayoutParams().height = imageHeight;
logo.getLayoutParams().width = imageWidth;
}
Thats depends. If you give objects a fixed size of course it will. for dp/dpi make sure to test it in Emu or real devices. You can also create density and orientation specific layout to support many screens. Consider that there are not only changes in size but also aspect ration and resolution and DPI.
For most apps RelativeLayout is might be the right approach.
You can read an excelent article about it here: http://developer.android.com/guide/practices/screens_support.html
If the items have fixed sizes you always will have trouble with some phones. For the big ones it may be too small, for the small ones too big...
In my experience Androids small/normal/large screens won't help you much for configuring, since the differences are just too big.
If you want to make sure everything sits where it belongs to, you could get the device metrics. That way you don't even need to rely on center, but you can work with percentages to place everything where you want it to be. Plus you can set the sizes in percentage, which is great. Like you could say I want a button thats width is 50% of the screen, no matter how large the screen is. Its more work (maybe even overkill), but I really like that approach. Once you figured it out its basically just a bit copy paste at the start of your classes.
Example:
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
int displayHeight = metrics.heightPixels;
int displayWidth = metrics.widthPixels;
float scaledDensity = metrics.scaledDensity;
//move some View 20% down:
float percentageToMoveViewDown = (float) 20.0;
float viewY_float = (float) ((displayHeight / 100.0) * percentageToMoveViewDown);
int viewY_int = Math.round(viewY_float);
RelativeLayout.LayoutParams view_Layout_params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
view_Layout_params.topMargin = viewY_int;
view.setLayoutParams(view_Layout_params);
//even works with text size for a TextView:
float percentageToResizeTextViewTextSize = (float) 3.1;
float textViewTextSize_float = (float) ((displayHeight / 100.0) * percentageToResizeTextViewTextSize);
int textViewTextSize_int = Math.round(textViewTextSize_float / scaledDensity);
textView.setTextSize(textViewTextSize_int);
Just a side note for the overkill thing: This should be only necessary if you want to support small devices (they mostly run something like android 2.3, but still are sold as budget phones) and big devices as well, but the trouble with the big ones is not as big as the trouble with the small ones. I personally rather put more effort in it than less, you never know.
Edit: ImageView by code
The easiest way is to do it hybridly, using xml and code. Note that you will have to change width and height if you set it to 0 in xml like in the following example.
Just place it in the xml where you would anyways, somewhere in your RelativeLayout.
In your xml:
<ImageView
android:id="#+id/myImageView"
android:layout_width="0dp"
android:layout_height="0dp" />
In your Code:
ImageView myImageView = (ImageView)findViewById(R.id.myImageView);
You now can work with that myImageView as I did it with view and textView. You can even set the image right here in code.
This imageView with the size of 0,0 is now placed where it would have been before. Now you could set the width to like 50% of the screenwidth and the height to...lets say 40% of the screen height. Then You would need to place it. If you want to center it you know that there must be 25% of the screen on each side, so you can add 25% as left and right margin.
Edit 2: maintain original imagesize
If you want to keep the original size of a image in your drawables, you can get its width and height like this:
BitmapFactory.Options dimensions = new BitmapFactory.Options();
dimensions.inJustDecodeBounds = true;
Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.yourImageName, dimensions);
int imageHeight = dimensions.outHeight;
int imageWidth = dimensions.outWidth;
Now that you have that, you could use it to calculate the aspect ratio to keep it.
Now since you know the devices width and height, you can easily calculate how much of the screen this image will need.(imageheight/screenheight*100 = your percentage of the screen you want the imageviews height to be). So the Height you set to the imageview would be displayHeight / 100 * (imageHeight / displayHeight * 100).
How to place that?
Now if you take a Screenheight of 100 and a imageheight of 80 you get 80%. You would now take this percentage and divide it from 100. Divide that /2 and you know how much space you would have as top and bottom margins if you wanted it to be placed in the middle of the screen (you would have to do the same for width).
Caution: If you don't want it to be relative to the screensize but the original size, that percentage approach is kind of pointless. If you do to your image what I just described, it may still be too big for small devices and too small for big ones. Instead you could think about what percentage would look good in proportion to the rest of the stuff on the screen and resize it to that, since it would have that relative size on all devices.
Edit 3:
Since you load the image in original size, it will be small on big devices if it is a small image.
//first you need to know your aspect ratio.
float ratio = imageWidth / imageHeight;
//lets say you wanted the image to be 50% of the screen:
float percentageToResizeImageTo = (float) 50.0;
float imageX_float = (float) ((displayHeight / 100.0) * percentageToResizeImageTo);
int imageX_int = Math.round(imageX_float);
//now you know how much 50% of the screen is
imageWidth = imageX_int;
imageHeight = imageWidth * ratio;
RelativeLayout.LayoutParams view_Layout_params = new RelativeLayout.LayoutParams(imageHeight, imageWidth);
view_Layout_params.topMargin = viewY_int;
logo.setLayoutParams(view_Layout_params);
logo.setScaleType(ImageView.ScaleType.Fit_XY);

android position view inside layout

My activity has a background image 1280x800 pixels. I set it using android:scaleType="centerCrop".
There's a flagstaff depicted on a background image and I need to position another image ("flag") above the flagstaff.
If device's screen dimension was exactly 1280x800, then "flag"'s position would be (850, 520). But screen size can vary and Android scales and shifts the background image accordingly to centerCrop flag. Hence I need to assign somehow scale and shift to "flag" image to make it placed nicely above the flagstaff.
I have examined ImageView.java and found that scaleType is used to set a private Matrix mDrawMatrix. But I have no read access to this field as it's private.
So, given
#Override
public void onGlobalLayout()
{
ImageView bg = ...;
ImageView flag = ...;
int bgImageWidth = 1280;
int bgImageHeight = 800;
int flagPosX = 850;
int flagPosY = 520;
// What should I do here to place flag nicely?
}
You can see the size of the screen (context.getResources().getDisplayMetrics().widthPixels, context.getResources().getDisplayMetrics().heightPixels;) and calculate what is visible from the image, for example you could do something like this (haven't really tested it but you should get the idea):
private void placeFlag(Context context) {
ImageView bg = new ImageView(context);
ImageView flag = new ImageView(context);
int bgImageWidth = 1280;
int bgImageHeight = 800;
int flagPosX = 850;
int flagPosY = 520;
int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
int screenHeight = context.getResources().getDisplayMetrics().heightPixels;
//calculate the proportions between the width of the bg and the screen
double widthScale = (double) bgImageWidth / (double) screenWidth;
double heightScale = (double) bgImageHeight / (double) screenHeight;
//see the real scale used, it will be the maximum between the 2 values because you are using crop
double realScale = Math.max(widthScale, heightScale);
//calculate the position for the flag
int flagRealX = (int) (flagPosX * realScale);
int flagRealY = (int) (flagPosY * realScale);
}
Also, you should be doing that in the method onGlobalLayout, you could do this in onCreate() or inside the constructor if you want a custom view.
you can use a LayerDrawable approach here to make one drawable image in it's static (In which you can set background image and icon on top of background image in custom_drawable.xml and can use that file as single drawable in activity).For reference go to android developer. Otherwise
For scale issue according to different device resolution put images in different drawable folder and can also design layout different .

Android getting Bitmap dimensions depending on resolution of device?

I am currently trying to create a grid of multiple squares (Should be a minesweeper grid one day).
Now my solution of getting every mine a specific position is by using this loop:
for(int i = 0; i < cols; i++)
{
for(int j = 0; j < rows; j++)
{
mines[i][j].setCoordinates(xCounter, yCounter);
xCounter+=150;
}
yCounter+=150;
xCounter = 0;
}
now 150 should display the width or the height of one square because if one mine is on (0,0) the next should be on (minewidth,0) to fit nice and snuggly.
But now my question is, how can I be independend of this 150?
I got to this number by trying out which number makes the grid without spacing. But when I use a lower density device the images are smaller but the distance to each other would remain 150 pixels.
Now how can I get something generally to be independent of this constant?
First I thought I could use the .getWidth() method to get the width of the Bitmap that is stored in my "Mine" class. But this didn't work out so...
Has anyone got an idea to create a grid independent of which density the device has?
You can try to get the width and height of your screen(in dp) and use a factor to determine the width for each grid..
You can add the below code in your application class,
DisplayMetrics displayMetrics = applicationContext.getResources().getDisplayMetrics();
int absoluteWidthPx = displayMetrics.widthInPixels;
int abduluteHeightPx = displayMetrics.heightInPixels;
float scalingFactor = displayMetrics.density;
Now you can find the dp values for width and height of the screen,
/** pixels = densityIndependantPixels * (dotsPerInch/160), based on this **/
int dpWidth = (int)(absoluteWidthPx /scalingFactor);
int dpHeight = (int)(abduluteHeightPx /scalingFactor);
Once you have the width and height in dp you can try using some factors to determine the width for each grid.

Scale a view and its layered subviews relatively

(This is somewhat a follow-up on Android: How do you scale multiple views together?)
My task is to port an iPhone / iPad app on Android that consists of a simple image view on which animations are layered on top in absolute positions. While this sounds rather easy on iOS where you only have to target a few possible screen sizes, it gets rather messy with Android.
My current setup is this: A RelativeLayout in which I place my main (background) image on left = 0, top = 0 and multiple ViewFlipper instances used as "animation containers" that are positioned relatively to the upper left corner of the parent layout instance.
This approach has two basic problems:
The positioned "animations" are mis-positioned as soon as the actual size of the layout does not match the size of the main background image.
The positioned "animations" are also mis-sized, because since they usually have "enough space" around themselves, Android doesn't scale them to fit into the RelativeLayout (nor would it scale them relatively to the original background.
Since the animations itself must be interactive, its not a solution to place and position all of the animations on a transparent layer that has the same size as the main (background) image, as they'd overlap each other and only the upper-most would be interactive at all.
I thought of different solutions:
To get the the scale factor of the main image, I could retrieve its measuredWidth and measuredHeight and set this into relation of the original width and height of the view. Then I'd use this scale factor for custom positioning and eventually custom scaling. But, apparently the measuredWidth/-Height properties are only set during the onMeasure() call and this is called after the component tree was built, so I don't know if this solution is feasible at all.
Implement my own layout manager and scale / position the views accordingly. I had a look at the implementation of RelativeLayout, but have to admit that the onMeasure() method scares me a bit.
What would you do in my case? Is there anything I haven't yet taken into account?
Thanks in advance.
Well, answering my own question - here is the way I resolved the issue:
I placed the background image on the top of my ImageView with ImageView.setScaleType(ScaleType.FIT_START)
I calculated the scale factor of my background image like so:
WindowManager mgr = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
mgr.getDefaultDisplay().getMetrics(metrics);
Drawable image = context.getResources().getDrawable(R.drawables.someImage);
float scale = metrics.widthPixels / (float) image.getIntrinsicWidth();
Finally, I used this scale in a custom ImageView class that loads the overlays to position and scale the view properly:
public class OverlayImage extends ImageView
{
private int imgWidth, imgHeight;
private final float scale;
public OverlayImage(Context context, int xPos, int yPos, float scale)
{
super(context);
this.scale = scale;
LayoutParams animParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
animParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
animParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
animParams.leftMargin = (int) (scale * xPos);
animParams.topMargin = (int) (scale * yPos);
setLayoutParams(animParams);
Drawable dr = context.getResources().getDrawable(R.id.someImage);
setBackgroundDrawable(dr);
imgWidth = dr.getIntrinsicWidth();
imgHeight = dr.getIntrinsicHeight();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
setMeasuredDimension((int) (scale * imgWidth),
(int) (scale * imgHeight));
}
}
I lately needed to do something similar, i also had to port a IPad app to android, the screen had many images that had to be in specific locations.
I solved this slightly differently, absolute layout, and run through all the views and set the coordinated and size of each.
//This gets the scale of the screen change:
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
Drawable image = getResources().getDrawable(R.drawable.background_image);
float scaleW = displaymetrics.widthPixels / (float)image.getIntrinsicWidth();
float scaleH = displaymetrics.heightPixels / (float)image.getIntrinsicHeight();
//And this scales each view accordingly:
for(int i = 0; i < mainLayout.getChildCount(); i++)
{
View v = mainLayout.getChildAt(i);
v.setLayoutParams(new AbsoluteLayout.LayoutParams(
Math.round(scaleW * v.getMeasuredWidth()),
Math.round(scaleH * v.getMeasuredHeight()),
Math.round(scaleW * ((AbsoluteLayout.LayoutParams)v.getLayoutParams()).x),
Math.round(scaleH * ((AbsoluteLayout.LayoutParams)v.getLayoutParams()).y)));
}

Categories

Resources