6

I want to create a ListView where each row would have two buttons. Button1 and Button2.

I have read few tutorials on how to create CustomAdapter or extend BaseAdapter, but none of them worked in my case, that is why I'm opening a new question here.

I have a static (not changing) String Array in my strings.xml:

 <string-array name="locations_array">
   <item>Item1</item>
   <item>Item2</item>
 </string-array>

I want to use this Array in my ListView ( I manage to create a normal ListView with onClick event, but now I want to add buttons to each row ) and add two buttons to each row where each will have their own onClick event.

I have followed steps from this answers here and few other tutorials on web, but I always get NullPointerException when trying to setText.

CustomAdapter.java:

public class CustomAdapter extends BaseAdapter implements ListAdapter {
    private ArrayList<String> list = new ArrayList<>();
    private Context ctx;

    public CustomAdapter(ArrayList<String> list, Context ctx){
        this.list = list;
        this.ctx = ctx;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        Log.d("TAG", list.get(position));
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = convertView;
        if(view == null){
            LayoutInflater inflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.layout_listview,null);
        }
        Log.d("TAG", list.get(position));
        TextView listItemText = (TextView) view.findViewById(R.id.list_item_string);
        listItemText.setText(list.get(position));

        Button localData = (Button) view.findViewById(R.id.button1);
        Button onlineData = (Button) view.findViewById(R.id.button2);

        localData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        onlineData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        return view;
    }
}

And here I use the adapter in my Fragment:

locationsArray = getResources().getStringArray(R.array.locations_array);
ArrayList<String> data = new ArrayList<>(Arrays.asList(locationsArray));
CustomAdapter adapter = new CustomAdapter(data,getActivity());
listView.setAdapter(adapter);

But this doesn't work. All tutorials are focusing on something else then what I need. I just need simply to put two buttons on each row, with existing String Array. I dont want to add anything dynamically etc. Just use that String Array that I have already created.

EDIT (adding XML files I forgot!)

R.layout.layout_listview (the EditText is to filter the List):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <EditText
        android:id="@+id/et_filter"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:hint="Search Location">
    </EditText>
    <ListView
        android:id="@+id/list"
        android:layout_height="wrap_content"
        android:layout_width="match_parent">
    </ListView>


</LinearLayout>

R.layout.layout_listview_buttons:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/list_item_string"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_alignParentLeft="true"
        android:paddingLeft="8dp"
        android:textSize="18sp"
        android:textStyle="bold" />

    <Button
        android:id="@+id/button1"
        android:layout_width="80dp"
        android:layout_height="40dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:text="Online Data"

        android:textColor="#0099CC" />

    <Button
        android:id="@+id/button2"
        android:layout_width="80dp"
        android:layout_height="40dp"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/button1"
        android:layout_marginTop="3dp"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:text="Local Data"
        android:textColor="#0099CC" />



</RelativeLayout>

LogCat Exception:

12-15 11:24:10.852  14626-14626/com.inodroid.myweatherapp E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.inodroid.myweatherapp, PID: 14626
    java.lang.NullPointerException
            at com.inodroid.myweatherapp.Data.CustomAdapter.getView(CustomAdapter.java:59)

Line in code:

listItemText.setText(list.get(position));

Hope someone can help me out here.

Cheers

Community
  • 1
  • 1
David Kasabji
  • 1,049
  • 3
  • 15
  • 32
  • Please paste your R.layout.layout_listview file :-) – Kelevandos Dec 15 '14 at 10:13
  • @Kelevandos aah, sorry, dont know how I forgot about adding XML files. I put both of them up! – David Kasabji Dec 15 '14 at 10:18
  • I think that the stack trace will also be usefull – Lampapos Dec 15 '14 at 10:20
  • I think you are inflating wrong xml file in adapter. change `view = inflater.inflate(R.layout.layout_listview,null);` to `view = inflater.inflate(R.layout.layout_listview_buttons,null);` – Shvet Dec 15 '14 at 10:26
  • 1
    That was it! Oh-My-God..so embarassing, thanks alot to all you guys !! – David Kasabji Dec 15 '14 at 10:30
  • rather you should keep different name of both xml file. sometimes making almost similar cause errors. – Shvet Dec 15 '14 at 10:34
  • @DhavalGondaliya Yea, good idea. Can you help me out with something else? Now my ItemClick Listener does not work when I select an Item from ListView, it doesnt store the value as it should. No Errors, but does nothing. – David Kasabji Dec 15 '14 at 10:36

4 Answers4

1

Please use the ViewHolder pattern. You will need these:

item_listrow.xml:

<LinearLayout
android:layout_height = "match_parent";
andorid:layout_width  = "match_parent";
andorid:orientation   = "horizontal";
>

<Button
android:id = "@+id/button1"
android:layout_height = "match_parent";
andorid:layout_width  = "0dp";
andorid:layout_weight = 1;
/>

<Button
android:id = "@+id/button2"
android:layout_height = "match_parent";
andorid:layout_width  = "0dp";
andorid:layout_weight = 1;
/>

</LinaerLayout>

And your Adapter getView() method should look like this:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View view = convertView;
    if(view == null){
        LayoutInflater inflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.layout_listrow,null);
        Holder h = new Holder();
        h.button1 = (Button) view.findViewById(R.id.button1);
        h.button2 = (Button) view.findViewById(R.id.button2);
        view.setTag(h);      
    }

    Holder holder = (Holder) view.getTag();

    holder.button1.setText(list.get(0));
    holder.button2.setText(list.get(1));
    listItemText.setText(list.get(position));

    holder.button1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

        }
    });

    holder.button2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

        }
    });

    return view;
}

The Holder class used above: (just nest it inside the Adapter class)

public static class Holder {
    Button button1;
    Button button2;
}

Here is the code for hiding the buttons based on a given method isDeveloperMode():

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View view = convertView;
    if(view == null){
        LayoutInflater inflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.layout_listrow,null);
        Holder h = new Holder();
        h.button1 = (Button) view.findViewById(R.id.button1);
        h.button2 = (Button) view.findViewById(R.id.button2);
        view.setTag(h);      
    }

    Holder holder = (Holder) view.getTag();

    if(isDeveloperMode()){
        holder.button1.setVisibility(View.VISIBLE);
        holder.button2.setVisibility(View.VISIBLE);
        holder.button1.setText(list.get(0));
        holder.button2.setText(list.get(1));
        listItemText.setText(list.get(position));

        holder.button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });

        holder.button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
    } else {
        holder.button1.setVisibility(View.GONE);
        holder.button2.setVisibility(View.GONE); 
    }

    return view;
}
Kelevandos
  • 7,024
  • 2
  • 29
  • 46
  • The code was hand-typed so please excuse any errors or typos ;-) – Kelevandos Dec 15 '14 at 10:22
  • I have added XMLs where the buttons are declared. But I didnt use Holder class that true, I'll try adding that. However, I get an LogCat error "NullPointerException" on my `setText(list.get(position)` – David Kasabji Dec 15 '14 at 10:23
  • This worked indeed, but the onClickListeners nwo dont work, they dont recognize "button1" and "button2"..should I add holder.button1 ? – David Kasabji Dec 15 '14 at 10:32
  • Indeed, by bad, it has to be `holder.button1` and `holder.button2`(corrected the answer) – Kelevandos Dec 15 '14 at 10:36
  • Thanks for help! I am wondering now why my ItemClickListener does not work in my ListView ? When I click on Iem from ListView, it doesn't do anything. No error. But it should store the selected item into variable, but now it does nothing. – David Kasabji Dec 15 '14 at 10:37
  • It is because the buttons are handling the clicks, not the list adapter :) – Kelevandos Dec 15 '14 at 10:39
  • I dont want that tho. Buttons should do something else when clicked. But the user can click the Item from listView also, to show something. How can I correct that? :) – David Kasabji Dec 15 '14 at 10:42
  • You can edit the `item_listrow.xml` so that there is some space around/next to the buttons. clicking this space will activate the adapter's listener. I am still unsure what your goal is, though. – Kelevandos Dec 15 '14 at 10:45
  • The buttons will appear only in Developer Mode, which will be monitored by boolean. But if the developer mode is set to false, then the buttons wont be displayed. Should I use seperate adapters for this cause? and try not to mix everything in one class? – David Kasabji Dec 15 '14 at 10:47
  • but how can now I retrieve the Text when user clicks on the button? Each row has these buttons. Now when user clicks one of the buttons, it should store the Text from ListView Item. – David Kasabji Dec 15 '14 at 10:58
  • What Text? Is there some TextView to get it from? – Kelevandos Dec 15 '14 at 11:01
  • Yes: `listItemText.setText(list.get(position));` – David Kasabji Dec 15 '14 at 11:04
  • Ok, simply change my code to inflate your xml. Then add a TextView type field to the Holder and bind it in the if (view == null) condition just like the buttons. – Kelevandos Dec 15 '14 at 11:13
  • do you maybe know how can I get the position of the Item where I clicked the button? I know it's on the same row, but I don't know how to get the position of the Item (in the same row as button was clicked). – David Kasabji Dec 15 '14 at 13:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/66929/discussion-between-kelevandos-and-androidnfc). – Kelevandos Dec 15 '14 at 13:42
1

Please try the following :

main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<ListView
    android:id="@+id/lvItems"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
</ListView>

</LinearLayout>

MainActivity.java :

public class MainActivity extends Activity {

private ListView lvItems;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    lvItems = (ListView) findViewById(R.id.lvItems);
    String[] locationsArray = getResources().getStringArray(
            R.array.locations_array);
    CustomAdapter adapter = new CustomAdapter(this, locationsArray);
    lvItems.setAdapter(adapter);

lvItems.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view,
                int position, long id) {
            ViewHolder holder = (ViewHolder) view.getTag();
            String item = holder.getItem();
            // Do what ever with your Item.
            // If You need the position, you can take it from above
            // position.
        }
    });
}

}

list_item.xml :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dp" >

<Button
    android:id="@+id/btnLocalData"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="local Data" />

<TextView
    android:id="@+id/tvItem"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignBottom="@+id/btnLocalData"
    android:layout_alignTop="@+id/btnOnlineData"
    android:layout_toLeftOf="@+id/btnOnlineData"
    android:layout_toRightOf="@+id/btnLocalData"
    android:gravity="center"
    android:text="Item"
    android:textSize="16dp"
    android:textStyle="bold" />

<Button
    android:id="@+id/btnOnlineData"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:text="Online Data" />

</RelativeLayout>

CustomAdapter.java :

public class CustomAdapter extends ArrayAdapter<String> {

private LayoutInflater lf;

public CustomAdapter(Context context, String[] objects) {
    super(context, 0, objects);
    lf = LayoutInflater.from(context);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;
    if (convertView == null) {
        convertView = lf.inflate(R.layout.list_item, parent, false);
        holder = new ViewHolder();
        holder.btnLocalData = (Button) convertView
                .findViewById(R.id.btnLocalData);
        holder.btnOnlineData = (Button) convertView
                .findViewById(R.id.btnOnlineData);
        holder.tvItem = (TextView) convertView.findViewById(R.id.tvItem);
        holder.initListeners();
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    holder.setData(getItem(position));

    return convertView;
}

public static class ViewHolder {
    TextView tvItem;
    Button btnOnlineData;
    Button btnLocalData;
    String mItem;

    public String getItem(){
        return mItem;
    }

    public void setData(String item) {
        mItem = item;
        tvItem.setText(item);
    }

    public void initListeners() {
        btnLocalData.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Toast.makeText(v.getContext(),
                        "Local Data Clicked : " + mItem, Toast.LENGTH_LONG)
                        .show();
            }
        });
        btnOnlineData.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Toast.makeText(v.getContext(),
                        "Online Data Clicked : " + mItem, Toast.LENGTH_LONG)
                        .show();
            }
        });
    }

}

}
Eldhose M Babu
  • 14,382
  • 8
  • 39
  • 44
  • This is a great answer! :) But now I have problem, that onItemClick for ListView items doesnt work. Only Button Clicks work. But I also want that Items from ListView are clickable and have their own onItemClickListeners, how should I implement this with my custom adapter? – David Kasabji Dec 15 '14 at 10:45
  • Hey it's me again. Do you maybe now, how can I get the position of ListView Item in the same row that the button was clicked ? E.g. when I click the button, I want to store the text of the ListView item, that is in the same row as the clicked button. – David Kasabji Dec 15 '14 at 13:43
0

Change your code to the following in adapter

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if(convertView== null){
        LayoutInflater inflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView= inflater.inflate(R.layout.layout_listview,null);
    }
    Log.d("TAG", list.get(position));
    TextView listItemText = (TextView) convertView.findViewById(R.id.list_item_string);
    listItemText.setText(list.get(position));

    Button localData = (Button) convertView.findViewById(R.id.button1);
    Button onlineData = (Button) convertView.findViewById(R.id.button2);

    localData.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

        }
    });

    onlineData.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

        }
    });

    return convertView;
}
Viral Thakker
  • 547
  • 2
  • 10
0

Try instead of

@Override
public View getView(int position, View convertView, ViewGroup parent) {    
            View view = convertView;
            if(view == null){
                LayoutInflater inflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.layout_listview,null);
            }
            Log.d("TAG", list.get(position));
            TextView listItemText = (TextView) view.findViewById(R.id.list_item_string);

use

@Override
public View getView(int position, View convertView, ViewGroup parent) {
            View view = super.getView(position, convertView, parent);       
            Log.d("TAG", list.get(position));
            TextView listItemText = (TextView) view.findViewById(R.id.list_item_string);
dr_yand
  • 171
  • 2
  • 11