Подтвердить что ты не робот

ExpandableListAdapter onClickListener, вызов notifyDataSetChanged()

У меня есть пользовательский адаптер, который использует onclickListener для изменения родительского текста, я не могу понять, как заставить метод notifyDataSetChanged работать в адаптере.

что должно произойти, я имею расширяемое представление, и когда вы нажимаете кнопку, находящуюся внутри дочернего элемента, он обновляет родительский текст текстом дочернего элемента...

Вот пример изображения того, что ДОЛЖНО произойти. перед нажатием кнопки ИСПОЛЬЗУЙТЕ ЭТО: enter image description here

и после нажатия кнопки ИСПОЛЬЗУЙТЕ ЭТО: enter image description here

"Выберите Ингредиент" измените на "Райс, чашки" или на что-либо, нажав на

поэтому в моем показанном коде после нажатия кнопки он должен обновить родительский элемент, который он делает, а затем обновить представление, которое я не могу сделать по какой-либо причине?

Вот мой код, спасибо заранее!

    package com.example.andrew.mypantry;

import android.content.Context; 
import android.graphics.Typeface;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import org.w3c.dom.Text;

import java.util.HashMap;
import java.util.List;

/**
 * Created by andrew on 7/1/2015.
 */
public class IngExpandableListAdapter extends BaseExpandableListAdapter {

private Context context;
private List<String> listDataHeader; //header titles
//child data in format of <header title, child title>
private HashMap<String, List<String>> listDataChild;

public IngExpandableListAdapter(Context context, List<String> listDataHeader,
                                HashMap<String, List<String>> listDataChild) {
    this.context = context;
    this.listDataHeader = listDataHeader;
    this.listDataChild = listDataChild;
}

@Override
public Object getChild(int groupPosition, int childPosition) {
    return this.listDataChild.get(this.listDataHeader.get(groupPosition)).get(childPosition);
}

@Override
public long getChildId(int groupPosition, int childPosition) {
    return childPosition;
}

private void test() {


    //notifyDataSetChanged();
    //super.notifyDataSetChanged();
    //this.notifyDataSetChanged();
}
@Override
public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild,
                         View convertView, ViewGroup parent) {
    final String childText = (String) getChild(groupPosition,childPosition);

    if (convertView == null) {
        LayoutInflater layoutInflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = layoutInflater.inflate(R.layout.ingredient_list_item, null);
    }

    TextView txtListChild = (TextView) convertView.findViewById(R.id.ingredientContentTextView);

    txtListChild.setText(childText);

    //handle button
    Button ingredientButton = (Button)convertView.findViewById(R.id.useIngredient_button);
    ingredientButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //change header to ingredient selected
            listDataHeader.set(groupPosition, childText);
            //test();
        }
    });

    return convertView;
}

@Override
public int getChildrenCount(int groupPosition) {
    return this.listDataChild.get(this.listDataHeader.get(groupPosition)).size();
}

@Override
public Object getGroup(int groupPostion) {
    return this.listDataHeader.get(groupPostion);
}

@Override
public int getGroupCount() {
    return this.listDataHeader.size();
}

@Override
public long getGroupId(int groupPosition) {
    return groupPosition;
}

@Override
public View getGroupView(int groupPosition, boolean isExpanded,
                         View convertView, ViewGroup parent) {

    String headerTitle = (String) getGroup(groupPosition);

    if(convertView == null) {
        LayoutInflater layoutInflater = (LayoutInflater) this.context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
        convertView = layoutInflater.inflate(R.layout.ingredient_list_group, null);
    }

    //handle textView
    final TextView listHeader = (TextView) convertView.findViewById(R.id.ingredientGroupTextView);
    listHeader.setTypeface(null, Typeface.BOLD);
    listHeader.setText(headerTitle);

    //handle button
    Button deleteButton = (Button)convertView.findViewById(R.id.deleteButton);
    deleteButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //remove item from list
            Log.d("TESTING", "item should be removed");

        }
    });


    return convertView;
}

@Override
public boolean hasStableIds() {
    return false;
}

@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
    return true;
}

}

4b9b3361

Ответ 1

Я считаю, что проблема заключается в структурах данных, которые вы используете. Вы сохраняете список детей, назначенных родительскому имени в хеш-таблице. Когда имя родителя изменяется в списке родителей, назначение старых детей в хэш-таблице больше недействительно.

Я предлагаю вам использовать SparseArray<List<String>> или List<List<String>> для поля listDataChild. Он будет отображать позицию группы в список детей напрямую. Таким образом, изменение родительского имени в listDataHeader не нарушит согласованность данных, так как позиция группы останется неизменной. Теперь вы можете безопасно использовать метод notifyDataSetChanged() для обновления заголовков.

Надеюсь, что это поможет.

Ответ 2

Как упоминалось в статье, ваша проблема возникает из-за неправильной структуры данных, и вот почему ваше приложение выходит из строя. Вы используете заголовки заголовков в качестве ключей к вашему хэш-карте, а когда вы изменяете заголовок в listDataHeader, к нему применяется правильное изменение, но не к ключевому объекту внутри хэш-карты. Это означает, что ваши дети все еще существуют под старым ключом заголовка внутри хэш-карты, а новый ключ заголовка имеет null как его значение. Таким образом, вы получите NullPointerException, когда вы вызываете

this.listDataChild.get(this.listDataHeader.get(groupPosition)).get(childPosition);

так как

this.listDataChild.get(this.listDataHeader.get(groupPosition))

возвращает null

Решение будет использовать ArrayList для заголовков заголовков и ArrayList > для детей. Ключ в обоих этих списках массивов - это ваш групповой индекс.

Ответ 3

Вместо того, чтобы использовать notifyDataSetChanged(); создать интерфейс, откуда поступают данные, и реализовать его - это ваш адаптер, а затем изменить значение родителя в этой функции интерфейса, поскольку getChildView() вызывается после каждого дочернего элемента, он обновит ваш список родитель. Проверено и проверено, так как у меня такая же проблема

Предположим, вы хотите перенести данные из адаптера в адаптер

adapter.java

public interface data { public void dataMethod(String data); }

и реализовать этот метод в adapter

public void dataMethod(String data) { this.data = data; }

вызов dataMethod() из getChildView() и строки обновления, как я показал

и установить данные в методе getViewChild в адаптере

Ответ 4

Как уже упоминалось @beworker, вы не должны хранить детей в HashMap. Вместо этого используйте ArrayList и получите доступ к дочерним группам по индексу группы. Как вы можете изменить адаптер. В качестве бонуса я реализовал удаление группы.

public class IngExpandableListAdapter extends BaseExpandableListAdapter {
    private Context context;
    private List<String> listDataHeader; //header titles
    private List<List<String>> listDataChild;

    public IngExpandableListAdapter(Context context, List<String> listDataHeader,
                                    HashMap<String, List<String>> listDataChild) {
        this.context = context;
        this.listDataHeader = listDataHeader;
        this.listDataChild = new ArrayList<List<String>>();
        for (String header : listDataHeader) {
            List<String> children = listDataChild.get(header);
            if (children == null) {
                children = Collections.emptyList();
            }
            this.listDataChild.add(children);
        }
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return this.listDataChild.get(groupPosition).get(childPosition);
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public View getChildView(final int groupPosition, final int childPosition,
                             boolean isLastChild, View convertView,
                             ViewGroup parent) {
        final String childText = (String) getChild(groupPosition, childPosition);

        if (convertView == null) {
            LayoutInflater layoutInflater = LayoutInflater.from(this.context);
            convertView =
                    layoutInflater.inflate(R.layout.ingredient_list_item, null);
        }

        TextView txtListChild =
                (TextView) convertView.findViewById(R.id.ingredientContentTextView);
        txtListChild.setText(childText);

        //handle button
        Button ingredientButton =
                (Button) convertView.findViewById(R.id.useIngredient_button);
        ingredientButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //change header to ingredient selected
                listDataHeader.set(groupPosition, childText);
                notifyDataSetChanged();
            }
        });

        return convertView;
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return this.listDataChild.get(groupPosition).size();
    }

    @Override
    public Object getGroup(int groupPostion) {
        return this.listDataHeader.get(groupPostion);
    }

    @Override
    public int getGroupCount() {
        return this.listDataHeader.size();
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded,
                             View convertView, ViewGroup parent) {

        String headerTitle = (String) getGroup(groupPosition);

        if(convertView == null) {
            LayoutInflater layoutInflater = LayoutInflater.from(this.context);
            convertView =
                    layoutInflater.inflate(R.layout.ingredient_list_group, null);
        }

        //handle textView
        final TextView listHeader =
                (TextView) convertView.findViewById(R.id.ingredientGroupTextView);
        listHeader.setTypeface(null, Typeface.BOLD);
        listHeader.setText(headerTitle);

        //handle button
        Button deleteButton = (Button) convertView.findViewById(R.id.deleteButton);
        deleteButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //remove item from list
                Log.d("TESTING", "item should be removed");
                this.listDataHeader.remove(groupPosition);
                this.listDataChild.remove(groupPosition);
                notifyDataSetChanged();
            }
        });

        return convertView;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }
}

Ответ 5

Это полностью работоспособное решение. Основная идея - не использовать OnClickListener внутри Adapter, вместо этого вы должны использовать ExpandableListView.setOnChildClickListener().

MyFragment.java

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListView;

public class MyFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
        String[][] data = {
                {"Rice, cups", "Chiken, breasts", "Milk, gallons", "Beans, cups"},
                {"Orange, lemon", "Tomato, potato", "Onion, garlic"},
                {"Apple, pinapple", "Grapes, pear", "Cherry, berry", "Banana, strawberry", "Peach, apricot"},
        };

        final ChoiceAdapter adapter = new ChoiceAdapter(data);
        final ExpandableListView listView = new ExpandableListView(inflater.getContext());

        listView.setGroupIndicator(null);
        listView.setAdapter(adapter);
        listView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                adapter.selectChild(groupPosition, childPosition);
                listView.collapseGroup(groupPosition);
                // NOTE you even don't need to call notifyDataSetChanged() if you call collapseGroup()
                // adapter.notifyDataSetChanged();
                return true;
            }
        });

        return listView;
    }
}

ChoiceAdapter.java

import android.graphics.Typeface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;

import java.util.Arrays;

public class ChoiceAdapter extends BaseExpandableListAdapter {
    private final String[][] mData;
    private final int[] mChoice;

    public ChoiceAdapter(String[][] data) {
        mData = data;
        mChoice = new int[data.length];
        Arrays.fill(mChoice, -1);
    }

    @Override
    public int getGroupCount() {
        return mData.length;
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return mData[groupPosition].length;
    }

    @Override
    public Integer getGroup(int groupPosition) {
        return mChoice[groupPosition];
    }

    @Override
    public String getChild(int groupPosition, int childPosition) {
        return mData[groupPosition][childPosition];
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        if (convertView == null) {
            // TODO inflate layout from xml instead
            convertView = new TextView(parent.getContext());
        }

        // TODO use ViewHolder pattern instead
        TextView textView = (TextView) convertView;
        textView.setTypeface(null, Typeface.BOLD);

        int childPosition = getGroup(groupPosition);
        if (childPosition < 0) {
            textView.setText("Choose an ingredient");
        } else {
            textView.setText(getChild(groupPosition, childPosition));
        }

        return convertView;
    }

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        if (convertView == null) {
            // TODO inflate layout from xml instead
            convertView = new TextView(parent.getContext());
        }

        // TODO use ViewHolder pattern instead
        TextView textView = (TextView) convertView;
        textView.setText(getChild(groupPosition, childPosition));

        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }

    public void selectChild(int groupPosition, int childPosition) {
        mChoice[groupPosition] = childPosition;
    }

    @Override
    public long getGroupId(int groupPosition) { return 0; }

    @Override
    public long getChildId(int groupPosition, int childPosition) { return 0; }

    @Override
    public boolean hasStableIds() { return false; }
}