I’m new to Android and have a simple question. Currently I’m working on a products application that allows users to add, remove and browse variety of products. The application uses a predefined XML-based layout file as a template that fills the entire screen to show the current product details. When the user adds a new product, I want to re-use the same layout file but populate it with the new product’s information. Also, I want to keep the previously added products (e.g. an ArrayList) and the user can browse this list by sliding horizontally (left-to-right or right-to-left). Now, what is the best thing to use to represent each product (View, sub-view, …etc.), and how to reuse the same XML-based layout file to display different products’ details. Please excuse my English and thank you in advance for the help
You can create a new class that extends the ArrayAdapter and then override the getView() method to inflate your custom layout. getView() will return the View for a single row. This way you can reuse your layout. So it will look something like:
public class ProductAdapter extends ArrayAdapter<Product> {
private LayoutInflater li;
public ProductAdapter(Context context, List<Product> products) {
super(context, 0, products);
li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the product of the position
Product product = getItem(position);
View v = convertView;
if ( v == null ) {
// Your custom layout file for a single row in the list.
v = li.inflate(R.layout.product_row, null);
}
// Populate your view here. Use v.findViewById().
return v;
}
}
To show the list in an ListActivity use:
// The variable products is your initial list of products.
ProductAdapter adapter = new ProductAdapter(this, products);
setListAdapter(adapter);
When you add a product you can add this to your ArrayAdapter by calling either the adapter.add() (if you want to add your product at the end of the list) or insert() (to specify where in the list of products you want to insert the new product) method on the ProductAdapter. Afterwards you can call adapter.notifyDataSetChanged() to notify your adapter that the data has changed and that the list has to be refreshed.
What you are describing is implemented by the ViewPager. There's a blog post on the developer site in addition to the API reference.
To implement it, you'll need to create a PagerAdapter subclass to fill in the details of each view for the ViewPager.
Related
I have a list that I want to display differently in two fragments.
That means I need two ListViews (one in each fragment) but do I also need two adapters ? Or can I simply use one adapter for the two list given that when the data changes, I want the two lists to update.
EDIT : Display differently means I want to display the tasks sorted by alphabetical order in the first fragment and on a calendar in the second fragment.
(Or maybe there is a simpler way to go about the problem ?)
The adapter :
public class TasksAdapter extends ArrayAdapter<Task> {
public TasksAdapter(Context context, ArrayList<Task> tasks) {
super(context, 0, tasks);
}
#Override
public View getView(int position, View convertView, #NonNull ViewGroup parent) {
Task task = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_task, parent, false);
}
return convertView;
}
}
Yes, you can certainly use a single adapter if the adapter is built in such a way that it accommodates the type of data you want to use in both Fragment's.
This is dependent on the specifics of what you want to accomplish, but here are some things to consider:
Simply instantiate two separate instances of the adapter and pass the instance into the .setAdapter method of your ListView or RecyclerView.
You can programmatically use different view holders in your adapter, look into the getItemViewType() method.
If the data in your adapter is more than simple primitives, consider writing a model class to model list objects that works with both types of data you are using.
Please note, you should probably be using a RecyclerView.
Hi All!
I am trying to create a listView with the same row layout. But I am changing the elements of the row dynamically. From the diagram above, you can see that I populate my listView with rows but then the buttons on the row will have to be different based on the type of row. The rest of the information in the row is the same.
Is there a way I can pass a flag into the adapter to make it add/remove elements from the layout based on the type of row? My adapter extends BaseAdapter.
Yes, Make your ListView adapter take a model that contains a flag as such in my answer.
This should work if you are converting your JSON object to a specific POJO with a parser like GSON. If you are trying to load your adapter from a JSON object I would recommend against that, so good luck.
public class Row{
private RowType rowType;
//all other row attributes
}
public enum RowType{
YesNo, Cancel, //all other possible scenarios
}
public class YourAdapter extends BaseAdapter{
private List<Row> rowsToPopulate;
//Override appropriate methods
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//load your view etc....
//Check the flag to load appropriate fragment
switch(rowsToPopulate.get(position).getRowType()){
case YesNo:
//Load your yes no fragment to your row view
case Cancel:
//Load your cancel fragment to your row view
}
return yourView;
}
This information should be contained in the model that the adapter knows about.
In the getView(..) method, you should be able to check if <model>.getType() is a specific type. If so, you can set visibility of specific components in your layout and change the UI accordingly.
If the type is not a part of your response, you can keep a Map<Model, Type> inside the adapter and update the adapter when you have new data, and have a convenience method to convert this to a list of model objects to display in the UI.
I was wondering if it is possible to add subscript values to listview using a custom adapter. My adapter is expecting two List<String> and I'm using this method to populate my ListView. I looked around and a lot of people are recommending the
((TextView)findViewById(R.id.text)).setText(Html.fromHtml("X<sup>2</sup>"));
...but, I do not want the subscript applied to any additional list item that I add. I would only like it added to specific items/instance.
Fields.add("My Sample\nMy Sample\nMy Sample");
Values.add("Value\nValue\nValue");
For example I'd like to be able to pick and choose when it is applied:
Is this possible?
Rows in list view are associated with data in the adapter. To control the content of each row like applying subscript, you need to add necessary information in the data. So you cannot do it if your adapter uses solely List<String>. But if you change it to say a list of Item where Item is
class Item {
String title;
String subtitle;
}
and use List<Item> instead in the adapter. You can apply a subscript given subtitle is null or not in the adapter's getView
class MyAdapter extends ArrayAdapter<Item>{
...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// assuming proper view holder pattern is applied here
Item = getItem(position);
if (item.subtitle != null){
//apply subscript state
}else{
//un-apply subscript state
}
return view;
}
}
I have a basic object called Employee. You can retrieve an Employee's name and id using .getName() and .getId().
I want to use Jeff Sharkey's SeparatedListAdapter to build a sectioned list. For the list items though, I need to use my custom Employee objects for the items instead of just lists of Strings.
In the included examples for the SeparatedListAdapter, he uses an ArrayAdapter<String> and a SimpleAdapter() for populating the list.
Is there any way to use a custom object/class, like my Employee class? The reason I'm needing to do this, is that when I click on an item in the list, I want to retrieve the actual Employee object that I used for that item and then retrieve the ID of the employee so I can display information pertaining to that Employee.
I'm a little bit confused on how to use Adapters properly. Should I make my own adapter or something?
You'll need to implement your custom adapter. Ex:
class CustomAdapter extends ArrayAdapter<Employee>
and when use it on SeparatedListAdapter:
CustomAdapter workersAdapter = new CustomAdapter(this, resourceID, workersList);
SeparatedListAdapter separated = new SeparatedListAdapter(this);
...
separated.addSection("Workers", workersAdapter);
Edit:
Overrride getView Method in your CustomAdapter to create views with Employee info. Like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if(convertView == null){
view = convertView;
}else
view = mInflater.inflate(R.layout.your_row, null);
Employee e = getItem(position);
((TextView)view.findViewById(R.id.textview_id)).setText(e.getId());
//...
return view;
}
I want to create list with different types of items. They should call different intents or do other things (display a map etc.) . It should act like contact details. Numbers of items and actions is predefined.
How to achieve this effect elegantly? I don't need exact code but guidelines and information where to look. Any help will be appreciated :)
UPDATE:
By "this effect" I mean creating a list of different types of items (onClickListener, layout). On picture above you can see that you have a contact with various options: calling somebody, emailing, chatting, looking at google maps etc. All of those options are grouped at list.
I'm wondering if it could be achieved by xml layout without defining custom Adapter class. I want also be able to add some static header rows with eg. category name.
The only way I see to achieve this would be to indeed create a custom adapter class.
I'm using this to create a file browsers, with different actions based on wether the item selected is a file or a folder.
Basically, You need to create a custom adapter extending ArrayAdapter (you can use another base class if all your items inherit from the same class). Here's a sample code :
public class MyCustomAdapter extends ArrayAdapter<Object> {
public MyCustomAdapter(Context context, int textViewResourceId,
ArrayList<Object> objects) {
super(context, textViewResourceId, objects);
mList = objects;
}
public View getView(int position, View convertView, ViewGroup parent) {
Object obj = mList.get(position);
View v = convertView;
LayoutInflater vi = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (obj.getClass().isAssignableFrom(MyClass1.class)){
v = vi.inflate(R.layout.myclass1_item_layout, null);
setupViewClass1(obj,v);
} else if (obj.getClass().isAssignableFrom(MyClass2.class)){
v = vi.inflate(R.layout.myclass2_item_layout, null);
setupViewClass2(obj,v);
}
return v;
}
private void setupViewClass1 (Object obj, View v){
// setup the content of your view (labels, images, ...)
}
private void setupViewClass2 (Object obj, View v){
// setup the content of your view (labels, images, ...)
}
private ArrayList<Object> mList;
}
Then you need to add an OnItemClickListener as well as an OnCreateContextMenuListener to handle the click and longpress event on your list, again making a filter on the class of your object.