使用微调器从自定义列表视图中删除了错误的行

Posted

技术标签:

【中文标题】使用微调器从自定义列表视图中删除了错误的行【英文标题】:Wrong row deleted from custom listview with spinner 【发布时间】:2015-11-09 19:25:26 【问题描述】:

我正在开发包含自定义 ListView 的应用程序,该应用程序带有我从主活动更新的删除按钮。

我在从 ListView 中删除一行时遇到问题,虽然我正在从自定义列表视图中删除正确的索引并调用 notifyDataChanged()method,但 GUI 无法正确更新。

在这里我写了一个示例项目,就像我在想法中的真实示例一样:

activity_main.xml(主布局)包含 ListView 仅称为 listview。 listview_row.xml 包含两个 Spinners(学生姓名和年级),设置和删除按钮和文本视图。 学生班级包含两个变量:姓名(字符串)和成绩(整数)

MainActivity.java:

public class MainActivity extends Activity 
    ListView listView;
    listviewAdapter adapter;
    ArrayList<Student> students = new ArrayList<>();

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

        String[] names = new String[]"Tom", "Ben", "Gil", "Adam", "Moshe", "Adi", "Michael", "Yasmin", "Jessica", "Caroline", "Avi", "Yael";

        students.add(new Student());
        students.add(new Student());
        students.add(new Student());

        adapter = new listviewAdapter(this, students, names);
        listView = (ListView) findViewById(R.id.listView);
        listView.setAdapter(adapter);

    

    public void updateStatus(int position)
    
        View convertView = listView.getChildAt(position - listView.getFirstVisiblePosition());
        TextView tvValue = (TextView) convertView.findViewById(R.id.tv_Value);
        Spinner spName = (Spinner) convertView.findViewById(R.id.spNames);
        Spinner spGrade = (Spinner) convertView.findViewById(R.id.spGrades);

        tvValue.setText(spName.getSelectedItem().toString() + " got " + spGrade.getSelectedItem().toString());
    

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        int id = item.getItemId();
        if (id == R.id.action_Add)
        
            students.add(new Student());
            adapter.notifyDataSetChanged();
            return true;
        
        return super.onOptionsItemSelected(item);
    

listviewAdapter.java

public class listviewAdapter extends BaseAdapter

    public Activity context;
    public LayoutInflater inflater;

    private ArrayList<Student> studentID;
    private String[] studentsNames;

    public listviewAdapter(Activity context, ArrayList<Student> students, String[] names)
    
        super();
        studentID = students;
        studentsNames = names;

        this.context = context;
        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    

    @Override
    public int getCount() 
        return studentID.size();
    

    @Override
    public Object getItem(int position) 
        return studentID.get(position);
    

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


    @Override
    public int getViewTypeCount() 
        return studentID.size() + 1;
    

    @Override
    public int getItemViewType(int position) 
        return position;
    

    public class ViewHolder 
        Spinner spNames, spGrades;
        TextView tvValue;
        Button btnSet, btnRemove;
    

    @Override
    public View getView(int i, View view, final ViewGroup viewGroup)
    
        final ViewHolder holder;
        if (view == null) 
            holder = new ViewHolder();
            view = inflater.inflate(R.layout.listview_row, null);

            holder.spNames = (Spinner) view.findViewById(R.id.spNames);
            holder.spGrades = (Spinner) view.findViewById(R.id.spGrades);
            holder.tvValue = (TextView) view.findViewById(R.id.tv_Value);
            holder.btnSet = (Button) view.findViewById(R.id.btn_setValue);
            holder.btnRemove = (Button) view.findViewById(R.id.btn_Remove);
            view.setTag(holder);
            holder.spNames.setTag(0);
            holder.spGrades.setTag(0);
        
        else
            holder = (ViewHolder) view.getTag();
        

        // pop spinner names
        ArrayAdapter<String> studentsNamesAdapater = new ArrayAdapter<>
                (view.getContext(), android.R.layout.simple_spinner_dropdown_item, studentsNames);
        studentsNamesAdapater.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        holder.spNames.setAdapter(studentsNamesAdapater);

        // pop spinner grades
        String[] grades = new String[101];
        for (int grade = 0; grade < 101; grade++)
            grades[grade] = String.valueOf(grade);

        final ArrayAdapter<String> studentsGradesAdapter = new ArrayAdapter<>
                (view.getContext(), android.R.layout.simple_spinner_dropdown_item, grades);
        studentsGradesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        holder.spGrades.setAdapter(studentsGradesAdapter);


        // select the right spNames index
        holder.spNames.setSelection((Integer) holder.spNames.getTag());
        // saving spinner index
        holder.spNames.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() 
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) 
                holder.spNames.setTag(position);
            

            @Override
            public void onNothingSelected(AdapterView<?> parent) 

            
        );

        // select the right spGrades index
        holder.spGrades.setSelection((Integer) holder.spGrades.getTag());
        // saving spinner index
        holder.spGrades.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() 
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) 
                holder.spGrades.setTag(position);
            

            @Override
            public void onNothingSelected(AdapterView<?> parent) 

            
        );

        // set (variable and textview)
        holder.btnSet.setTag(i);
        holder.btnSet.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                // update studentID
                int position = (Integer) v.getTag();
                Student tmp = new Student(holder.spNames.getSelectedItem().toString(), Integer.valueOf(holder.spGrades.getSelectedItem().toString()));
                studentID.set(position, tmp);
                ((MainActivity) context).updateStatus(position);
            
        );


        // remove row
        holder.btnRemove.setTag(i);
        holder.btnRemove.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                int position = (Integer) v.getTag();
                studentID.remove(position);
                //notifyDataSetChanged();
                ((MainActivity) context).adapter.notifyDataSetChanged();

                // for debug
                String dStatus = "Vector size: " + studentID.size() + "\n";
                for (int index = 0; index < studentID.size(); index++)
                    dStatus += studentID.get(index).name + " " + studentID.get(index).grade + "\n";
                Toast.makeText(v.getContext(), dStatus, Toast.LENGTH_SHORT).show();
            
        );

        return view;
    

我的问题是 GUI 没有正确更新,GUI 上删除的项目仍然出现在屏幕上,如下所示:

有人可以指导我如何从 GUI 中删除正确的行吗?

编辑

    我更新了我的 listview 适配器并在你回答我时使用标签,但不起作用。 我在删除一个后尝试添加到学生时也发现了一个奇怪的问题。出于某种原因,它会“记住”最后一个学生并返回他的完整数据,正如您在更新图片中看到的那样。

EDIT2

如果有人想尝试,我添加 Student 类和 XML 源:

Student.java

public class Student

    public String name;
    public int grade;

    public Student()
    
        name = "";
        grade = 0;
    

    public Student(String _name, int _grade)
    
        name = _name;
        grade = _grade;
    

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_
    android:layout_
    tools:context=".MainActivity">

    <ListView
        android:layout_
        android:layout_
        android:id="@+id/listView"
        android:layout_gravity="center_horizontal"
        android:dividerHeight="2dp" />
</LinearLayout>

listview_row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_
    android:layout_
    android:background="#c4e0ff">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_
        android:layout_>

        <Spinner
            android:layout_
            android:layout_
            android:id="@+id/spNames" />

        <Spinner
            android:layout_
            android:layout_
            android:id="@+id/spGrades"
            android:layout_marginLeft="10dp" />

        <Button
            android:layout_
            android:layout_
            android:text="Set Value"
            android:id="@+id/btn_setValue" />
    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_
        android:layout_>

        <TextView
            android:layout_
            android:layout_
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Medium Text"
            android:id="@+id/tv_Value" />

        <Button
            style="?android:attr/buttonStyleSmall"
            android:layout_
            android:layout_
            android:text="Remove"
            android:id="@+id/btn_Remove"
            android:layout_marginLeft="30dp" />

    </LinearLayout>

</LinearLayout>

【问题讨论】:

***.com/questions/31666797/… 【参考方案1】:

问题出在

holder.spNames.setTag(position); 

holder.spNames.setSelection((Integer) holder.spNames.getTag());

当您在“onItemSelected()”中设置名称标签时。当您删除项目时,您会从学生列表中删除该项目,但是标签呢

假设您在 0 时删除了该项目。

现在,当调用“notifyDataSetChanged()”时,它将根据可用数据重新填充列表视图。现在,这里

else
        holder = (ViewHolder) view.getTag();
    

正在被调用。 当 getView() 中的 i= 0 时,您将获得前一个填充列表的“0 th”索引的视图。因此,“(Integer) holder.spNames.getTag()”将指向前一个标签(即前一个列表的 0)。这可能是问题的原因。

我正在发布更新的代码

listviewAdapter

public class ListviewAdapter extends BaseAdapter

public Activity context;
public LayoutInflater inflater;
private ArrayList<Student> studentID;
private String[] studentsNames;
private boolean isDeleted;
public ListviewAdapter(Activity context, ArrayList<Student> students, String[] names)

    super();
    studentID = students;
    studentsNames = names;

    this.context = context;
    this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);


@Override
public int getCount() 
    return studentID.size();


@Override
public Student getItem(int position) 
    return studentID.get(position);


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





public class ViewHolder 
    Spinner spNames, spGrades;
    TextView tvValue;
    Button btnSet, btnRemove;
    int index;


@Override
public View getView(int i, View view, final ViewGroup viewGroup)

    final ViewHolder holder;
    if (view == null) 
        holder = new ViewHolder();
        view = inflater.inflate(R.layout.listview_row, null);

        holder.spNames = (Spinner) view.findViewById(R.id.spNames);
        holder.spGrades = (Spinner) view.findViewById(R.id.spGrades);
        holder.tvValue = (TextView) view.findViewById(R.id.tv_Value);
        holder.btnSet = (Button) view.findViewById(R.id.btn_setValue);
        holder.btnRemove = (Button) view.findViewById(R.id.btn_Remove);
        Log.e("IAM", "CALLED");
        view.setTag(holder);
        //holder.spNames.setTag(0);
        //holder.spGrades.setTag(0);
    
    else
        holder = (ViewHolder) view.getTag();
    
    holder.index=i;
   if(isDeleted)
       holder.tvValue.setText(getItem(holder.index).getName()+ " got " + getItem(holder.index).getGrade()); 
            
    // pop spinner names
    ArrayAdapter<String> studentsNamesAdapater = new ArrayAdapter<String>
            (view.getContext(), android.R.layout.simple_spinner_dropdown_item, studentsNames);
    studentsNamesAdapater.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    holder.spNames.setAdapter(studentsNamesAdapater);

    // pop spinner grades
    String[] grades = new String[101];
    for (int grade = 0; grade < 101; grade++)
        grades[grade] = String.valueOf(grade);

    final ArrayAdapter<String> studentsGradesAdapter = new ArrayAdapter<String>
            (view.getContext(), android.R.layout.simple_spinner_dropdown_item, grades);
    studentsGradesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    holder.spGrades.setAdapter(studentsGradesAdapter);


    // select the right spNames index
    //holder.spNames.setSelection((Integer) holder.spNames.getTag());
    holder.spNames.setSelection(getItem(holder.index).getNameIndex());
    // saving spinner index
    holder.spNames.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() 
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) 
            //holder.spNames.setTag(position);
            getItem(holder.index).setNameIndex(position);
        

        @Override
        public void onNothingSelected(AdapterView<?> parent) 

        
    );

    // select the right spGrades index
   // holder.spGrades.setSelection((Integer) holder.spGrades.getTag());

    holder.spGrades.setSelection(getItem(holder.index).getGrageIndex());
    // saving spinner index
    holder.spGrades.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() 
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) 
           // holder.spGrades.setTag(position);
            getItem(holder.index).setGrageIndex(position);
        

        @Override
        public void onNothingSelected(AdapterView<?> parent) 

        
    );

    // set (variable and textview)
    holder.btnSet.setTag(i);
    holder.btnSet.setOnClickListener(new View.OnClickListener() 
        @Override
        public void onClick(View v) 
            // update studentID
            //final int position = getRowPosition(v);
            int position = (Integer) v.getTag();
           // Student tmp = new Student(holder.spNames.getSelectedItem().toString(), Integer.valueOf(holder.spGrades.getSelectedItem().toString()));
           // studentID.set(position, tmp);
            getItem(position).setName(holder.spNames.getSelectedItem().toString());
            getItem(position).setGrade(Integer.valueOf(holder.spGrades.getSelectedItem().toString()));
            holder.tvValue.setText(getItem(position).getName()+ " got " + getItem(position).getGrade());
            //((MainActivity) context).updateStatus(position);
        
    );


    // remove row
    holder.btnRemove.setTag(i);
    holder.btnRemove.setOnClickListener(new View.OnClickListener() 
        @Override
        public void onClick(View v) 
            //final int position = getRowPosition(v);
            int position = (Integer) v.getTag();
            studentID.remove(position);
            notifyDataSetChanged();
            //((MainActivity) context).adapter.notifyDataSetChanged();
            isDeleted=true;
            // for debug
            String dStatus = "Vector size: " + studentID.size() + "\n";
            for (int index = 0; index < studentID.size(); index++)
                dStatus += studentID.get(index).name + " " + studentID.get(index).grade + "\n";
            Toast.makeText(v.getContext(), dStatus, Toast.LENGTH_SHORT).show();
        
    );

    return view;


public void printS() 
    for (int i = 0; i <studentID.size(); i++) 
        Log.e("NAME", ""+studentID.get(i).getName());
        Log.e("GRADE", ""+studentID.get(i).getGrade());
    


学生

public class Student 
public String name;
public int grade;
private int nameIndex;
private int grageIndex;

public Student()

    name = "";
    grade = 0;


public Student(String _name, int _grade)

    name = _name;
    grade = _grade;


public String getName() 
    return name;


public void setName(String name) 
    this.name = name;


public int getGrade() 
    return grade;


public void setGrade(int grade) 
    this.grade = grade;


public int getNameIndex() 
    return nameIndex;


public void setNameIndex(int nameIndex) 
    this.nameIndex = nameIndex;


public int getGrageIndex() 
    return grageIndex;


public void setGrageIndex(int grageIndex) 
    this.grageIndex = grageIndex;


【讨论】:

真的真的非常非常非常非常感谢!!!!我必须等 3 个小时才能获得赏金,它会来的!再次感谢!! 顺便说一句,我发现问题,添加 4 个学生后应用程序崩溃,解决方案:不要覆盖 getViewTypeCountgetItemViewType @AsfK :将从解决方案中删除。感谢您的突出显示。【参考方案2】:

我建议您在列表视图中使用setOnItemClickListener 方法。有一些优点:

您不必在每次渲染项目时都创建一个新的OnClickListener 您可以直接访问视图的位置和视图本身: onItemClick(AdapterView&lt;?&gt; parent, View view, int position, long id)

然后您可以使用该位置从适配器中删除您的项目。

【讨论】:

【参考方案3】:

基本上这个问题是由于视图回收而发生的。将这两个方法添加到您的适配器类中

 @Override
 public int getViewTypeCount() 
     return studentID.size() + 1;
 

 @Override
 public int getItemViewType(int position) 
     return position;
 

这里是解释为什么你必须添加这两种方法的链接。 Getting an issue while checking the dynamically generated checkbox through list view

希望对你有帮助!

【讨论】:

【参考方案4】:

你需要在每次创建按钮时将tag设置为position,然后在onclick里面,获取tag,它会返回正确的位置

在您的 getView 中

holder.btnRemove = (Button) view.findViewById(R.id.btn_Remove);
holder.btnRemove.setTag(position);

然后onClick(查看视图)

int position = (Integer) ((Button) view).getTag();
//remove item from position, and do the stuff here

然后尝试从位置移除项目。

alternate solution:

【讨论】:

谢谢。但我对这个职位没有异议,这是正确的,我正在从 ArrayList 中删除合适的学生。只是 GUI 没有正确更新。您可以在图片中看到这一点。【参考方案5】:

只需在单击时获取要删除的项目的位置,然后使用 arraylist.remove(position) 将其从 arraylist 中删除,然后调用 notifyDataSetChanged。

【讨论】:

【参考方案6】:

你做错了,看看下面的代码。

@Override
public View getView(int i, View view, final ViewGroup viewGroup)

     if (view == null)
         view == LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.xxx, null);
         ViewHolder holder = new ViewHolder();
         holder.spNames = (Spinner) view.findViewById(R.id.spNames);
         holder.spGrades = (Spinner) view.findViewById(R.id.spGrades);
         holder.tvValue = (TextView) view.findViewById(R.id.tv_Value);
         holder.btnSet = (Button) view.findViewById(R.id.btn_setValue);
         holder.btnRemove = (Button) view.findViewById(R.id.btn_Remove);
         view.setTag(holder);

     else
         holde = view.getTag();
     

     /** You have to redefine each view every time because it can be recycled by the listview **/
     ArrayAdapter<String> studentsNamesAdapater = new ArrayAdapter<>
                (view.getContext(), android.R.layout.simple_spinner_dropdown_item, studentsNames);
        studentsNamesAdapater.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        holder.spNames.setAdapter(studentsNamesAdapater);
     /** so no **//


     /** you can set the position to the button as a tag **/
     holder.btnRemove.setTag(i);

     /** you MUST set the button OnClickListener after the view holder is created. you MUST NOT set the listener inside the if (view==null) pattern. **/
    holder.btnRemove.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v)
            
                int position = (Integer)v.getTag(); //Get the position which you would like you remove from the button.
                studentID.remove(position);
                notifyDataSetChanged();


            
        );

你可能想知道为什么你必须像这样处理 ListViews,我现在不能告诉你为什么,但你必须充分了解 AdapterViews(如 ListView、GridView、RecyclerView)是如何工作的,如果你想成为一名出色的移动(Android、ios 和其他移动设备都一样)开发人员。

看看这个: http://developer.android.com/guide/topics/ui/layout/listview.html

欢迎来到编程世界,你还有很长的路要走。祝你好运。

【讨论】:

【参考方案7】:

变化:

    @Override
        public View getView(int final position, View view, final ViewGroup viewGroup)
        
            final ViewHolder holder;
            if (view == null) 
                holder = new ViewHolder();
                view = inflater.inflate(R.layout.listview_row, null);

                holder.spNames = (Spinner) view.findViewById(R.id.spNames);
                holder.spGrades = (Spinner) view.findViewById(R.id.spGrades);
                holder.tvValue = (TextView) view.findViewById(R.id.tv_Value);
                holder.btnSet = (Button) view.findViewById(R.id.btn_setValue);
                holder.btnRemove = (Button) view.findViewById(R.id.btn_Remove);
               view.setTag(viewHolder);

            
            else
                holder = (ViewHolder) view.getTag();
            

    // pop spinner names
                ArrayAdapter<String> studentsNamesAdapater = new ArrayAdapter<>
                        (view.getContext(), android.R.layout.simple_spinner_dropdown_item, studentsNames);
                studentsNamesAdapater.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                holder.spNames.setAdapter(studentsNamesAdapater);

                // pop spinner grades
                String[] grades = new String [101];
                for (int grade = 0; grade < 101; grade++)
                    grades[grade] = String.valueOf(grade);

                final ArrayAdapter<String> studentsGradesAdapater = new ArrayAdapter<>
                        (view.getContext(), android.R.layout.simple_spinner_dropdown_item, grades);
                studentsGradesAdapater.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                holder.spGrades.setAdapter(studentsGradesAdapater);

                // set (variable and textview)
                holder.btnSet.setOnClickListener(new View.OnClickListener() 
                    @Override
                    public void onClick(View v) 
                        // update studentID
                        Student tmp = new Student(holder.spNames.getSelectedItem().toString(), Integer.valueOf(holder.spGrades.getSelectedItem().toString()));
                        studentID.set(position, tmp);
                        ((MainActivity) context).updateStatus(position);
                    
                );

                // remove row
                holder.btnRemove.setOnClickListener(new View.OnClickListener() 
                    @Override
                    public void onClick(View v)
                    
                        studentID.remove(position);
                        notifyDataSetChanged();

                        // for debug
                        String dStatus = "Vector size: " + studentID.size() + "\n";
                        for (int index = 0; index < studentID.size(); index++)
                            dStatus += studentID.get(index).name + " " + studentID.get(index).grade + "\n";
                        Toast.makeText(v.getContext(), dStatus, Toast.LENGTH_SHORT).show();
                    
                );

            return view;
        

【讨论】:

非常感谢。我只想为其他用户添加,在你这样做之后,微调器的值将重置。您必须手动保存它们,请参见:***.com/questions/14258866/… 对不起,这是我的错。它不工作。仍然是错误的学生从 GUI 上的列表视图中删除。

以上是关于使用微调器从自定义列表视图中删除了错误的行的主要内容,如果未能解决你的问题,请参考以下文章

使用 sqliteDatabase 从自定义列表视图中删除项目

使用sqliteDatabase从自定义列表视图中删除项目

Android - 从自定义列表视图中删除一个项目并在长按时更新它

从自定义数据源中获取选定的行?

Android:从自定义列表视图中单击的按钮获取列表视图项目

无法从自定义表格视图单元格按钮中删除行