带有自定义适配器的列表视图 - 每个其他项目元素都是重复的

listview with a custom adapter - every other item element are duplicates

我的列表视图及其自定义适配器遇到了一些奇怪的问题(我认为)。

问题是我的列表视图中的每个项目都有一些不同的视图元素,例如 EditText 字段等。 每当我在列表视图中有超过 2 个项目时,这些元素的条目现在在所有其他项目中。

我举个例子: - 我正在向列表视图中添加 8 个项目。 - 我在第一个文本字段中输入一个值 - 现在在文本字段中重复相同的值:3、5 和 7 - 如果我在第二个项目的文本字段中输入一个值,该值将在项目编号的文本字段中重复:4、6 和 8

希望你现在能看到问题。

这里是这个问题的一些相关代码:

首先,我的自定义适配器 (CreateProgramAdapter) 的代码:

    public class CreateProgramAdapter extends BaseAdapter {

    private LayoutInflater inflater;
    private ArrayList<TrainingPass> trainingPasses;
    private List<TrainingPass> items;
    private int layoutResourceId;
    private Context context;

    public CreateProgramAdapter(Context context, List<TrainingPass> items) {
        this.context = context;
        this.items = items;
        this.inflater = LayoutInflater.from(this.context);
    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public TrainingPass getItem(int position) {
        return items.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view;
        TrainingPassHolder holder;

        if(convertView == null) {
            view = inflater.inflate(R.layout.adapter_createprogram_trainingpasses_item, parent, false);
            holder = new TrainingPassHolder();
            view.setTag(holder);

        } else {
            view = convertView;
            holder = (TrainingPassHolder)view.getTag();
        }

        holder.trainingPass = this.getItem(position);
        holder.trainingPass.trainingPassID = position + 1;
        holder.trainingPassContainer = (RelativeLayout) view.findViewById(R.id.trainingPassContainer);
        holder.trainingPassHeadlineShowHide = (TextView) view.findViewById(R.id.trainingpass_headline);
        holder.noExercisesYet = (TextView) view.findViewById(R.id.txt_no_exercises_yet);
        holder.exerciseSearchField = (EditText) view.findViewById(R.id.trainingpass_exercise_searchfield);
        holder.trainingPassSetsListView = (ListView) view.findViewById(R.id.trainingpass_sets_listview);
        holder.exercisesListView = (ListView) view.findViewById(R.id.exercises_listview);
        holder.addSetButton = (Button) view.findViewById(R.id.add_set_button);
        holder.addExerciseButton = (Button) view.findViewById(R.id.add_exercise_button);
        holder.deleteTrainingpass = (Button) view.findViewById(R.id.delete_trainingpass_button);
        holder.doneTrainingpass = (Button) view.findViewById(R.id.done_trainingpass_button);

        setupItems(holder);

        return view;
    }


    private void setupItems(TrainingPassHolder trainingPassHolder) {
        final TrainingPassHolder holder = trainingPassHolder;
        int trainingPassID = trainingPassHolder.trainingPass.trainingPassID;

        TextView showHide         = holder.trainingPassHeadlineShowHide;
        Button addSet             = holder.addSetButton;
        Button deleteTrainingPass = holder.deleteTrainingpass;
        final EditText exerciseNameField = holder.exerciseSearchField;

        // Set title of the trainingpass
            showHide.setText("Trainingpass " + trainingPassID);

        // Set name of exercise


        if (trainingPassHolder.trainingPass.tempExerciseName != null) {
            System.out.println(holder.trainingPass.tempExerciseName);
            exerciseNameField.setText(holder.trainingPass.tempExerciseName);
        } else {
            exerciseNameField.setText("");
        }

        // Add 1 default set to exercise
            if (holder.sets.size() == 0) {
                holder.sets.add(1);
            }

        /*
            Set adapter for sets of the listview for the trainingpass
         */

        final TrainingPassSetsAdapter createProgramAdapter = new TrainingPassSetsAdapter(context, holder.sets);
        holder.trainingPassSetsListView.setAdapter(createProgramAdapter);


        exerciseNameField.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                holder.trainingPass.tempExerciseName = exerciseNameField.getText().toString();
                return false;
            }
        });


        /*
            Show / hide the trainingpass
         */
        showHide.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (holder.trainingPass.visible) {
                    holder.trainingPass.visible = false;
                    holder.trainingPassContainer.setVisibility(View.GONE);
                } else {
                    holder.trainingPass.visible = true;
                    holder.trainingPassContainer.setVisibility(View.VISIBLE);
                }
            }
        });

        /*
            Add set to the listview
         */
        addSet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                holder.sets.add(1);
                createProgramAdapter.notifyDataSetChanged();
            }
        });

        /*
            Delete training pass
         */
        deleteTrainingPass.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                items.remove(holder.trainingPass);
                notifyDataSetChanged();
            }
        });
    }

    private static class TrainingPassHolder {
        ArrayList<Integer> sets = new ArrayList<Integer>();
        TrainingPass    trainingPass;
        RelativeLayout  trainingPassContainer;
        TextView        trainingPassHeadlineShowHide;
        TextView        noExercisesYet;
        EditText        exerciseSearchField;
        ListView        trainingPassSetsListView;
        ListView        exercisesListView;
        Button          addSetButton;
        Button          addExerciseButton;
        Button          deleteTrainingpass;
        Button          doneTrainingpass;
    }
}

这里是 class TrainingPass 的代码:

public class TrainingPass {

    public String trainingPassName;
    public ArrayList<Exercise> exercises;
    public int trainingPassID;
    public boolean visible = false;
    public String tempExerciseName;


    public TrainingPass() {
        exercises = new ArrayList<Exercise>();
    }

    public void addExercise(Exercise exercise ) {
        exercises.add(exercise);
    }

}

activity class CreateProgramActivity 的代码:

public class CreateProgramActivity extends CustomActivity {

    private ArrayList<TrainingPass> trainingPasses;
    private CreateProgramAdapter createProgramAdapter;
    private ListView trainingPassesListView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.AppTheme);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_create_program);

        trainingPasses = new ArrayList<TrainingPass>();


        trainingPassesListView = (ListView)findViewById(R.id.listView_trainingpasses);
        createProgramAdapter = new CreateProgramAdapter(CreateProgramActivity.this, trainingPasses);
        trainingPassesListView.setAdapter(createProgramAdapter);

    }

    public void addTrainingPass(View v) {
        TrainingPass trainingPass = new TrainingPass();
        trainingPasses.add(trainingPass);

        createProgramAdapter.notifyDataSetChanged();
    }
    public void addTrainingPassHelp(View v) {
        Toast.makeText(this, "Add training pass HELP!", Toast.LENGTH_SHORT).show();
    }
    public void createProgram(View v) {
        Toast.makeText(this, "Create program", Toast.LENGTH_SHORT).show();
    }
}

这是 CreateProgramActivity 的 xml 布局,包含父列表视图:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.mobileplatformexam.activities.CreateProgramActivity"
    android:id="@+id/relativeLayoutCreateProgram">


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add training pass"
        android:id="@+id/btn_add_trainingpass"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_toLeftOf="@+id/btn_add_trainingpass_help"
        android:layout_toStartOf="@+id/btn_add_trainingpass_help"
        android:onClick="addTrainingPass"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="\?"
        android:id="@+id/btn_add_trainingpass_help"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:onClick="addTrainingPassHelp"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Create program"
        android:id="@+id/btn_create_program"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignRight="@+id/btn_add_trainingpass_help"
        android:layout_alignEnd="@+id/btn_add_trainingpass_help"
        android:onClick="createProgram"/>

    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/listView_trainingpasses"
        android:layout_above="@+id/btn_create_program"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/btn_add_trainingpass" />


</RelativeLayout>

最后,这是我的 xml 父列表视图项目的布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_gravity="center_horizontal"
    android:orientation="horizontal"
    android:padding="10dp">

    <TextView
        android:id="@+id/trainingpass_headline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="false"
        android:layout_alignParentTop="true"
        android:text="Trainingpass"
        android:textSize="16dp"
        android:textColor="@android:color/white"
        android:background="@android:color/holo_blue_dark"
        android:height="35dp"
        android:gravity="center_vertical"
        android:paddingLeft="10dp"
        android:textIsSelectable="true"
        android:textStyle="bold"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true" />

    <ImageView
        android:id="@+id/trainingpass_showhide_imageview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_blue_dark"
        android:height="25dp"
        android:minWidth="50dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:padding="7dp"
        android:layout_alignBottom="@+id/trainingpass_headline"
        android:src="@mipmap/arrows_up_down"
        android:layout_alignParentTop="true" />

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@+id/trainingpass_headline"
        android:layout_alignLeft="@+id/trainingpass_headline"
        android:layout_alignStart="@+id/trainingpass_headline"
        android:id="@+id/trainingPassContainer"
        android:background="@color/blue_bg_neutral"
        android:visibility="visible"
        android:paddingBottom="10dp">

        <EditText
            android:id="@+id/trainingpass_exercise_searchfield"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="Name of exercise"
            android:padding="6dp"
            android:layout_alignParentTop="true"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true"
            android:background="@color/blue_bg_inputField"
            android:layout_margin="10dp" />

        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/trainingpass_exercise_searchfield"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:id="@+id/sets_container"
            android:background="@color/blue_bg_inputField"
            android:padding="10dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="10dp">

            <TextView
                android:id="@+id/txt_sets_for_exercise"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Sets for exercise"
                android:textSize="14dp"
                android:textColor="@android:color/black"
                android:paddingRight="30dp"
                android:paddingLeft="0dp"
                android:paddingTop="0dp"
                android:paddingBottom="10dp"
                android:textStyle="bold"
                android:layout_alignParentTop="true"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_alignRight="@+id/add_set_button"
                android:layout_alignEnd="@+id/add_set_button" />

            <ListView
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:id="@+id/trainingpass_sets_listview"
                android:nestedScrollingEnabled="true"
                android:layout_alignParentRight="true"
                android:layout_alignParentEnd="true"
                android:layout_alignTop="@+id/txt_no_sets"
                android:layout_alignBottom="@+id/txt_no_sets" />

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Add set"
                android:id="@+id/add_set_button"
                android:layout_below="@+id/trainingpass_sets_listview"
                android:layout_alignParentRight="true"
                android:layout_alignParentEnd="true" />

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Add exercise"
                android:id="@+id/add_exercise_button"
                android:layout_below="@+id/add_set_button"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_alignParentRight="true"
                android:layout_alignParentEnd="true" />

            <TextView
                android:id="@+id/txt_no_sets"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="No sets yet"
                android:textSize="14dp"
                android:textColor="@android:color/black"
                android:paddingRight="30dp"
                android:paddingLeft="10dp"
                android:paddingTop="20dp"
                android:paddingBottom="10dp"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_alignParentRight="true"
                android:layout_alignParentEnd="true"
                android:layout_below="@+id/txt_sets_for_exercise"
                android:visibility="invisible" />
        </RelativeLayout>

        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true"
            android:layout_below="@+id/sets_container"
            android:id="@+id/exercises_container"
            android:background="@color/blue_bg_inputField"
            android:padding="10dp"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="10dp">

            <TextView
                android:id="@+id/txt_exercises"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Exercises"
                android:textSize="14dp"
                android:textColor="@android:color/black"
                android:paddingRight="30dp"
                android:paddingLeft="0dp"
                android:paddingTop="0dp"
                android:paddingBottom="10dp"
                android:textStyle="bold"
                android:layout_alignParentTop="true"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_alignRight="@+id/txt_no_exercises_yet"
                android:layout_alignEnd="@+id/txt_no_exercises_yet" />

            <TextView
                android:id="@+id/txt_no_exercises_yet"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="No exercises yet"
                android:textSize="14dp"
                android:textColor="@android:color/black"
                android:paddingRight="30dp"
                android:paddingLeft="10dp"
                android:paddingTop="20dp"
                android:paddingBottom="10dp"
                android:layout_below="@+id/txt_exercises"
                android:layout_alignParentRight="true"
                android:layout_alignParentEnd="true"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true" />

            <ListView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:id="@+id/exercises_listview"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_below="@+id/txt_exercises"
                android:layout_alignBottom="@+id/txt_no_exercises_yet" />

        </RelativeLayout>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Delete"
            android:id="@+id/delete_trainingpass_button"
            android:layout_alignRight="@+id/exercises_container"
            android:layout_alignEnd="@+id/exercises_container"
            android:layout_below="@+id/exercises_container"
            android:background="@color/btn_red"
            android:layout_marginLeft="10dp" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Done"
            android:id="@+id/done_trainingpass_button"
            android:layout_below="@+id/exercises_container"
            android:layout_alignLeft="@+id/exercises_container"
            android:layout_alignStart="@+id/exercises_container"
            android:layout_toStartOf="@+id/delete_trainingpass_button"
            android:background="@color/btn_green"
            android:layout_marginRight="10dp" />

    </RelativeLayout>

</RelativeLayout>

我真的希望有人能看到问题所在。

任何帮助将不胜感激。

我认为这种行为是可以预测的,因为适配器利用视图用新数据替换其内容。由于您没有在 getView() 方法中为 EditText 设置新的文本值,因此您将获得具有旧值的 EditText,即您输入的值。 因此,请尝试将 TextWatcher 添加到您的 EditText 并将输入的值保存在相应的 TrainingPass 对象实例中。在 getView() 方法中,您必须添加 holder.exerciseSearchField.setText()。如果相应的 TrainingPass 对象包含先前输入的值,则必须将其设置为 EditText,否则必须设置空字符串。

@Oleg Osipenko 说这是因为列表视图 reuse/recycle.. 示例

class Items {
    String name;
    String searchField=""; //to stored edit text values , init with empty string

    //create getter and setter
}

适配器的 getView()

@Override
public View getView(int position, View convertView, ViewGroup parent) {


    final ViewHolder holder;
    if (convertView == null) {

        holder = new ViewHolder();
        convertView = inflater.inflate(R.layout.your_adapter_layout, null);
        holder.textView1 = (TextView) convertView.findViewById(R.id.textView1);
        holder.editText1 = (EditText) convertView.findViewById(R.id.editText1);    
        convertView.setTag(holder);

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

    //holder.position to store view position
    holder.position= position;  

    item=items.get(position);

    holder.textView1.setText(item.getName());
    holder.editText1.setText(item.getSearchField());
    holder.editText1.addTextChangedListener(new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {

        }

        @Override
        public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
                int arg3) {

        }

        @Override
        public void afterTextChanged(Editable arg0) {
            //set more conditions if needed - like arg0=null, or empty
            items.get(holder.position).setSearchField(arg0.toString());
        }
    });

    return convertView;
}

private class ViewHolder {
    TextView textView1;
    EditText editText1;
    int position;
}

获取有关此次访问的简要说明:Creating ListView with EditText and TextWatcher in Android