I programmatically created a GridView of images to be used inside a Dialog. If I set the columns to autofit, I always get exactly two columns, whatever the size of the images is and whatever the screen size is.
If I force the columns to a fixed number, then it works, but I cannot avoid the images to overlap on certain screen sizes, so an automatic management of the number of columns would be much better.
Even more, when I try to set streching, it simply does not show anything!
My ImageAdapter:
public class ImageAdapter extends BaseAdapter {
private Context mContext;
// Constructor
public ImageAdapter(Context c){
mContext = c;
}
public int getCount() {
return (IconC.Res.length);
}
public Object getItem(int position) {
return (IconC.Res[position]);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(mContext);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(new GridView.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(IconC.Res[position]);
return (imageView);
}
}
And the function that creates the Dialog (that is called elsewhere with a .show() )
private void createPopUp() {
d_icons = new Dialog(this);
GridView gv_icons = new GridView(this);
// gv_icons.setNumColumns(GridView.AUTO_FIT); // autofit disabled, shows ONLY TWO COLUMNS
gv_icons.setNumColumns(4); // instead, autofit forced, now it works, but I don't want it fixed!
gv_icons.setGravity(Gravity.CENTER);
gv_icons.setHorizontalSpacing(3);
gv_icons.setVerticalSpacing(1);
// gv_icons.setStretchMode(GridView.STRETCH_SPACING); // stretching disabled, shows ZERO COLUMNS
gv_icons.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
gv_icons.setAdapter(new ImageAdapter(this));
gv_icons.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick (AdapterView<?> parent, View view, int position, long id) {
eventIcons.set(selectedItem, position);
updateEventView();
d_icons.dismiss();
}
});
d_icons.setTitle(R.string.text_chooseicon);
d_icons.addContentView(gv_icons, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
d_icons.setCancelable(true);
}
Can anyone spot why this happens? Thank you!!
Here's how it appears with the 4 fixed columns: i50.tinypic.com/2ebdfec.png
Here's how it is with the autofit: i50.tinypic.com/25jxo9s.png
Here is the layout with autofit on a horizontal tab: i49.tinypic.com/23r7qj5.png
This post answers your question:
Android: How does GridView auto_fit find the number of columns?
In order for the auto_fit to work you need to specify the column width:
gridView.setColumnWidth( width );
Actually it depends on device size. As you first tested your application with making the no of columns fixed, grid displays only 2 columns because the space for the 3 image is not enough large so that it can fit in.
Related
I have a String[][] of data and I am trying to make a custom listView from it.
Here is the data
String[][] myDataArray = {{"cat1","cat2","cat3","cat4"},
{"dog1","dog2","dog3"},
{"lion1"},
{"monkey1","monkey2"}};
And now here is how I am trying to display this data in my listView. I want each array within the array to have its own row. So all the cats will be in one row (cell), all the dogs will be in another row and so on. Here is a picture to make it clear each item in the row, is a textView.
I have made cell_4.xml, cell_3.xml, cell_2.xml, cell_1.xml layout file for each of the rows.
And then in the activity that I am trying to show this, I just have a plain old listView.
Now I am not quite sure how to edit/ adapt the data. I have to display it in this way. So that it uses the correct cell layout for each array within the String[]. I was thinking about using a switch statement to get the number of items in each inner array. But having some trouble with the ArrayAdapter. To get it set up.
I have looked at a couple of examples on stackoverflow like this one Custom ListView Android to try and figure this out but can't get it.
EDIT
Here is trying to set up adapter and call MyListViewAdapter, but I don't know what to set as context.
here is the code:
private void handleData(String[][] data){
BaseAdapter adapter = MyListAdapter(context, data);
ListView list = (ListView) findViewById(R.id.mealsListView);
list.setAdapter(adapter);
}
Some thoughts:
1) If you are determined to use ListView, skip this point. Else, you might be interested in GRIDVIEW that natively support a table structure.
2) Your idea is consistent. ListView only knows about ROWS, so your adapter will be called for you to display a ROW, and it's up to you to transform the array in that row into an element with multiple cells. You'll do that in getView()
3) You'll make use of the Item Types (getViewTypeCount and getItemViewType) to declare you have different item types. Each type will be a row with a given number of cells: 1,2,3,4...
you will override getViewTypeCount() to return the maximum number of cells in a row
you will either inflate a static layout for the number of cells a row has, or generate it dynamically
Let's get started ... First of all in the adapter we override the Type methods to declare
our rows will be of different types:
#Override
public int getViewTypeCount() {
return 4;
// you have 4 types of rows.
// SUPER IMPORTANT: No row in the array can have more cells than this number
// or getView will crash (you'd have to define additional layouts)
}
#Override
public int getItemViewType(int position) {
// for a given position, you need to return what type is it. This number ranges
// from 0 to itemtypecount-1. We return the length of the array (number of cells)
// this function is called by the View Recycler to appropriately pass you the
// correct view to reuse in convertView
return myDataArray[position].length - 1;
}
And then we need to implement getView(). The typical implementation will be the first one, where you create different XMLs, and the second one is a more advanced implementation where we dynamically create the layouts without any xml.
First Case: Static Layouts
Ideal if you limit the Row Array Length to say 3 or 4, to avoid creating dozens of layouts. So you define 4 xmls (ie. row_1_childs, row_2_childs, row_3_childs, row_4_childs) that will be the templates of rows with that number of children. Then,
and then in GetView:
// we define an array of layout ids to quickly select the layout to inflate depending on
// the number of rows:
private final static int[] sLayouts=new int[] {
R.layout.row_1_childs,
R.layout.row_2_childs,
R.layout.row_3_childs,
R.layout.row_4_childs
};
public View getView (int position, View convertView, ViewGroup parent) {
int maxcells=myDataArray[position].length;
if (convertView == null) {
// generate the appropriate type
if (maxcells<=sLayout.length) {
// just check we are in bounds
convertView=LayoutInflater.from(parent.getContext()).inflate(sLayout[maxcells-1], null);
} else {
// you have a row with too many elements, need to define additional layouts
throw new RuntimeException ("Need to define more layouts!!");
}
}
// At this point, convertView is a row of the correct type, either just created,
// or ready to recycle. Just fill in the cells
// for example something like this
ViewGroup container=(ViewGroup)convertView;
for (int i=0; i<maxcells; i++) {
// We assume each row is a (linear)layout whose only children are textviews,
// one for each cell
TextView cell=(TextView)container.getChildAt(i); // get textview for cell i
cell.setText(myDataArray[position][i]);
cell.setTag( new PositionInfo(position, i)); // we store the cell number and row inside the TextView
cell.setOnClickListener(mCellClickListener);
}
return convertView;
}
Second Case: Dynamic Layouts
Another solution would be to dynamically generate the rows, and dynamically generate as many text views as you might need. To do so, keep overriding getViewTypeCount() to return the Maximum number of children, and define getView like this:
public View getView (int position, View convertView, ViewGroup parent) {
String rowData=myDataArray[position];
if (convertView==null) {
// generate a LinearLayout for number of children:
LinearLayout row=new LinearLayout(context);
for (int i=0, len=rowData.length(); i<len; i++) {
// generate a textview for each cell
TextView cell=new TextView(parent.getContext());
// we will use the same clicklistener (very efficient)
cell.setOnClickListener(mCellClickListener);
row.addView(cell, new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1)); // same width for each cell
}
convertView=row;
}
// here convertView has the correct number of children, same as before:
ViewGroup container=(ViewGroup)convertView;
for (int i=0, len=rowData.length(); i<len; i++) {
TextView cell=(TextView)container.getChildAt(i);
cell.setText(rowData[i]);
cell.setTag( new PositionInfo(position, i)); // we store the cell number and row inside the TextView
}
return convertView;
}
// auxiliar class to store row and col in each textview for the clicklistener
private class PositionInfo {
public int row, col;
public PositionInfo(int row, int col) { this.row=row; this.col=col; }
}
// trick: only one clicklistener for millions of cells
private View.OnClickListener mCellClickListener=new View.OnClickListener() {
#Override
public void onClick(View v) {
PositionInfo position=(PositionInfo)v.getTag(); // we stored this previously
// you pressed position.row and position.col
}
}
Solution (1) is cool to manually create the layouts and configure them a lot.
Solution (2) is cool to programmatically support any number of cells, in case they are very different
Both solutions are pretty efficient, because they play nice with the View recycler: If you fail to use View Types and you constantly inflate layouts, your ListView will be laggy and waste a lot of memory and resources.
You might run into problems if the size of each string in the row varies and you might then have to push data onto the next line.
Try using an alternate view, if your aim is categorization of similar data, expandable listview is an option to consider.
You will need to make your own adapter by extending BaseAdapter. You can check the data's size the getView() method, and inflate the correct layout.
UPDATE:
public class MyListAdapter extends BaseAdapter{
String[][] mData;
LayoutInflater mLayoutInflater;
public MyListAdapter(Context context, String[][] data) {
mData = data;
mLayoutInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return mData.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
String data[] = mData.get(position);
switch(data.length){
case 4:
convertView = mLayoutInflater.inflate(R.layout.cell_4, parent, false);
TextView t1 = (TextView) convertView.findViewById(R.id.one);
t1.setText(data[0]);
break;
case 3:
convertView = mLayoutInflater.inflate(R.layout.cell_3, parent, false);
break;
case 2:
convertView = mLayoutInflater.inflate(R.layout.cell_2, parent, false);
break;
case 1:
convertView = mLayoutInflater.inflate(R.layout.cell_1, parent, false);
break;
default:
convertView = mLayoutInflater.inflate(R.layout.blank, parent, false);
}
return convertView;
}
}
I have a gallery in my android application when I clicked on gallery item I want to display image and grid view. I done when i have only three images in gallery and in click it displayed correctly. But now I have seven images in gallery and each gallery item there are four images to display gallery.In this I m using array of drwawable images only.In this time I got an exception as Java.lang.OutOfMemory
I use below code
public ImageThemeAdapter(Context c, Integer[] mImageIds) {
imagesId=mImageIds;
bitmap=new Bitmap[imagesId.length];
mContext = c;
TypedArray ta=obtainStyledAttributes(R.styleable.Gallery1);
imageBackground=ta.getResourceId(R.styleable.Gallery1_android_galleryItemBackground, 1);
ta.recycle();
for (int i = 0; i <imagesId.length; i++) {
bitmap[i]=BitmapFactory.decodeResource(mContext.getResources(),imagesId[i]);
}
}
public int getCount() {
return bitmap.length;
}
public Object getItem(int position) {
return bitmap[position];
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView i = new ImageView(mContext);
i.setImageBitmap(bitmap[position]);
i.setScaleType(ImageView.ScaleType.FIT_XY);
i.setLayoutParams(new Gallery.LayoutParams(130, 120));
// i.setLayoutParams(new Gallery.LayoutParams((ScreenWidth*80)/100, android.view.ViewGroup.LayoutParams.WRAP_CONTENT));
i.setBackgroundResource(imageBackground);
return i;
}
please provide any suggestions. Thanks in advance.
I have have a requirement like this
Please repeat the steps using Allocation Tracker(Windows -> Shows View -> Other -> Android -> Allocation Tracker) and find out the memory allocation to different objects.
The main reason could be due the layout of custom view as you are creating GridView and at runtime you will be creating thumbinals of images of gallery. we might have to create a gridview with minimum number of images available at one time.
I have infinite gallery based on this example :
http://blog.blundell-apps.com/infinite-scrolling-gallery/ ,
It runs nicely, now I want to have the Gallery still scroll through the images and under each image there should be a text caption.
I searched net with no result, would you please help me in coding that, just beginner in development.
==================================================================================
NEW Update :
upload photo explane what i need exactly which i get it with normal gallery (applied text to each image and able to customize text too as shown down image ,and each image has diffrenet text than others , but still not succeed to do it with infinite gallery :
PLEASE ANY HELP AND ADVICE .
THANKS ALOT.
I went through Blundell's tutorial and thanks to him I know how to make an Infinitelyscrolling gallery :)
To answer the question, about how to add a text caption below each of the images , I made same small changes to Blundell's nice tut and used some of his suggestions in the above answer and I think I got a nice way of doing the task.
The code below doesnt inflate or use gallery_item.xml at all, so it will increase the performance significantly compared to the way when you are inflating it every time.
Trimmed down code of classes from Blundell's tutorial ( because in the question, you are using only resources and not sdcard).
public class InfiniteScrollingGalleryActivity extends Activity {
public class GalleryItem{
int imageId;
String caption;
public int getImageId() {
return imageId; }
public String getCaption() {
return caption;
}
public GalleryItem(int i,String s) {
imageId=i;
caption=s; }
}
int[] resourceImages = {R.drawable.ic_launcher,R.drawable.ic_launcher,R.drawable.ic_launcher,
R.drawable.ic_launcher,R.drawable.ic_launcher,R.drawable.ic_launcher};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GalleryItem[] item = new GalleryItem[6];
//initialising all items, change member variables according to needs
for(int i=0;i<6;i++){
item[i] = new GalleryItem(resourceImages[i], "pic no" +(i+1)); }
setContentView(R.layout.activity_main);
InfiniteGallery galleryOne = (InfiniteGallery) findViewById(R.id.galleryOne);
galleryOne.setResourceGallery(item);
} }
Here I have added the GalleryItem class array and passed it.
Also added the below code in InfiniteGalley class.
public void setResourceGallery(GalleryItem[] item) {
setAdapter(new InfiniteGalleryResourceAdapter(getContext(), item));
setSelection((getCount() / 2));
}
below code's getView() is where the good things happen :
public class InfiniteGalleryResourceAdapter extends BaseAdapter {
/** The context your gallery is running in (usually the activity) */
private Context mContext;
GalleryItem[] myItems;
public InfiniteGalleryResourceAdapter(Context context, GalleryItem[] item) {
this.mContext = context;
myItems=item;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// convertView is always null in android.widget.Gallery
TextView t = new TextView(mContext);
try {
int itemPos = (position % myItems.length);
t.setText(myItems[itemPos].getCaption());
Drawable d = mContext.getResources().getDrawable(myItems[itemPos].getImageId());
((BitmapDrawable) d).setAntiAlias(true); // Make sure we set anti-aliasing otherwise we get jaggies (non-smooth lines)
//d.setBounds(0,0,60,60); //use this to change dimens of drawable,if needed
t.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null);
} catch (OutOfMemoryError e) {
// a 'just in case' scenario
Log.e("InfiniteGalleryResourceAdapter", "Out of memory creating imageview. Using empty view.", e);
}
return t;
}
#Override
public int getCount() {
return Integer.MAX_VALUE;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
/** The width of each child image */
private static final int G_ITEM_WIDTH = 120;
/** The height of each child image */
private static final int G_ITEM_HEIGHT = 80;
private int imageWidth;
private int imageHeight;
}
In getView(), I am just creating a textView and assigning the drawable to it using the handy t.setCompoundDrawablesWithIntrinsicBounds(null, d, null, null); . So it excludes the need of inflating layouts which is a heavy operation.
Below is the output image:
In the adapter you can see the method: getView, you can see this method returns an ImageView, so now you want the getView method to return an imageview and textview...
U can do this in a few ways, here how you can do it with a LayoutInflater
View v = getLayoutInflater().inflate(R.layout.gallery_item, null);
((ImageView) v.findViewById(R.id.img)).setImageResource(imageIds[position]);
((TextView) v.findViewById(R.id.caption)).setText(captions[position]);
So in your res/layout folder you should have an 'gallery_item' layout that contains an ImageView (img) and a TextView (caption)
i.e.
gallery_item.xml
<LinearLayout>
<ImageView ... />
<TextView ... />
</LinearLayout>
Hope this was helpfull!
EDIT
so as the above example shows you would need two arrays, one of imageIds and one of textCaptions. To keep your Adapter nice and clean it's screaming for you to make an object.
public class GalleryItem {
int imageId;
String caption
// Constructor
// getters and setters
}
You could then pass an Array or List of your GalleryItems to the Adapter (replacing the setAdapter method). i.e:
GalleryItem[] items = new GalleryItem[];
Then in your getView method as outlined above you would extract each object:
GalleryItem item = items[position];
View v = getLayoutInflater().inflate(R.layout.gallery_item, null);
((ImageView) v.findViewById(R.id.img)).setImageResource(item.getImageId());
((TextView) v.findViewById(R.id.caption)).setText(item.getCaption());
Hope thats clear
I'm implementing a application that allows user to drag pages though a Gallery to read a newspaper.
I wrote a Adapter that returns ImageViews with some Bitmaps loaded on application.
My problem is, the first image shows perfectly, the next ones has a gray foreground that make the image looks darker. After dragging somewhere the first one also get this foreground. How can I avoid my views getting this foreground?
Now I realized that the ImageViews are getting some value on alpha channel, this is why together with my black background the images look darker. How can I avoid it?
As solicited, here it's my code:
Gallery view = new Gallery(context);
view.setSpacing(10);
view.setAdapter(new PageAdapter(context, pages));
LayoutParams params = new LayoutParams(600, 300);
layout.addView(view, params);
And the Page Adapter
private class PageAdapter extends ArrayAdapter {
private List pages;
public PageAdapter(Context c, List<Page> pages) {
super(c, 0);
this.pages = pages;
}
public View getView(int position, View convertView, ViewGroup parent) {
Page page = getItem(position);
ImageView image;
if (convertView != null)
image = (ImageView) convertView;
else
image = new ImageView(context);
image.setImageBitmap(ImageUtils.rgb(page.loadNormal()));
return image;
}
public int getCount() {
return pages.size();
}
public Page getItem(int position) {
return pages.get(position);
}
}
This ImageUtils.rgb was created to create a copy of any Bitmap into a Bitmap.RGB_586, but the same behaviour occurs.
You need to specify the unselectedAlpha property on the Gallery.
Either via your java (as per your example)
view.setUnselectedAlpha(1.0);
Of if you are using xml to declare the Gallery, add
android:unselectedAlpha="1.0"
I have a grid view which is populated using a custom ImageAdapter class extending BaseAdapter.
The images are dynamically loaded from a particular folder in the SD card. I have named the images according to their postition (1.png, 2.png etc.). I have also set an OnClickListener for the grid items: an audio file with the same name as the image is played from the SD card.
It works well when the number of images is less and fits on a screen.
But when the number is large and the images doesn't fit on a screen, the next set of rows displayed by scrolling the screen downwards is mostly repetition of images from the first few rows rather than the images at the corresponding position.
I find from the logcat that the getView() function of the adapter class gets called initially only for the images which are visible on the screen and while scrolling downwards, its not being called properly for further positions
Also sometimes the entire set of images gets re-arranged.
Should I do anything different from the basic implementation of grid view for properly displaying large number of images? Is there anything else I must be taking care of?
EDIT - CODE
I'm setting each tab using
tabGrid[i].setAdapter(new ImageAdapter(this,i));
This is the image adapter class
#Override
public int getCount() {
// fileNames is a string array containing the image file names : 1.png, 2.png etc
return fileNames.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
// I did not use this function
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View v;
if(convertView==null) {
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.grid_image, null);
ImageView iv = (ImageView)v.findViewById(R.id.icon_image);
String bitmapFileName = fileNames[position];
Bitmap bmp =(Bitmap)BitmapFactory.decodeFile(dir.getPath() + "/" + bitmapFileName);a
iv.setImageBitmap(bmp);
}
else {
v = convertView;
}
return v;
}
Does the getItem() and getItemId() functions matter? The directories and file names are all valid.
Here's a quick fix which should be better.
#Override
public String getItem(int position) {
return fileNames[position];
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if(convertView==null) {
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.grid_image, parent, false);
}
else {
v = convertView;
}
ImageView iv = (ImageView)v.findViewById(R.id.icon_image);
String bitmapFileName = getItem(position);
Bitmap bmp =(Bitmap)BitmapFactory.decodeFile(dir.getPath() + "/" + bitmapFileName);a
iv.setImageBitmap(bmp);
return v;
}
I filled getItem, it's not 100% needed but it's always better to have it. The rest of your adapter code can then rely on it
The item id should be different for every entry, you could either use getItem(position).hashCode() (might be slower) or just return position (which I did here).
The getView method is a bit more tricky. The idea is that if the convertView is null, you create it. And then, in every case, you set the view's content.
The inflate in the getView item should use the parent as parent, and the "false" is there to tell the system not to add the new view to the parent (the gridview will take care of that). If you don't, some layout parameters might get ignored.
The erorr you had was because the views were getting recycled (convertView not null) and you weren't setting the content for those. Hope that helps !