使用微调器

时间:2015-08-17 08:16:56

标签: android listview android-listview adapter android-adapter

我正在使用包含自定义ListView的应用程序,其中包含我从主要活动更新的删除按钮。

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

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

  • activity_main.xml(主布局)包含仅名为listview的ListView。
  • listview_row.xml包含两个Spinners(学生姓名和成绩),设置和删除按钮以及文本视图。
  • Student类包含两个变量:name(String)和grade(int)

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上的已删除项目仍会显示在屏幕上,如下所示:

enter image description here

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

修改

  1. 我更新了我的listview适配器并在你回答我时使用了标签,而不是工作。
  2. 我在删除一个后尝试添加到学生时也发现了一个奇怪的问题。出于某种原因,它会“记住”最后一个学生,并将其返回给他全部数据,如更新图片所示。
  3. 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_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <ListView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            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_width="match_parent"
        android:layout_height="match_parent"
        android:background="#c4e0ff">
    
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <Spinner
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/spNames" />
    
            <Spinner
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:id="@+id/spGrades"
                android:layout_marginLeft="10dp" />
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Set Value"
                android:id="@+id/btn_setValue" />
        </LinearLayout>
    
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:text="Medium Text"
                android:id="@+id/tv_Value" />
    
            <Button
                style="?android:attr/buttonStyleSmall"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Remove"
                android:id="@+id/btn_Remove"
                android:layout_marginLeft="30dp" />
    
        </LinearLayout>
    
    </LinearLayout>
    

7 个答案:

答案 0 :(得分:4)

问题在于

holder.spNames.setTag(position); 

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

在&#34; onItemSelected()&#34;中设置名称标记时。当您删除该项目时,您会从学生列表中删除该项目,但该标记

我们假设您在0处删除了该项目。

现在,当&#34; notifyDataSetChanged()&#34;被称为它将根据可用的数据重新填充列表视图。现在,这里

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

正在被召唤。 当getView()中的i = 0时,您将获得&#34; 0 th&#34;上一个填充列表的索引。因此,&#34; (整数)holder.spNames.getTag()&#34;将指向前一个标签(即前一个列表的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;
}
}

答案 1 :(得分:2)

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

  • 每次呈现项目时,您都不必创建新的OnClickListener
  • 您可以直接访问视图的位置和视图本身: onItemClick(AdapterView<?> parent, View view, int position, long id)

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

答案 2 :(得分:1)

基本上这个问题是因为View回收而发生的。将这两个方法添加到适配器类

 @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

我希望它有所帮助!

答案 3 :(得分:0)

每次创建时,您需要将标记设置为位置作为按钮,然后在onclick中获取标记,它将返回正确的位置

在你的getView

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

然后点击(查看视图)

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

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

alternate solution:

答案 4 :(得分:0)

变化:

    @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;
        }

答案 5 :(得分:0)

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

答案 6 :(得分:0)

你正在做这件事,请看下面的代码。

@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

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