Add Image Thumbnails to a Layout in a Grid

How to show image and video as thumbnail in grid view?

Yes..

I got the answer after a long experiment: here it is:

public class GridGallery extends Activity
{
ArrayList<String>list;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.grid_gallery);
DataModel dbModel = new DataModel(this);
list = dbModel.selectAll();

GridView sdcardImages = (GridView) findViewById(R.id.sdcard);
sdcardImages.setAdapter(new ImageAdapter(this));
}

/**
* Adapter for our image files.
*/
private class ImageAdapter extends BaseAdapter {

private final Context context;
public ImageAdapter(Context localContext) {
context = localContext;
}

public int getCount()
{
return list.size();
}
public Object getItem(int position)
{
return position;
}
public long getItemId(int position)
{
return position;
}

public View getView(int position, View convertView, ViewGroup parent)
{
ImageView picturesView;
if (convertView == null) {
picturesView = new ImageView(context);
if(list.get(position).contains(".jpg"))
{
bitmap = BitmapFactory.decodeFile(list.get(position)); //Creation of Thumbnail of image
}
else if(list.get(position).contains(".mp4"))
{
bitmap = ThumbnailUtils.createVideoThumbnail(list.get(position), 0); //Creation of Thumbnail of video
}
picturesView.setScaleType(ImageView.ScaleType.FIT_CENTER);
picturesView.setPadding(8, 8, 8, 8);
picturesView.setLayoutParams(new GridView.LayoutParams(100, 100));
}
else
{
picturesView = (ImageView)convertView;
}
return picturesView;
}
}

}

This works fine for me

How to layout a 'grid' of images in the center of the screen

I figured this shouldn't be too hard using a custom view, which should be an interesting exercise. This is my first custom view; feedback is welcome!

Limitations

  • AspectGrid completely ignores the size that its children want to be. For your purposes, this appears to be okay. If something fancier is needed, onMeasure needs a lot of extra work.
  • The size that is suggested by AspectGrid's parent is used without a second thought. This is related to the previous issue.

Screenshots

Landscape screenshot http://tinypic.com/images/404.gif

Portrait screenshot

How it works

A main parameter is the number of columns. The number of rows is computed automatically, because we know the number of children. Another main parameter is the aspect ratio we want to use for the children (set to 1 for squares).

In onLayout, we receive the final size of the grid, so we can compute the maximum width and height of the children.

We then check this against the aspect ratio. If the children are too tall, we make them shorter (as in the portrait example). If they are too wide, we make them narrower (as in the landscape example).

That's all there is to it; the rest is just plumbing.

The code

com/photogrid/AspectGrid.java: The actual ViewGroup class

package com.photogrid;

import android.content.Context;

public class AspectGrid extends ViewGroup {

private int mNumColumns = 1;
private int mHorizontalSpacing = 0;
private int mVerticalSpacing = 0;
private float mChildAspectRatio = 1.0f;

public AspectGrid(Context context) {
super(context);
}

public AspectGrid(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public AspectGrid(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

try {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AspectGrid);

setNumColumns(a.getInt(R.styleable.AspectGrid_numColumns, mNumColumns));
setHorizontalSpacing(a.getDimensionPixelSize(R.styleable.AspectGrid_horizontalSpacing, mHorizontalSpacing));
setVerticalSpacing(a.getDimensionPixelSize(R.styleable.AspectGrid_verticalSpacing, mVerticalSpacing));
setChildAspectRatio(a.getFloat(R.styleable.AspectGrid_childAspectRatio, mChildAspectRatio));

a.recycle();
} catch (RuntimeException ex) {
throw ex;
}
}

public int getNumColumns() {
return mNumColumns;
}

public void setNumColumns(int numColumns) {
if (numColumns < 1)
throw new IllegalArgumentException("numColumns must be at least 1");
if (numColumns != mNumColumns) {
mNumColumns = numColumns;
requestLayout();
}
}

public int getHorizontalSpacing() {
return mHorizontalSpacing;
}

public void setHorizontalSpacing(int horizontalSpacing) {
mHorizontalSpacing = horizontalSpacing;
}

public int getVerticalSpacing() {
return mVerticalSpacing;
}

public void setVerticalSpacing(int verticalSpacing) {
mVerticalSpacing = verticalSpacing;
}

public float getChildAspectRatio() {
return mChildAspectRatio;
}

public void setChildAspectRatio(float childAspectRatio) {
if (childAspectRatio <= 0)
throw new IllegalArgumentException("childAspectRatio must be positive");
if (childAspectRatio != mChildAspectRatio) {
mChildAspectRatio = childAspectRatio;
requestLayout();
}
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int measuredWidth = widthSize;
int measuredHeight = heightSize;
int width = Math.max(measuredWidth, getSuggestedMinimumWidth());
int height = Math.max(measuredHeight, getSuggestedMinimumHeight());
setMeasuredDimension(width, height);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
if (childCount <= 0)
return;

int innerWidth = r - l - getPaddingLeft() - getPaddingRight();
int innerHeight = b - t - getPaddingBottom() - getPaddingTop();
int numRows = (childCount + mNumColumns - 1) / mNumColumns;

int leftEdge = getPaddingLeft();
int topEdge = getPaddingTop();
int horizontalStride = (innerWidth + mHorizontalSpacing) / mNumColumns;
int verticalStride = (innerHeight + mVerticalSpacing) / numRows;
int childWidth = horizontalStride - mHorizontalSpacing;
int childHeight = verticalStride - mVerticalSpacing;

if (childHeight * mChildAspectRatio > childWidth) {
childHeight = (int)(childWidth / mChildAspectRatio);
verticalStride = childHeight + mVerticalSpacing;
topEdge = (innerHeight + mVerticalSpacing - numRows * verticalStride) / 2;
} else {
childWidth = (int)(childHeight * mChildAspectRatio);
horizontalStride = childHeight + mHorizontalSpacing;
leftEdge = (innerWidth + mHorizontalSpacing - mNumColumns * horizontalStride) / 2;
}

for (int i = 0; i < childCount; ++i) {
View child = getChildAt(i);
int row = i / mNumColumns;
int column = i % mNumColumns;
int left = leftEdge + column * horizontalStride;
int top = topEdge + row * verticalStride;
child.layout(
left,
top,
left + childWidth,
top + childHeight);
}
}

}

res/values/attrs.xml: Declaration of attributes for use in the XML

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="AspectGrid">
<attr name="numColumns" format="integer"/>
<attr name="horizontalSpacing" format="dimension"/>
<attr name="verticalSpacing" format="dimension"/>
<attr name="childAspectRatio" format="float"/>
</declare-styleable>
</resources>

res/layout/main.xml: The example used in the screenshots above

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.photogrid"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.photogrid.AspectGrid
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp"
app:numColumns="3"
app:horizontalSpacing="5dp"
app:verticalSpacing="5dp"
app:childAspectRatio="1.0"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffcccc"
android:text="Item 1"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ccffcc"
android:text="Item 2"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ccccff"
android:text="Item 3"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffffcc"
android:text="Item 4"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffccff"
android:text="Item 5"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ccffff"
android:text="Item 6"
/>
</com.photogrid.AspectGrid>
</LinearLayout>

CSS simple thumbnail grid shows weird spacing

The broken formatting is because some images are taller in the second example. The taller images take up more space and because the thumbnails have float:left set, they flow around the taller one. This explains why the first example works, since they all have the same height.

That said, float:left is also overriding the display:inline-block with display:block - see css display property when a float is applied

If you remove float:left or set the height of the .thumb class the thumbnails will also line up as expected.

Responsive image gallery using CSS flexbox or grid-layout

Using flex capabilities should be sufficient for your task. Be aware of partial support in IE11: http://caniuse.com/#feat=flexbox.

Put these styles on your container:

.gallery {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
justify-content: space-between;
}

Styles for wrappers:

.gallery a {
flex-grow: 1;
flex-basis: 125px;
max-width: 300px;
margin: 5px;
}

Styles for images:

.gallery img {
height: 100%;
width: 100%;
}

Space between images can be simply defined using margin.
In order to preserve image ratio you can use for example links (<a>) as a wrappers for images (<img>).

Furthermore, in order to prevent enlarging images, you can apply flex-grow, flex-basis and max-width attributes on anchors.
There was also a problem with stretching images in the last row - hack for that is to put n - 1 (where n is number of images) empty items inside container.

Setting width and height to 100% on the images enforces them to grow automatically up to the width defined by max-width attribute, while maintaining aspect ratio.

Please check the working example:
FIDDLE

Fit full thumbnails in a masonry display

On the <img> tag that the thumbnails are displayed, object-fit: cover is applied.

Change that to

object-fit: contain;

And it will stay inside the container.

The replaced content is scaled to maintain its aspect ratio while fitting within the element’s content box. The entire object is made to fill the box, while preserving its aspect ratio, so the object will be "letterboxed" if its aspect ratio does not match the aspect ratio of the box.

MDN Docs

Here's what happens:

BEFORE
Image is cropped

AFTER
Image looks fine

Controlling the size of an image within a CSS Grid layout

You have two different problems here.

I'll address the first one, which is to contain the image in its container. You almost had it.

Your code:

.photo > img {
object-fit: cover;
width: 100%;
}

You just needed to specify a maximum height on the image so it could not overflow the container:

.photo > img {
object-fit: cover;
width: 100%;
max-height: 100%;
}

JSFiddle demo


The second problem, which is to scale the size of the image container, and drag up the grid items below when the image gets smaller, is significantly more complex.

This isn't an issue relating to the image. In fact, you can remove the image altogether when trying to solve this problem.

This is an issue of dynamically sizing a grid item (the image container). Why would this grid item change size in relation to the image size, when its size is being controlled by grid-template-columns and grid-template-rows?

In addition, why would the bottom row of grid items (tag, album, rotate) follow the image container either up or down? They would have to exit their row.

Scaling the entire grid container wouldn't be a problem. But it seems like you only want to scale one grid item (the image container). It's tricky. You're probably looking at additional containers, auto values for lengths, and possibly scripting.

Here's what happens if you give the image rows an auto value: JSFiddle demo



Related Topics



Leave a reply



Submit