3

I have a Custom Listview that it is updated from a EditText component in a dialog. I have the custom row, the adapter class and the custom dialog all working but I can't seem to trigger the code in the adatper class that would add the text from the edit text control to the list. Here is my activity code, let me know if you want the adapter code. It worked before I added the custom row and adapter to the list :(

Symptom of problem: emailAdapter.notifyDataSetChanged(); does nothing

public class InvitePlayers_Activity extends Activity {
    ListViewAdapter emailAdapter = null;
    ImageView imgView_mail;
    ImageView imgView_confirm;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE); //remove title bar
        setContentView(R.layout.activity_inviteplayers);

        //Generate list View from ArrayList
        displayListView();
    }

    private void displayListView() {
        //assign controls
        final ListView listView = (ListView) findViewById(R.id.listView_invitePlayers);
        imgView_mail = (ImageView)findViewById(R.id.imgView_mail);

        //Test data
        ArrayList<String> inviteNew = new ArrayList<String>();
        final ArrayList<ArrayList<String>> inviteList = new ArrayList<ArrayList<String>>();

        emailAdapter = new ListViewAdapter(this,inviteList);
        listView.setAdapter(emailAdapter);

        // Assign adapter to ListView
        listView.setTextFilterEnabled(true);

        //Edit listeners
        imgView_mail.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view)
            {
                        //variables
                        final String enteredMail = "testListViewEntry";
                        final ArrayList<ArrayList<String>> inviteList = new ArrayList<ArrayList<String>>();
                        ArrayList<String> invite = new ArrayList<String>();
                        invite.add(0, enteredMail);//add first email
                        invite.add(1,"icon_invitestatussent.png"); //add first status icon
                        inviteList.add(invite);
                        emailAdapter.notifyDataSetChanged();
                        listView.setAdapter(emailAdapter);
            }
        });
    }
}

Adapter code as requested

public class ListViewAdapter extends BaseAdapter {
    private Activity context;
    ArrayList<ArrayList<String>> inviteDetails = new ArrayList<ArrayList<String>>();
    public ListViewAdapter(Activity context, ArrayList<ArrayList<String>> inviteDetails ) {
        this.inviteDetails = inviteDetails;
        this.context = context;
    }

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

    @Override
    public Object getItem(int i) {
        return inviteDetails.get(i).get(0);
    }

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

    public View getView(int position, View view, ViewGroup parent){
        //Inflater
        LayoutInflater inflater = context.getLayoutInflater();

        //get row view
        if (view == null) {
            LayoutInflater mInflater = (LayoutInflater)
                    context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);

            view = mInflater.inflate(R.layout.list_item_email, null);
        }

        //assign controls
        final TextView textView_playerEmail = (TextView) view.findViewById(R.id.textView_playerEmail);
        ImageView imgView_inviteStatus = (ImageView) view.findViewById(R.id.imgView_inviteStatus);

        //Assign control values that are dynamic
        textView_playerEmail.setText(inviteDetails.get(position).get(0));
        imgView_inviteStatus.setImageResource(R.drawable.icon_invitestatussent);

        return view;
    }

    @Override
    public void notifyDataSetChanged() {
        super.notifyDataSetChanged();
    }
}

Custom row xml

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

    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:padding="10dp"
        android:textSize="16sp"
        android:id="@+id/textView_playerEmail"
        android:textColor="@color/white"
        android:text="item1">
    </TextView>

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imgView_inviteStatus" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imgView_remove"
        android:src="@drawable/btn_cancel" />
</LinearLayout>

The activity layout

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="20"
            android:background="@color/yellow"
            android:layout_margin="20dp"
            android:padding="5dp">

            <LinearLayout
                android:orientation="horizontal"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:weightSum="1"
                android:gravity="left|center">


                <ImageView
                    android:layout_width="45dp"
                    android:layout_height="34dp"
                    android:id="@+id/imgView_mail"
                    android:src="@drawable/btn_mail"
                    android:layout_weight="0.22"
                    android:padding="3dp" />
            </LinearLayout>

            <ListView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:id="@+id/listView_invitePlayers"
                android:layout_gravity="center_horizontal" />
        </LinearLayout>

        <ImageView
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:id="@+id/btn_confirm"
            android:src="@drawable/btn_confirm"
            android:clickable="false"
            android:adjustViewBounds="true"
            android:layout_gravity="center_horizontal"
            android:padding="2dp"
            android:layout_weight="1" />
    </LinearLayout>

</FrameLayout>

enter image description here

Fearghal
  • 10,569
  • 17
  • 55
  • 97
  • We're probably going to need your `Adapter` code to give you an answer. – Mauker May 26 '15 at 15:02
  • No problem. I've updated the answer. See if that was the problem. – Mauker May 26 '15 at 15:44
  • Just one more question... Are you trying to show images and text on this ListView? – Mauker May 26 '15 at 16:14
  • I am but i have havnt done anything with the images just yet – Fearghal May 26 '15 at 16:15
  • If you're trying a more complex Layout, that is not just Text OR ImageView, you'll have to subclass BaseAdapter instead of ArrayAdapter. I'll update my answer with an example. – Mauker May 26 '15 at 16:16
  • There are three problems with the `BaseAdapter` you'll have to check before I go any further. **First**, the `getItem()` method should return the object you want to show, which is stored in your ArrayList. **Second**, your `getCount()` shouldn't return 0, but the size of your Array. **Third**, change your `getItemID()` to `return i;`. See if it works after that. And yeah, this class should replace the previous one you were using. – Mauker May 27 '15 at 13:26
  • No luck, i have a suggestion though. I see my adapter is hit at @getCount only after the activity calls 'emailAdapter.notifyDataSetChanged();' and this override is never hit in adapter. – Fearghal May 27 '15 at 13:51
  • So... 2 suggestions. How to hit adapter after notifyDataSetChanged at the correct spot. ...and more likely....do i not need to update my adapter after updating my arraylist? invite.add(0, enteredMail);//add first email invite.add(1,"icon_invitestatussent.png"); inviteList.add(invite); emailAdapter.notifyDataSetChanged(); – Fearghal May 27 '15 at 13:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/78916/discussion-between-mauker-and-fearghal). – Mauker May 27 '15 at 13:51
  • my code updated in question – Fearghal May 27 '15 at 13:52

1 Answers1

3

Well, for what we discussed, you wanted something like this:

Final result

When you want to make a custom ListView, you have to write your own adapter. In this particular case I'm subclassing the BaseAdapter class.

This custom adapter will take hold of my data model, and will inflate the data to my ListView rows.

First, I'll create the XML of my custom row. You can see the code below.

item_mail.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="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Test text"
        android:id="@+id/tv_mail"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_centerVertical="true"/>

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/iv_icon"
        android:layout_alignParentTop="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:src="@android:drawable/ic_menu_report_image" />
</RelativeLayout>

Here I've created a row that displays a text and an image.

Now, I'll create my custom adapter to handle this XML. As you can see below.

MailAdapter.java

public class MailAdapter extends BaseAdapter {

    private static final String LOG_TAG = MailAdapter.class.getSimpleName();

    private Context context_;
    private ArrayList<ArrayList<String>> mailitems;

    public MailAdapter(Context context, ArrayList<ArrayList<String>> mailitems) {
        this.context_ = context;
        this.mailitems = mailitems;
    }

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

    @Override
    public Object getItem(int position) {
        return mailitems.get(position).get(0);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            LayoutInflater mInflater = (LayoutInflater)
                    context_.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);

            convertView = mInflater.inflate(R.layout.item_mail, null);
        }

        TextView tv_mail = (TextView) convertView.findViewById(R.id.tv_mail);
        ImageView iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon);

        String mail = mailitems.get(position).get(0);
        String icon = mailitems.get(position).get(1);

        Log.d(LOG_TAG,"Mail: " + mail + " mail_icon: " + icon);

        tv_mail.setText(mail);
        // iv_icon.setImageURI(); Here you can do whatever logic you want to update your image, using URI's, ID's, or something else.

        return convertView;
    }
}

Ok. Now we have everything to make this work. In your Activity class, do something like that:

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <TextView
        android:text="@string/hello_world"
        android:id="@+id/tv_header"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true" />

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/tv_header">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Add mail"
            android:id="@+id/button"
            android:layout_gravity="center" />

        <ListView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/listView" />
    </LinearLayout>

</RelativeLayout>

MainActivity.java

public class MainActivity extends ActionBarActivity {

    private int numMail = 1; // Dummy int to create my items with different numbers.

    private MailAdapter mailAdapter; // Your custom adapter.

    private ArrayList<ArrayList<String>> mailItems; // This is going to be your data structure, everytime you change it, call the notifyDataSetChanged() method.

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

        Button bt = (Button) findViewById(R.id.button);
        ListView lv_mail = (ListView) findViewById(R.id.listView);
        mailItems = new ArrayList<>();
        mailAdapter = new MailAdapter(this,mailItems);
        lv_mail.setAdapter(mailAdapter);

        bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addItem(); // The method I'm using to insert the item. Look for it below.
            }
        });
    }

    // Here I'm creating a new ArrayList, and appending it to my 'mailItems' Array. After that, I'm notifying the adapter that my data changed.
    private void addItem() {
        ArrayList<String> mail = new ArrayList<>();

        mail.add(0,"mail " + numMail++);
        mail.add(1,"path_to_image"); // Depending on what you want to do, put your path, URI, or whatever other way you want to store that image data.

        mailItems.add(mail); // Inserting the data on the ArrayList.
        mailAdapter.notifyDataSetChanged(); // Notifying the adapter that my ArrayList was modified.
    }
}

This should do the trick.

I guess your problem was that you weren't updating the same ArrayList that was in your custom adapter. That's why when you called notifyDataSetChanged()nothing happened, I mean. You were creating a new ArrayList, which wasn't the same that was in your adapter. So here's what I did... I've made the ArrayList global, and then I've used it in my custom adapter constructor. After that, when the user triggers the onClick() method of my button, I'm inserting some new data on my global Array, and then I'm notifying the adapter that the data changed.

You can read a little more about that here and I've found a similar question here, which you can read as well.

Edit: Another related question, which might be an interesting read.

Community
  • 1
  • 1
Mauker
  • 11,237
  • 7
  • 58
  • 76
  • 2
    Comments are not for extended discussion; this conversation has been [moved to chat](http://chat.stackoverflow.com/rooms/78914/discussion-on-answer-by-mauker-listview-updated-from-edittext). – Taryn May 27 '15 at 13:49
  • Ok. Sorry about that. I'll remember next time. Thanks for the advice. – Mauker May 27 '15 at 13:50
  • 1
    Absolutely fantastic Mauker. Superstar. Thx for sticking with this. The disconnect was that i was creating a new arraylist, i consolidated all code into the onCreate method to simplify and it worked, will take a further look to understand why the global arraylist fixed it as AFAICS my arraylist was fine where it was. Again, thx for all the time and effort. – Fearghal May 28 '15 at 13:35