I have a activity which shows a button, a text and a list. I would like to repeat that pattern depending on data (SQLite). If I have 5 unique categories, show 5 buttons, 5 text and 5 lists. How do I do that ? How can I repeat the pattern ?
You can just add a new button text and list to the same LinearLayout. So..
addStuffz(){
LinearLayout main = (LinearLayout)findViewById(R.id.main_linearLayout);
LinearLayout newLayout = new LinearLayout(context);
/**
* Do Init here
*/
Button anotherButton = new Button(context);
/**
* Do Init here
*/
TextView anotherText = new TextView(context);
/**
* Do Init here
*/
ListView anotherList = new ListView(context);
/**
* Do Init here
*/
newLayout.addView(anotherButton);
newLayout.addView(anotherText);
newLayout.addView(anotherList);
mainLayout.addView(newLayout);
}
The standard design pattern is to use a ListActivity(or a standard activity with a listView)
and create a CursorAdapter to create views and databind values from your recordset cursor to the view.
Then call setAdapter() on your ListActvity.
VIDEO
The use of list adapters is dicussed in the Google IO 2009 video on UI improvements
in android you cannot "repeat a view" in a same actiivty (a child cannot have more than 1 parent ) but u can always do a workaround. u can write a mehod as getview() which returns u a parent view conatining ur buttons, texts etc. and u add this to the top layer viewgroup;
while(somelogic)
urparentview.addview(getview());
setcontentview(urparentview)
ur getviewmethod may be like this
view getview()
{
button b = new button(this);
edittext ed = new edittext(this);
//set orientations etc...
LinearLayout ll = new LineaLayout(this);
ll.addview(b);
ll.addView(ed);
return ll;
}
Related
I am trying to make a dialog that consists of a LinearLayout which contains a number of other LinearLayouts that are effectively Buttons that each bring you to a website. Instead of statically making each LinearLayout Button in the layout xml file, I have a JSON feed that has a possibly changing number of buttons to display in the dialog. I found a question that looks similar...
... but I'm not sure how to dynamically add LinearLayouts to a Dialog. The data for each of the LinearLayout buttons gets parsed into an ArrayList of items, each representing a Button in the Dialog.
Normally you should use a RelativeLayout for this:
To notice, where your last created button is and to set the next one to the next position costs not so much performance as creating new layouts. You should try to call addView(); as less as possible, because each time the view must be rendered again.
But if it's necessary to add LinearLayouts, here is a sample code:
LinearLayout linearLayout = new LinearLayout(context);
linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
linearLayout.setOrientation(LinearLayout.VERTICAL);
viewGroup.addView(linearLayout);
Use a ListView or RecyclerView and control it with an adapter.
For the dialog you can use a DialogFragment. Override the onCreateDialog method and return an AlertDialog inside. The AlertDialog.Builder has the setView method that you can use to inflate your list or recycler. Then just keep a reference to the adapter in your dialog fragment and use it to load your layout-buttons dynamically.
For example:
public class WebsitePickerDialogFragment extends DialogFragment {
Adapter adapter = new YourRecyclerViewAdapter();
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final RecyclerView contentView = LayoutInflater.from(getContext()).inflate(...
contentView.setAdapter(adapter);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(contentView);
return builder.create();
}
}
If you don't want scrolling behavior, you can still use this pattern with any adapter view, like the https://github.com/frankiesardo/LinearListView lib that uses LinearLayout like a ListView.
I was wondering how to refer to an View if I create it programmatically.
I have to create new passenger Views with "Add Passenger" and respectively "Remove Passenger" buttons to my app. The promts are kept LinearLayouts called "#+id/passenger" which have two EditTexts called "#+id/passenger_name" and "#+id/passenger_weight". Those are then kept in a yet another parent LinearLayout called passenger_layout that can hold all the passenger LinearLayouts in a bunch
Adding new passengers is easy, but I have no idea how to refer to the newly created elements. I guess they get a identifier of some sort automatically? I'd prefer them to be "passenger_name%" and "passenger_weight%", where % is an index _passengerCount.
addPassenger.Click += delegate {
//Add to index
++passengerCount;
//Prep new passenger layout
var newLayout = new LinearLayout(Activity);
//Set LayoutParameters from the existing passenger LayoutParameters
newLayout.LayoutParameters = newPassenger.LayoutParameters;
//Prep the new EditTexts
var name = new EditText(Activity);
var weight = new EditText(Activity);
//Set the EditTexts' LayoutParameters from existing LayoutParameters
name.LayoutParameters = new ViewGroup.LayoutParams(passengerName.LayoutParameters);
weight.LayoutParameters = new ViewGroup.LayoutParams(passengerWeight.LayoutParameters);
//These cleary don't work :<
// name.Id = Resources.GetIdentifier( "passenger_name" + passengerCount, "id", Activity.PackageName);
// weight.Id = "passenger_weight" + passengerCount;
//Add EditTexts to the new passenger layout and then and then add the new passenger to the parent LinearLayout
newLayout.AddView(name);
newLayout.AddView(weight);
passengerLayout.AddView(newLayout);
Log.Debug(GetType().FullName, "Add clicked");
};
That is my click delegate to create a new passenger, but again even if I create them like this I don't know how I can find them later if I have to for example remove them or get the name or weight data.
How do I refer to programmatically created UI elements?
When you create a view, try giving it some ID, and holding that ID as a static reference somewhere.
Then, you could simply call the containing view's findViewById(MY_VIEWS_ID) and get the view.
Of course, alternatively, you could always hold a reference to the view you created somewhere in your code when you create it. If you're afraid of memory leaks, you could use WeakReference.
Hope this helps.
You could maintain a static variable within the Activity that will contain the UI element after it gets initialized.
Something like:
// Definition
private static TextView textViewToSave = null;
textViewToSave = // create the TextView programatically.
// Do stuff with the saved TextView
textViewToSave.setText("O Hai world!");
I create a dialog and populate it with a listview that uses a custom list adapter. It works fine, but I've noticed that when the list is long enough to scroll, doing so back and forth will cause some of my list items to randomly lose some of their data. I've noticed it is always the same list items too. For instance, each list item will have a title, image, and date on it. The dates seem to vanish on some when I scroll. They are always there when I start the dialog, and they always vanish once I scroll.
The weird thing is that my list row consists of a few TextViews in 2 rows and its only the bottom row TextViews that dissapear...Any ideas?
Code for my dialog
itemSendPickerDialog = new Dialog(this);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Select Item to Send");
ListView lv = new ListView(this);
Cursor c = mDbHelper.fetchItemsByDate(id);
c.moveToFirst();
int i = R.layout.send_item_menu_row;
MyListAdapter ia = new MyListAdapter(this, mainListBackground, c, true);
lv.setAdapter(ia);
builder.setView(lv);
itemSendPickerDialog = builder.create();
itemSendPickerDialog.show();
And my custom list adapter class:
class MyListAdapter extends ResourceCursorAdapter {
public MyListAdapter(Context context, int i, Cursor cursor, boolean...sending) {
super(context, i, cursor);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView title = (TextView) view.findViewById(R.id.item_name);
title.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_TITLE)));
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getWidth();
width = width - 150;
ViewGroup.LayoutParams params = title.getLayoutParams();
params.width = width;
title.setLayoutParams(params);
String cat = cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_CATEGORY));
if (cat.equalsIgnoreCase("trip notes")) {
LinearLayout ll = (LinearLayout) view.findViewById(R.id.item_datetime_holder);
ll.setVisibility(View.INVISIBLE);
}
TextView date = (TextView) view.findViewById(R.id.item_date);
date.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_DEP_DATE)));
TextView time = (TextView) view.findViewById(R.id.item_time);
time.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_DEP_TIME)));
ImageView iv = (ImageView) view.findViewById(R.id.image_icon);
if (iv != null) {
int index = cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_TYPE);
String type = cursor.getString(index);
if (type != null) {
} else {
type = "notes";
}
iv.setImageResource(getTypeResource(type));
}
}
}
I have faced this problem too...
The problem you are facing is due to the recycling of views by the LIstView when you scroll up/down. In your case, the ListView is giving you those recycled views , whose properties you have changed by making them invisible. Some possible solutions could be:
1) When cat.equalsIgnoreCase("trip notes")) is becoming true, you are making some views invisible. This invisible view is then recycled and given back. The recycled view is still invisible (when it is given back to you), so what you can do is make this invisible view visible in the beginning of your ListAdapter every time .
You can put this code at the beginning of bindView method where you make the layout visible first and then proceed with rest of the logic.( In short, the dates from your display are not vanishing but are just invisible).
2) Override getViewTypeCount() in you adapter . From your codesnippet, it looks like you have two types of rows( one in which R.id.item_datetime_holder is invisible and other in which it is visible) , so return 2 from this method( please do some trial and error ) . This should take care of the problem.
public int getViewTypeCount() {
return 2;
}
You will find an excellent explanation at this link http://logc.at/2011/10/10/handling-listviews-with-multiple-row-types/
3) You can inflate completely different layouts depending on your if condition. But the effeciency would be a little less.
I had a similar problem, when scrolling a list, only the items after the window height decided to get their data repeating from index 0 - so if the last visible item was 8, the next would be 0 again.
So you could try to check if the index of the view is correct, maybe this method of ListView would help
lv.getPositionForView(view);
I've figured out that the code that is doing this is in my custom list adapter class
String cat = cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_CATEGORY));
if (cat.equalsIgnoreCase("trip notes")) {
LinearLayout ll = (LinearLayout) view.findViewById(R.id.item_datetime_holder);
ll.setVisibility(View.INVISIBLE);
}
I hide some of the layout items depending on what the category is, and for some reason when putting a list view in an AlertDialog builder it appears to mix up the items. I don't know exactly how I am going to fix it, but for now if I just remove the invisibility functionality, my data won't go away.
I would use two different layouts, depending on the "type" of list item. It looks like you are switching the "type" based on the cat string containing "trip notes". If it contains it, then you would have a layout that is the same as you have now, but without the item_datetime_holder view. If it doesn't contain it, then you would use the same layout as you're using now (send_item_menu_row).
Here is a good tutorial on how to use multiple layouts in a ListView: http://android.amberfog.com/?p=296
By the way, I think that the reason why some of your rows are not drawing correctly is due to view reuse by the ListView. Utilizing the multiple layouts like I've mentioned above should fix the problem since you won't be changing the visibility of views, but just utilizing two different views to render, depending on what type of list item you're rendering.
I am creating a ListView dynamically and want to add two TextViews in a single ListItem (one Below the other). If I add both TextView they are overlapping each other (The reason I m adding two TextView is because both have to have different text size and styles.)
here is the code... inside the custom ListAdapter getView Method
FrameLayout v = new FrameLayout(AppStarter.this);
TextView title = new TextView(AppStarter.this);
TextView date = new TextView(AppStarter.this);
title.setTextSize(16);
title.setTypeface(Typeface.DEFAULT, Typeface.BOLD);
title.setText(values[position]);
date.setText("Date : " + AppStarter.this.date[position]);
date.setTextSize(14);
v.addView(title);
v.addView(date, 1);
return v;
both title and date are overlapping each other I even tried addView(date,1);it didnt workout.
Either use LinearLayout or RelativeLayout.
LinearLayout linear = new LinearLayout(this);
linear.setOrientation(LinearLayout.VERTICAL);
TextView tv1 = new TextView(this);
tv1.setText("First Text!");
TextView tv2 = new TextView(this);
tv2.setText("Second Text!");
linear.addView(tv1);
linear.addView(tv2);
pass the layout in adapter of listview.
or Add RelativeLayout and place views relatively on same
you have to make a xml file containing your desired design of listview and after that you have to pass that xml file to adapter and then adapter in listview like mentioned below
private ListView listView;
private myadapter myadapter;
listView = (ListView) findViewById(R.id.BuddiesList);
myadapter = new myadapter(this, R.id.XMLFILE, SourceOfdata);
listView.setAdapter(myadapter);
you will use this in program
addcategory1("title ");
addcategory2(" date ");
you will use this in program
addcategory1(" ");
addcategory2(" ");
Th question: Can I re-use RadioButton objects over and over again in an child activity?
I have a parent activity and a child activity. In the child activity, I have a large number of radio button displayed in a UI. In order to provide databinding from the parent down to the child, I have created a class (below) which contains a collection of RadioButtons. To populate the child activity, I pass a reference to this class down to the child which then groups the radioButtons into RadioGroups and displays them. I do this because the checked status of each button is now automatically available in the parent class, without the need to transfer any data through bundles.
public class GeneralAttribute{
Activity mThis;
public class Gender { // Mutually exclusive members
String categoryDesc = "Gender of user";
RadioButton isUnspecified = initRadioButton("Unspecified", true);
RadioButton isMale = initRadioButton("Male" , false);
RadioButton isFemale = initRadioButton("Female" , false);
} ;
<....more subclasses....>
RadioButton initRadioButton(String str, Boolean b) { // Factory
float cLayoutWeight = 0.5f;
RadioButton rb = new RadioButton(mThis);
rb.setText (str);
rb.setChecked(b);
rb.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, cLayoutWeight));
return rb;
}
GeneralAttribute(Activity localThis){ // Constructor
mThis = localThis;
gender = new Gender();
handedness = new Handedness();
location = new Location();
}
}
In the parent activity i have:
public class Parent(...)
public GeneralAttribute mGeneralAttribute; // Member class of RadioButtons
public static SPIGestureLoggerActivity TopLevelActivity;// Reference to the parent activity
public void onCreate(Bundle savedInstanceState) {
TopLevelActivity = this; // Assign this to the reference
mGeneralAttribute = new GeneralAttribute(this); // Initialize the class of RBs
startActivity(child); // Start the child
In the child activity i have this:
radiogroup = new RadioGroup(this);
radiogroup.setOrientation(RadioGroup.VERTICAL);
radiogroup.addView(Parent.TopLevelActivity.mGeneralAttribute.gender.isUnspecified);
radiogroup.addView(Parent.TopLevelActivity.mGeneralAttribute.gender.isMale);
radiogroup.addView(Parent.TopLevelActivity.mGeneralAttribute.gender.isFemale);
Parent.TopLevelActivity.mGeneralAttribute.gender.isUnspecified.setChecked(true);
mLinearLayout.addView(radiogroup);
This works fine....the first time the child activity is displayed. The second time it is displayed I get an exception.
In summary, here is the chain of events:
create the class of RadioButtons,
pass them to the child,
add them to a new RadioGroup
collect user choices
finish the child acitivty (which should destroy the RadioGroups)
use the data in the parent,
start the child activity again,
attempt to add the RadioButtons to new RadioGroups...
...Exception.
I can avoid this problem, if I null the class and reconstruct it. However, I would like to re-show the choices made from the first viewing with the second viewing.
Ideas:
Are the radioButtons saving pointers to the non-existant RadioGroups from the first viewing?
Is there a way to re-assign the view parent on each radio button in the class?
P.S. You may ask why I'm not using XML. For one, I will have 100+ of these radio buttons and I think it will be too painful to manage through XML. For another, I just like working programmatically on these things.
make sure you remove all the radiobuttons from the all radiogroups. Basically your right the radiobuttons are saving pointers to the non-existant raidogroups and no there isn't isn't a way to reassigned without calling removeAllViews on all the radiogroups. The best place to do that will be the onDestroy if your sure thats being called.