我正在使用包含自定义ListView的应用程序,其中包含我从主要活动更新的删除按钮。
我在从ListView中删除行时遇到问题,虽然我正在从自定义列表视图中删除正确的索引并调用notifyDataChanged()
方法,但GUI无法正确更新。
在这里,我写了一个示例项目,就像我的想法中的真实项目更多样本:
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中删除右行吗?
修改
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>
答案 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中获取标记,它将返回正确的位置
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
然后尝试从位置移除该项目。
答案 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
欢迎来到编码世界,你还有很长的路要走。祝你好运。