I have an application that will have 5-15 buttons depending on what is available from a backend. How do I define the proper GridView layout files to include an array of buttons that will each have different text and other attributes? Each button will essentially add an item to a cart, so the onClick code will be the same except for the item it adds to the cart.
How can I define an array so I can add a variable number of buttons, but still reference each of them by a unique ID? I've seen examples of the arrays.xml, but they have created an array of strings that are pre-set. I need a way to create an object and not have the text defined in the layout or arrays xml file.
Update - Added info about adding to a GridView
I want to add this to a GridView, so calling the [addView method](http://developer.android.com/reference/android/widget/AdapterView.html#addView(android.view.View,%20int) results in an UnsupportedOperationException. I can do the following:
ImageButton b2 = new ImageButton(getApplicationContext());
b2.setBackgroundResource(R.drawable.img_3);
android.widget.LinearLayout container = (android.widget.LinearLayout) findViewById(R.id.lay);
container.addView(b2);
but that doesn't layout the buttons in a grid like I would like. Can this be done in a GridView?
In the following code, you should change the upper limits of the for to a variable.
public class MainActivity
extends Activity
implements View.OnClickListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TableLayout layout = new TableLayout (this);
layout.setLayoutParams( new TableLayout.LayoutParams(4,5) );
layout.setPadding(1,1,1,1);
for (int f=0; f<=13; f++) {
TableRow tr = new TableRow(this);
for (int c=0; c<=9; c++) {
Button b = new Button (this);
b.setText(""+f+c);
b.setTextSize(10.0f);
b.setTextColor(Color.rgb( 100, 200, 200));
b.setOnClickListener(this);
tr.addView(b, 30,30);
} // for
layout.addView(tr);
} // for
super.setContentView(layout);
} // ()
public void onClick(View view) {
((Button) view).setText("*");
((Button) view).setEnabled(false);
}
} // class
Here's a nice sample for you:
https://developer.android.com/guide/topics/ui/layout/gridview.html
You should just create buttons instead of imageviews in getView adapter method.
If you are using a GridView, or a ListView (etc), and are producing Views to populate them via the adapter getView(pos, convertView, viewGroup), you might encounter confusion (i did once).
If you decide to re-use the convertView parameter, you must reset everything inside of it. It is an old view being passed to you by the framework, in order to save the cost of inflating the layout. It is almost never associated with the position it was in the layout before.
class GridAdapter extends BaseAdapter // assigned to your GridView
{
public View getView(int position, View convertView, ViewGroup arg2) {
View view;
if (convertView==null)
{
view = getLayoutInflater().inflate(R.layout.gd_grid_cell, null);
}
else
{
// reusing this view saves inflate cost
// but you really have to restore everything within it to the state you want
view = convertView;
}
return view;
}
// other methods omitted (e.g. getCount, etc)
}
I think this represents one of those Android things where the concept is a little difficult to grasp at first, until you realize there's a significant optimization available within it (have to be nice to CPU on a little mobile device)
Related
As a little eperiment, I'm trying to do the following.
I have an AXML describing a vertical linear layout which contains a listview (only filling 200dp of the vertical linear layout ). The AXML is inflated when the activity starts with SetContentView. Then the listview is correctly populated with values using its Adapter.
In the GetView method of the listview Adapter, I am trying to also dynamically create a button and add it to the linear layout, but for some reason the button is not added.
If I try to add the button in the constructor method of the Adapter instead, it is correctly added.
Can you tell me what could be possibly going wrong?
Let me add some code:
class TracksAdapter : BaseAdapter<string> {
Activity context;
List<Dictionary<string,string>> trackList;
// constructor
public TracksAdapter (Activity context, List<Dictionary<string,string>> trackList) {
this.context = context;
this.trackList = trackList;
// Just as a little test, if I create the button from here it will be correctly added to linear layout:
var ll = context.FindViewById<LinearLayout>(Resource.Id.linLayForResultsActivity);
Button b1 = new Button(context);
b1.Text = "Btn";
ll.AddView(b1);
}
public override View GetView(int position, View oldView, ViewGroup parent) {
// if I create the button from here it will not be added to the layout
var ll = context.FindViewById<LinearLayout>(Resource.Id.linLayForResultsActivity);
Button b1 = new Button(context);
b1.Text = "Btn";
ll.AddView(b1);
// this other code is working
View view = context.LayoutInflater.Inflate(Resource.Layout.ResultItem, null);
var artistLabel = view.FindViewById<TextView>(Resource.Id.resultArtistNameTextView);
artistLabel.Text = trackList[position]["trackArtistName"];
return view;
}
}
Update: adding some more context information because I know this can be a bit weird to understand without it:
In GetView, I don't need to return the new button I am trying to create there. GetView only need to return a listview view item, but, along its execution, GetView also has to create and add a button to the linear layout containing the listview.
The real code is much more complex than that. I have simplified it in the question. In the real code, the listview items are made of text and a button. The GetView also attaches event handlers to the buttons. Then what I need is, when a user clicks a button in any of the listview items, another button is added below the listview. So I need the code for adding another button to be in GetView, and the button needs to be added outside of the listview, ie. to the linear layout containing the listview.
Use the LayoutInflator to create a view based on your layout template, and then inject it into the view where you need it.
LayoutInflater vi = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = vi.inflate(R.layout.your_layout, null);
// fill in any details dynamically here
TextView textView = (TextView) v.findViewById(R.id.a_text_view);
textView.setText("your text");
// insert into main view
ViewGroup insertPoint = (ViewGroup) findViewById(R.id.insert_point);
insertPoint.addView(v, 0, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
I looked in you code, you are returning view, while you add the button to ll, you should return ll
what you return in getView() is what you see in the list item layout, since you're adding the button to ll and returning view, the button won't appear.
you can add the button to view as you implementation
Also check this:
Try using boolean addViewInLayout (View child, int index, ViewGroup.LayoutParams params)
http://developer.android.com/reference/android/view/ViewGroup.html#addViewInLayout(android.view.View, int, android.view.ViewGroup.LayoutParams)
It's working... Without making any changes now it's working as it should... ! Ugh!
I really don't know what I was doing wrong here... probably it was because of some sort of caching of older version of the installed APK.. ? I know this sort of stuff can happen, and that's why I've always been uninstalling the app before deplyoing the new version to the device... but still...!
I need to have an scroll with items together, and the selected item should expand a part down.
I am currently using a Gallery (I tried with viewflow and viewpager, but the items have much space between them), but I need to know how can I do this effect.
I have 2 ideas, but i don't know how can I implement it.
1) The expandable part is a LinearLayout with visibility=gone, and when the item is selected, this layout should be visible. (Gallery do not have "onItemSelectedListener")
2) Treat each element as a fragment (once I use a Viewpager that use this, https://github.com/mrleolink/SimpleInfiniteCarousel)
It does not necessarily have to be a gallery, any idea is welcome
I am working on an Activity.
Depends on the behavior that you want. Some questions can more than one item be expanded at a time? Do you want the views to be paged (snap into place) or smooth scroll them?
One Suggestion I have is to make a custom view for the individual cells. Then add them programmatically to a HorizontalScrollView Object.
HorizontalScrollView hsv = new HorizontalScrollView(activity);
LinearLayout hll = new LinearLayout(activity);
hll.setOrientation(LinearLayout.HORIZONTAL);
for(int i=0;i<items.length();i++){
hsv.addView(new CustomExpandView(item));
}
The CustomExpandView would be used for your cells and could be something like this...
public class CustomExpandView extends RelativeLayout implements OnClickListener {
MyActivity mActivity = null;
ImageView ivImage, ivOverImage;
RelativeLayout rlView;
public CustomExpandView(Context context) {
super(context);
initialize();
}
public CustomExpandView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public void initialize() {
mActivity = (MyActivity) this.getContext();
LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.custom_cell_expand, this, true);
//you can initialize subviews here
rlView = (RelativeLayout) getChildAt(0);
ivImage = (ImageView) rlView.getChildAt(0);
ivOverImage = (ImageView) rlView.getChildAt(1);
rlView.setOnFocusChangeListener(new OnFocusChangeListener(){
#Override
public void onFocusChange(View v, boolean hasFocus) {
LinearLayout expand = v.findViewById(R.id.view_i_want_to_expand);
if(hasFocus)
expand.setVisibility(View.VISIBLE);
else
expand.setVisibility(View.GONE);
}
});
}
You gave the answer yourself. You can use a ViewPager, with fragments, and have an animation to extend the lower part of the window. Depends on whether you want the windows to be full screen or not. A viewpager doesn't necessarily need fragments, you can use ordinary views, and an appropriate adapter. Just play with it and see which solution you like most.
Next time, just create the code and the app, and ask a much more specific question, with code to illustrate the issue you're experiencing.
You could simply define a TableView with just one TableRow (or as many as you need) and set a onClickListener for each of those Views inside the TableRow, which would make that on any click, the selected View would expand itself.
I don't know whether you'll have a static number of Views inside that row or you'll construct them dynamically, but this should work for any of them, the real "work" here about populating that row.
Once you have your row of Views, simply declare an onClickListener() on each of them. For example, this should be enough:
OnClickListener myListener = new OnClickListener() {
#Override
public void onClick(final View v) {
v.setVisibility(View.VISIBLE);
}
};
And as the onClick event for all of your items inside the TableRow:
for (View v : myTableRowViews)
v.setOnClickListener(myListener);
This has a disadvantage: You can know which View has been clicked for selection, but natively you cannot know which has been deselected, so you'll need to keep track of the last selected tab declaring a class-wide variable and setting it each time onClick() is fired, so your listener will become something like this:
// In your class declare a variable like this
View lastSelected = null;
OnClickListener myListener = new OnClickListener() {
#Override
public void onClick(final View v) {
if (lastSelected != null)
lastSelected.setVisibility(View.GONE);
v.setVisibility(View.VISIBLE);
lastSelected = v;
}
};
Additionally, you can set an animation to the effect to make it more attractive, but mainly this is the idea.
One last thing: To make it work this way you'll need to set the layout_height of both your TableRow and the View items inside, so it may expand afterwards when you set the additional part as visible. Also, to make it look good all of your Views will have to be the same height (both in the reduced and extended state).
I have a base adapter class which i use to fill in a listview. Some of the contents are defined in a layout file and i also need to dynamically add in a certain number of image button depending on the int value passed to the base adapter.
The obj is a object that has the int value along with a arrayList of bitmaps;
when i run this code i get more image buttons then the value of obj.value.
likePre_pics is the name of the arrayList of bitmaps
Can someone please help?
public class News_Feed_BaseAdapter extends BaseAdapter{
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
LinearLayout linLayout =
(LinearLayout)convertView.findViewById(R.id.like_preview_LinearLayout);
for(int i=0; i< obj.value;i++)
{
ImageButton op= new ImageButton(context);
LayoutParams lpView = new LayoutParams(100, 100);
//op.setImageBitmap(obj.get(position).likePre_pics.get(i));
linLayout.addView(op,lpView);
}
}
}
As i can see from your code there is two problem,first is IndexOutOfBoundsException. ArrayList IndexOutOfBoundsException only occurs when there is more/less item then you are referring to.
//op.setImageBitmap(obj.get(position).likePre_pics.get(i));
The line you commented out. You are referring to obj.get(position) where obj has obj.size() number of element.
Next is ImageButton issue since you are not reusing the convertview in an efficient way when you are adding new imagebutton into linLayout of convertview that is why linLayout is showing more then 2 imagebutton.
For example: If you have 20 items in your list that means getView will be called 20 times. As you will find in android documentation convertView is the old view that gets passed for you to reuse and every-time it gets passed to you , you are adding more imagebutton into it. That's why imagebutton problems are occurring.
Take a look into Romain Guy's World of ListView google i/o presentation if you are interested.
Hellow this is my first question in stack.
Im creating a dynamic grid of this tutorial
http://www.stealthcopter.com/blog/2010/09/android-creating-a-custom-adapter-for-gridview-buttonadapter/comment-page-1/
Now it's working pretty good. My layout is composed by a gridView and under this gridView i have a TextView.
The problem is that i want to change the TextView to display different information on each id when the focus changes (on the grid elements). I've tried to use OnFocusChangeListener inside ButtonAdapter, but when trying to get a reference to the textView, it says that findViewById is not implemented.
I wonder how to make a reference in my main activity that allows me to handle my dynamic grid elements.
I have the following in onCreate();
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ButtonAdapter(this));
So i want to handle my grid elements from here, any ideas?
Thanks
Edit:
I've been trying to change different things, but im receiving a NullPointerException from my getView method. I can't find a way to make it work, i'll apreciate any help guys, this is my code:
public View getView(int position, View convertView, ViewGroup parent) {
final Button btn;
if (convertView == null) {
// if it's not recycled, initialize some attributes
btn = new Button(mContext);
btn.setLayoutParams(new GridView.LayoutParams(100, 55));
btn.setPadding(8, 8, 8, 8);
} else {
btn = (Button) convertView;
}
btn.setText(filenames[position]);
// filenames is an array of strings
btn.setTextColor(Color.WHITE);
btn.setId(position);
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
TextView vt = (TextView) btn.findViewById(R.id.textView1);
vt.setText("Button Pressed");
}
});
Thanks.
I think you are on the right track, but I think it would make most sense to take care of it in your adapter. So findViewById() isn't working for you, but it will work if you change it to convertView.findViewById() (or whatever view is returned in your getView method of your Adapter). From there you will be able to manipulate the TextView.
I am using a ListView to display the main screen of my application.
The main screen is essentially a menu to get into the different sections of application. Currently, I have the ListView whose contents are added programmatically in the onCreate method.
Here is the code snippet that does this:
String[] mainItems = {
"Inbox", "Projects", "Contexts", "Next Actions"
}
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
setListAdapter(new ArrayAdapter<String>(
this, android.R.layout.simple_list_item_1, mainItems));
registerForContextMenu(getListView());
}
So the menu is essentially just a bunch of nodes with the text contained in the mainItems array. I know that I can create an XML layout (i.e. R.layout.mainMenu_item) that has an ImageView and TextView in it, but I am unsure how to set the ImageView's icon. I have seen that there is a setImageResouce(int resId) method, but the way to use this when generating with an ArrayAdapter is eluding me. Is there a better way to do this?
What I typically do for a ListView is to implement my own Adapter by extending the handy BaseAdapter class. One of the abstract methods you'll implement will be getView() as the previous poster mentioned. From there you can inflate a layout containing an ImageView, get a reference to it using findViewById, and set the image to whatever drawable you've added into your resources.
public View getView(int position, View convertView, ViewGroup parent) {
View row = inflater.inflate(R.layout.menu_row, null);
ImageView icon = (ImageView) row.findViewById(R.id.icon);
icon.setImageResource(..your drawable's id...);
return view;
}
From the google docs for ArrayAdapter.
To use something other than TextViews
for the array display, for instance,
ImageViews, or to have some of data
besides toString() results fill the
views, override getView(int, View,
ViewGroup) to return the type of view
you want.