Folks -
I'm trying to implement a Gallery widget that displays an ArrayList of images, and I have started with the Hello, Gallery example on the dev site. This part is all working great.
I need to have the gallery display an empty view (a special view when the ArrayList has no contents), but I cannot seem to get the Gallery to do this. I have done this with ListView and other AdapterViews in the past, but I cannot get it to work with Gallery. What do I need to override/implement in the Adapter, Gallery, or both to get an empty view displayed? This is my adapter code:
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
private ArrayList<Drawable> images;
public ImageAdapter(Context c) {
mContext = c;
TypedArray a = c.obtainStyledAttributes(R.styleable.Gallery1);
mGalleryItemBackground = a.getResourceId(R.styleable.Gallery1_android_galleryItemBackground, 0);
a.recycle();
images = new ArrayList<Drawable>();
}
public void addImage(Drawable d) {
images.add(d);
}
public boolean isEmpty() {
return getCount() == 0;
}
public int getCount() {
return images.size();
}
public Drawable getItem(int position) {
return images.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View contentView, ViewGroup parent) {
ImageView i = new ImageView(mContext);
i.setImageDrawable(images.get(position));
i.setLayoutParams(new Gallery.LayoutParams(160, 120));
i.setScaleType(ImageView.ScaleType.FIT_XY);
i.setBackgroundResource(mGalleryItemBackground);
return i;
}
}
When the view is to be displayed with an empty ArrayList, getCount() does get called (returning 0), but the Gallery never checks isEmpty, and when I had defined getEmptyView() in the Gallery, it was never called either. Did I miss another required method in BaseAdapter to properly notify the empty state?
Thanks!
With the assistance of this article, I found the answer:
Correct use of setEmtpyView in AdapterView
The key to the issue was that (once I got the Gallery/AdapterView to properly call the empty status check using the addendum information) AdapterView is designed only to switch the View visibility settings between the content and empty views (swapping View.GONE and View.VISIBLE). Therefore, if you didn't do the legwork of properly creating and laying out both the content and empty views in the parent layout, they will not display properly.
In may case, I had created the empty view programmatically (just a TextView) and used setEmptyView() to attach it to the adapter view. The TextView was never attached to the LinearLayout that represented the Activity, so it didn't show up even after the AdapterView so kindly set it View.VISIBLE.
Related
I'm trying to create a UI similar to Google Keep. I know how to create a staggered View using a Recycler View. If i click a specific Card. Then it has to open a activity.
I can achieve this using onclick method.
This same scenario happens in atleast 5 different Activities in my App.
My question is that
Can I use this single Adapter in all those 5 places ?
If yes, where should i place the onclick actions ?
If no, How can I Create a staggered layout like Keep?
Thanks in Advance!
(See application for RecyclerView below in edits)
Like I mentioned in my comment, it's certainly fine to have separate adapters for all your Activities which use different data and views. As your app data and layouts get more complex, so does your code...that's just the way it is.
But if some of your Activities used similar data in their ListViews -- maybe, for example, two TextViews and an ImageButton -- you could save some effort by defining a single adapter that can be used for multiple Activities. You would then instantiate separate objects for each Activity, similar to the way you would create several ArrayAdapter<String> objects to populate multiple ListViews.
The BaseAdapter is a great class to extend when writing a custom adapter. It's flexible and allows you complete control over the data that's getting shown in your ListView. Here's a minimal example:
public class CustomBaseAdapter extends BaseAdapter {
private Context context;
private ArrayList<String> listData;
public CustomBaseAdapter(Context context, ArrayList<String> listData) {
this.context = context;
this.listData = listData;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.your_list_item_layout, parent, false);
//populate the view with your data -- some examples...
TextView textData = (TextView) convertView.findViewById(R.id.yourTextView);
textData.setText(listData.get(position));
ImageButton button = (ImageButton) convertView.findViewById(R.id.yourImageButton);
button.setOnClickListener(new View.OnClickListener() {
//...
//...
});
}
return convertView;
}
#Override
public Object getItem(int position) {
return 0;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public int getCount() {
return listData.size();
}
}
So the key part of this code is obviously the getView() method, which is called every time the ListView needs some data to display. For efficiency, views are stored in something called a convertView so they may be re-used and not have to be inflated every time a view appears on the screen.
So what we do in getView() is first find out if the convertView exists. If it does, we just pass that back to the calling ListView because everything should already be instantiated and ready to go. If the convertView is null, it means the data hasn't been instantiated (or needs to be re-instantiated for whatever reason), and so we inflate a brand new view and populate it with our data.
So in the case of this example adapter above, if several of your Activities all displayed a single list of Strings, you could reuse this adapter for each one, passing in a different ArrayList<String> through the constructor each time you created a new object. But obviously you could pass in more than just Strings if you had more data to show. The level of complexity is up to you. And if the difference among your Activities was too great, I would just create custom versions of this class for all of them and just instantiate them and populate them however you'd like. It will keep all your data very organized and encapsulated.
Hope this helps! Feel free to ask questions for more clarification if you need it.
EDIT IN RESPONSE TO COMMENTS
Since you are using a RecyclerView instead of just plain ListViews (which I, for some reason, totally forgot) you could still do something very similar using a RecyclerView.Adapter<YourViewHolder> instead. The difference would be that instead of inflating the views in a getView() method, they are inflated inside your custom ViewHolder, which I assume you already have. The code might look something like this:
public class CustomRecyclerViewAdapter extends RecyclerView.Adapter<StringViewHolder> {
private final List<String> items;
public CustomRecyclerViewAdapter(ArrayList<String> items) {
this.items = items;
}
#Override
public StringViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//So instead of inflating the views here or in a getView() like in
//in the BaseAdapter, you would instead inflate them in your custom
//ViewHolder.
return new StringViewHolder(parent);
}
#Override
public void onBindViewHolder(StringViewHolder holder, int position) {
holder.setModel(items.get(position));
}
#Override
public long getItemId(int position) {
return items.get(position).hashCode();
}
#Override
public int getItemCount() {
return items.size();
}
}
I was looking at a way to answer this question where the OP is trying to limit the number of items displayed in the spinner's dropdown view. It seems that it cannot be done.
The Spinner class has its own private interface called SpinnerPopup which defines how dropdown items can be shown. This is currently based on the spinnerMode allowing for a dropdown or dialog list.
Both options are also implemented inside the Spinner class as private classes: DialogPopup and DropdownPopup. So it seems to me that the only way to customize this to add another popup option would be to copy the spinner source code and create my own version of it.
But if the SpinnerPopup interface were public, it seems like it would be easy to just:
Create my own popup implementing SpinnerPopup; and
Create my own spinner extending the original one where I override the constructor Spinner(Context context, AttributeSet attrs, int defStyle, int mode) to handle my popup.
Does anyone have any idea (or guess) why this is not the case? Or am I missing a simpler solution here?
Thanks!
I need set my own view to the dropdown, so this was what I did:
public class SpinnerAdapter_Tabela_Preco extends ArrayAdapter<Tabela_Preco>{
// Your sent context
private Context context;
private List<Tabela_Preco> list_tabela_preco;
public SpinnerAdapter_Tabela_Preco(Context context, int textViewResourceId, List<Tabela_Preco> values) {
super(context, textViewResourceId, values);
this.context = context;
this.list_tabela_preco = values;
}
public int getCount(){
return list_tabela_preco.size();
}
public Tabela_Preco getItem(int position){
return list_tabela_preco.get(position);
}
public long getItemId(int position){
return position;
}
// And the "magic" goes here
// This is for the "passive" state of the spinner
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Tabela_Preco t_p = getItem(position);
// I created a dynamic TextView here, but you can reference your own custom layout for each spinner item
TextView label = new TextView(context);
label.setTextColor(Color.BLACK);
label.setTypeface(null, Typeface.BOLD);
// Then you can get the current item using the values array (Users array) and the current position
// You can NOW reference each method you has created in your bean object (User class)
label.setText(t_p.toString());
// And finally return your dynamic (or custom) view for each spinner item
return label;
}
// And here is when the "chooser" is popped up
// Normally is the same view, but you can customize it if you want
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
Tabela_Preco t_p = getItem(position);
TextView label = new TextView(context);
label.setTextColor(Color.BLACK);
label.setBackgroundColor(Color.WHITE);
label.setMinimumHeight(50);
label.setGravity(Gravity.CENTER_VERTICAL);
label.setTextSize(18);
label.setText(t_p.toString());
return label;
}
}
And I use with this:
final SpinnerAdapter_Tabela_Preco adapter = new SpinnerAdapter_Tabela_Preco(Consulta_Produto.this, android.R.layout.simple_spinner_item, lista_tabela_preco);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spn_tabela_preco.setAdapter(adapter);
I hope this could help you.
I have a GridView adapter based on some example code that I'm trying to figure out. Basically I have an arraylist of applications that I pass into the adapter, the Applications class, among other things, contains the packageinfo for all apps that have been qualified by the user as "Arcade". What I want is to extract the icons from the packageinfo then blow them up and put them into the GridView, no text or anything else underneath, just the app icons.
public class GridAdapter extends BaseAdapter {
private Context mContext;
ArrayList<Applications> mlistArcadeApps;
public GridAdapter(Context context, ArrayList<Applications> listArcadeApps) {
this.mlistArcadeApps = listArcadeApps;
mContext = context;
}
public int getCount() {
return mlistArcadeApps.size();
}
public Object getItem(int position) {
return mlistArcadeApps.get(position);
}
public long getItemId(int position) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView =
}
}
I have that much so far, I'm not entirely sure if this is even right, I'm just trying to get a hold on how to use GridViews, is it possible to extract app icons as an ImageView object? Or is there an alternative method I should look at?
Yes you are doing it right, you need to create another XML file for each item in the list. Create and XML file and put ImageView inside the LinearLayout. After that you need to pass this xml file to your GridView.
Inside your getView method you can update ImageView and show your icons.
This tutorial explains it clearly : Android GridView Layout Tutorial
I just realized, in this tutorial he doesn't create XML file for each item, instead he creates new ImageView at RunTime.
If you look at this example, it shows in different way : Android GridView Example
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"