ListView with Buttons and static String Array
Asked Answered
R

4

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

Recuperator answered 15/12, 2014 at 10:8 Comment(7)
Please paste your R.layout.layout_listview file :-)Booze
@Booze aah, sorry, dont know how I forgot about adding XML files. I put both of them up!Recuperator
I think that the stack trace will also be usefullDocila
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);Dugger
That was it! Oh-My-God..so embarassing, thanks alot to all you guys !!Recuperator
rather you should keep different name of both xml file. sometimes making almost similar cause errors.Dugger
@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.Recuperator
K
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();
            }
        });
    }

}

}
Kania answered 15/12, 2014 at 10:35 Comment(2)
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?Recuperator
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.Recuperator
B
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;
}
Booze answered 15/12, 2014 at 10:20 Comment(15)
The code was hand-typed so please excuse any errors or typos ;-)Booze
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)Recuperator
This worked indeed, but the onClickListeners nwo dont work, they dont recognize "button1" and "button2"..should I add holder.button1 ?Recuperator
Indeed, by bad, it has to be holder.button1 and holder.button2(corrected the answer)Booze
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.Recuperator
It is because the buttons are handling the clicks, not the list adapter :)Booze
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? :)Recuperator
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.Booze
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?Recuperator
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.Recuperator
What Text? Is there some TextView to get it from?Booze
Yes: listItemText.setText(list.get(position));Recuperator
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.Booze
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).Recuperator
Let us continue this discussion in chat.Booze
K
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();
            }
        });
    }

}

}
Kania answered 15/12, 2014 at 10:35 Comment(2)
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?Recuperator
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.Recuperator
C
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;
}
Chang answered 15/12, 2014 at 10:23 Comment(0)
S
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);
Sonnie answered 15/12, 2014 at 10:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.