使用微调器从自定义列表视图中删除了错误的行
Wrong row deleted from custom listview with spinner
我正在开发包含自定义 ListView 的应用程序,其中包含我从主 activity 更新的删除按钮。
我在从 ListView 中删除一行时遇到问题,虽然我从自定义列表视图中删除了正确的索引并调用了 notifyDataChanged()
方法,但 GUI 没有正确更新。
这里我写了一个sample project,和我真实的idea中的一样只是sample:
- activity_main.xml(主布局)只包含ListView,名为listview。
- listview_row.xml 包含两个 Spinners(学生姓名和年级),设置和删除按钮和文本视图。
- Student class 包含两个变量: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 上删除的项目仍然出现在屏幕上,如下所示:
有人可以指导我如何从 GUI 中也删除右行吗?
编辑
- 我更新了我的列表视图适配器并在您回答我时使用了标签,但没有用。
- 当我试图在删除一个学生后添加到学生时,我也发现了一个奇怪的问题。出于某种原因,它 "remember" 最后一个学生和 return 他有完整的数据,正如你在更新图片中看到的那样。
EDIT2
如果有人想尝试,我添加 Student class 和 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>
每次创建button需要设置tag为position,然后在onclick里面,获取tag,会return你的位置正确
在您的 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
然后尝试从位置上删除项目。
变化:
@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;
}
只需在单击时获取要删除的项目的位置,然后使用 arraylist.remove(position) 将其从数组列表中删除,然后调用 notifyDataSetChanged。
基本上这个问题是因为View回收造成的。将这两个方法添加到您的适配器 class
@Override
public int getViewTypeCount() {
return studentID.size() + 1;
}
@Override
public int getItemViewType(int position) {
return position;
}
这里 link 解释了为什么你必须添加这两个方法。
Getting an issue while checking the dynamically generated checkbox through list view
希望对您有所帮助!
我建议您在列表视图中使用 setOnItemClickListener 方法。有一些优点:
- 您不必在每次呈现项目时都创建新的
OnClickListener
- 您可以直接访问视图的位置和视图本身:
onItemClick(AdapterView<?> parent, View view, int position, long id)
然后您可以使用该位置从适配器中删除您的项目。
你弄错了,看下面代码
@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();
}
});
}
你可能想知道为什么你必须这样处理 ListView,我现在不能告诉你为什么,但你必须充分理解 AdapterViews(如 ListView、GridView、RecyclerView)是如何工作的如果你想成为一名出色的移动设备(Android、iOS 和其他移动设备都是一样的)开发人员,请工作。
这样看:
http://developer.android.com/guide/topics/ui/layout/listview.html
欢迎来到编码世界,你还有很长的路要走运行。祝你好运。
问题出在
holder.spNames.setTag(position);
和
holder.spNames.setSelection((Integer) holder.spNames.getTag());
正如您在 "onItemSelected()" 中设置名称标签一样。当您删除项目时,您会从学生列表中删除该项目,但是标签。
假设您删除了 0 处的项目。
现在,当调用 "notifyDataSetChanged()" 时,它将根据可用数据重新填充列表视图。现在,这里
else{
holder = (ViewHolder) view.getTag();
}
接到电话。
当 getView() 中的 i= 0 时,您将获得先前填充列表的“第 0 个”索引的视图。因此,“(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;
}
}
我正在开发包含自定义 ListView 的应用程序,其中包含我从主 activity 更新的删除按钮。
我在从 ListView 中删除一行时遇到问题,虽然我从自定义列表视图中删除了正确的索引并调用了 notifyDataChanged()
方法,但 GUI 没有正确更新。
这里我写了一个sample project,和我真实的idea中的一样只是sample:
- activity_main.xml(主布局)只包含ListView,名为listview。
- listview_row.xml 包含两个 Spinners(学生姓名和年级),设置和删除按钮和文本视图。
- Student class 包含两个变量: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 上删除的项目仍然出现在屏幕上,如下所示:
有人可以指导我如何从 GUI 中也删除右行吗?
编辑
- 我更新了我的列表视图适配器并在您回答我时使用了标签,但没有用。
- 当我试图在删除一个学生后添加到学生时,我也发现了一个奇怪的问题。出于某种原因,它 "remember" 最后一个学生和 return 他有完整的数据,正如你在更新图片中看到的那样。
EDIT2
如果有人想尝试,我添加 Student class 和 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>
每次创建button需要设置tag为position,然后在onclick里面,获取tag,会return你的位置正确
在您的 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
然后尝试从位置上删除项目。
变化:
@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;
}
只需在单击时获取要删除的项目的位置,然后使用 arraylist.remove(position) 将其从数组列表中删除,然后调用 notifyDataSetChanged。
基本上这个问题是因为View回收造成的。将这两个方法添加到您的适配器 class
@Override
public int getViewTypeCount() {
return studentID.size() + 1;
}
@Override
public int getItemViewType(int position) {
return position;
}
这里 link 解释了为什么你必须添加这两个方法。 Getting an issue while checking the dynamically generated checkbox through list view
希望对您有所帮助!
我建议您在列表视图中使用 setOnItemClickListener 方法。有一些优点:
- 您不必在每次呈现项目时都创建新的
OnClickListener
- 您可以直接访问视图的位置和视图本身:
onItemClick(AdapterView<?> parent, View view, int position, long id)
然后您可以使用该位置从适配器中删除您的项目。
你弄错了,看下面代码
@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();
}
});
}
你可能想知道为什么你必须这样处理 ListView,我现在不能告诉你为什么,但你必须充分理解 AdapterViews(如 ListView、GridView、RecyclerView)是如何工作的如果你想成为一名出色的移动设备(Android、iOS 和其他移动设备都是一样的)开发人员,请工作。
这样看: http://developer.android.com/guide/topics/ui/layout/listview.html
欢迎来到编码世界,你还有很长的路要走运行。祝你好运。
问题出在
holder.spNames.setTag(position);
和
holder.spNames.setSelection((Integer) holder.spNames.getTag());
正如您在 "onItemSelected()" 中设置名称标签一样。当您删除项目时,您会从学生列表中删除该项目,但是标签。
假设您删除了 0 处的项目。
现在,当调用 "notifyDataSetChanged()" 时,它将根据可用数据重新填充列表视图。现在,这里
else{
holder = (ViewHolder) view.getTag();
}
接到电话。 当 getView() 中的 i= 0 时,您将获得先前填充列表的“第 0 个”索引的视图。因此,“(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;
}
}