Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - ListView dynamic Buttons for each row calling dynamic listeners

I'm new to android, i've spent the last 2 days trying previous examples and online solutions but I just can't seem to get my head around it :(

I'm able to display a list view, parse some json from online and store a book title, book description and book ID and display this data in the listview. I want to be able to put a 'download' button in each row for the ListView, each button will correspond to its book ID on Click() and the action listener will download the book by appending that ID to a url. e.g www.books.com/download_book1 or /download_book2....

Here is my code. Catalogue.java class

public class Catalogue extends ListActivity {

private JSONObject json;
private ListView lv;

private ArrayList<Integer> alKey = new ArrayList<Integer>();

@Override
    public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState); //icicle
            setContentView(R.layout.shelflist);
            ArrayList<HashMap<String, String>> mylist = new ArrayList<HashMap<String, String>>();


....
try{

                JSONArray entries = json.getJSONArray("entries");

                for(int i=0;i<entries.length();i++){                        
                    HashMap<String, String> map = new HashMap<String, String>();    
                    JSONObject e = entries.getJSONObject(i);

                    alKey.add(e.getInt("key")); 
                    map.put("id",  String.valueOf(i));
                    map.put("title", "Title:" + e.getString("title"));
                    map.put("description", "Description: " +  e.getString("description"));
                    mylist.add(map);


                }       
            }catch(JSONException e)        {
                 Log.e("log_tag", "Error parsing data "+e.toString());
            }

            ListAdapter adapter = new SimpleAdapter(this, mylist , R.layout.shelfrow, 
                            new String[] { "title", "description" }, 
                            new int[] { R.id.item_title, R.id.item_subtitle });

            setListAdapter(adapter);

                    lv = getListView();
                    lv.setTextFilterEnabled(true);  

.....

This is as far as I get. I don't know how to add 1 button per row in the List and assign an action listener to each button. I also have a shelfrow.xml file (textView, textView for item_title and item_subtitle) and a shelflist.xml file (ListView). I have a shelf.xml file with

like image 800
Plokoon Avatar asked Dec 27 '22 20:12

Plokoon


2 Answers

Basically you need to learn the concept of ListAdapter.

Here's the short story: picture an object that holds the data to be displayed inside a list, along with the way to display each line individually. That's your ListAdapter. Now take each individual line: it's a book with a title and an OnClickListener. It's rendered inside a View with a TextView (for the title) and a Button (for the OnClickListener). All you need to do is give one View to the adapter that will be used for each line, and a List of the books you want to be inside the list.

Here's some sample code. I hope it clears things up a bit

private class MyItemModel{ //that's our book
    String title; // the book's title
            String description;
            long id;
    OnClickListener listener = new OnClickListener(){ // the book's action
        @Override
        public void onClick(View v) {
                            // the default action for all lines
            doSomethingWithTheBookTitleOrUniqueId(this);
        }
    };
}

private class MyListAdapter extends BaseAdapter{
    View renderer;
    List<MyItemModel> items;

            // call this one and pass it layoutInflater.inflate(R.layout.my_list_item)
    public MyListAdapter(View renderer) {
        this.renderer = renderer;
    }

            // whenever you need to set the list of items just use this method.
            // call it when you have the data ready and want to display it
    public void setModel(List<MyItemModel> items){
        this.items = items;
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return items!=null?items.size():0;
    }
    @Override
    public Object getItem(int position) {
        return items!=null?items.get(position):null;
    }
    @Override
    public long getItemId(int position) {
        return items!=null?items.get(position).id:-1;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView==null){
            convertView = renderer;
        }
        MyItemModel item = items.get(position);
             // replace those R.ids by the ones inside your custom list_item layout.
        TextView label = (TextView)convertView.findViewById(R.id.item_title);
        label.setText(item.label);
        Button button = (Button)convertView.findViewById(R.id.item_button);
        button.setOnClickListener(item.listener);
        return convertView;
    }
}

In order to pass the List, instead of putting the data inside your list of hashmaps you can do this for instance (be careful, I also updated the MyItemModel and MyListAdapter to your need, added the id and description properties):

List<MyItemModel> myListModel = new ArrayList<MyItemModel>();
try{
    JSONArray entries = json.getJSONArray("entries");
    for(int i=0;i<entries.length();i++){                        
        MyItemModel item = new MyItemModel();    
        JSONObject e = entries.getJSONObject(i);
        alKey.add(e.getInt("key")); 
        item.id = i;
        item.title = e.getString("title");
        item.description = e.getString("description");
        // you can change the button action at this point: 
        // item.onClickListener = new OnClickListener(){...};
        myListModel.add(item);
    }

}catch(JSONException e)        {
    Log.e("log_tag", "Error parsing data "+e.toString());
}
ListAdapter adapter = new MyListAdapter(getLayoutInflater().inflate(R.layout.shelfrow, this));
adapter.setModel(myListModel);
setListAdapter(adapter);
lv = getListView();
lv.setTextFilterEnabled(true); 
like image 117
Thomas Philipakis Avatar answered Dec 30 '22 10:12

Thomas Philipakis


You can create your own class extending ArrayAdapter that will hold your list and set onClickListener to the Button in each row.

But in getView method of your ArrayAdapter you have to create a new view every time.

for example - row layout

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="110dp"
    android:background="#FFF"
    android:layout_width="fill_parent">

<LinearLayout
              android:layout_width="fill_parent"
              android:background="#FFF"
              android:orientation="vertical"
              android:padding="2dp"
              android:layout_height="110dp">
    <TextView android:id="@+id/list_item_title"
              android:background="#FFF"
              android:layout_width="fill_parent"
              android:layout_height="40dp"/>
    <Button android:id="@+id/download_button"
            android:gravity="center"
            android:text="Download"
            android:layout_height="35dp"/>
</LinearLayout>

</RelativeLayout>

and getView method in ArrayAdapter

private List<Map<String, String>> jsonMapList;

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View v = inflater.inflate(R.layout.list_item, null);
    // here you set textview values (title and description)
    // TextView title = (TextView) v.findViewById(R.id.list_item_title);
    // title.setText('bla');

    // and set OnClickListener
    Button button = (Button) v.findViewById(R.id.download_button);                
    button.setOnClickListener(new View.OnClickListener() {
        public void onClick(View view) {
            downloadFile(getUrl(position));
        }
    });

    return v;
}

// method that downloads file
private void downloadFile(String url) {}

// get url from your list by index
private String getUrl(int index) {
    return jsonMapList.get(index).get("url");
}

Usage of Map is unnecessary, you could use any object you prefer.

In activity class

    CustomAdapter listAdapter = new CustomAdapter(this, android.R.layout.simple_list_item_single_choice, jsonMapList);
    setListAdapter(listAdapter);
like image 21
sidslog Avatar answered Dec 30 '22 10:12

sidslog