【问题标题】:ListView with wrong status items when scrolling listListView 滚动列表时状态项错误
【发布时间】:2015-07-28 14:40:35
【问题描述】:

我的 ListView 出现问题。

列表视图

<ListView
    android:id="@+id/lvMyList"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="@android:color/transparent"
    android:dividerHeight="5dp" >
</ListView>

我有一个 ListView,它在您的适配器中有许多组件。通过点击一条线,LinearLayout有一个展开动画,变得可见或者已经可见隐藏(折叠)。

项目列表视图(适配器 xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/llContainer"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="5dp"
    android:layout_marginTop="5dp"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tvLocal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="question"
        android:textColor="@color/app_gray"
        android:textSize="12sp" >
    </TextView>

    <TextView
        android:id="@+id/tvQuestion"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="question"
        android:textColor="@color/app_gray"
        android:textSize="12sp"
        android:textStyle="bold" >
    </TextView>

    <LinearLayout
        android:id="@+id/llContainerLikeDislike"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        android:orientation="vertical"
        android:visibility="gone" >

        <TextView
            android:id="@+id/tvAnswer"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:layout_marginBottom="5dp"
            android:textColor="@color/app_blue"
            android:textSize="12sp" />

        <LinearLayout
            android:id="@+id/llContainerButtons"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:layout_marginTop="5dp"
            android:orientation="horizontal" >

            <LinearLayout
                android:id="@+id/llLike"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:padding="5dp"
                android:clickable="true"
                android:gravity="center_horizontal"
                android:orientation="vertical" >

                <ImageView
                    android:id="@+id/ivLike"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:contentDescription="@null"
                    android:layout_margin="5dp"
                    android:src="@drawable/background_btn_like" />

                <TextView
                    android:id="@+id/tvHelped"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Helped"
                    android:textColor="@color/app_gray"
                    android:textSize="12sp" >
                </TextView>

                <TextView
                    android:id="@+id/tvNumLike"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/app_gray"
                    android:textSize="12sp" >
                </TextView>
            </LinearLayout>

            <LinearLayout
                android:id="@+id/llDisLike"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:padding="5dp"
                android:clickable="true"
                android:gravity="center_horizontal"
                android:orientation="vertical" >

                <ImageView
                    android:id="@+id/ivDisLike"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:contentDescription="@null"
                    android:layout_margin="5dp"
                    android:src="@drawable/background_btn_dislike" />

                <TextView
                    android:id="@+id/tvNotHelp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Not Help"
                    android:textColor="@color/app_gray"
                    android:textSize="12sp" >
                </TextView>

                <TextView
                    android:id="@+id/tvNumDisLike"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/app_gray"
                    android:textSize="12sp" >
                </TextView>
            </LinearLayout>

        </LinearLayout>

    </LinearLayout>

</LinearLayout>

问题是(指随机模式)通过延长一行,例如第一行,另一行也接收到事件。

动画

    /**
         * Expand animation
         * @param v : {@link View}
         */
        public static void expand(final View v, final ListView lv, final int position) {
            v.measure(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
            final int targetHeight = v.getMeasuredHeight();

            v.getLayoutParams().height = 0;
            v.setVisibility(View.VISIBLE);
            Animation a = new Animation()
            {
                @Override
                protected void applyTransformation(float interpolatedTime, Transformation t) {
                    v.getLayoutParams().height = interpolatedTime == 1
                            ? LayoutParams.WRAP_CONTENT
                            : (int)(targetHeight * interpolatedTime);
                    v.requestLayout();

                    //Moves the listview scroll so that the expanding area is visible.
                    lv.setSelectionFromTop(position, v.getLayoutParams().height);
                }

                @Override
                public boolean willChangeBounds() {
                    return true;
                }
            };

            // 1dp/ms
            a.setDuration((int)(targetHeight / v.getContext().getResources().getDisplayMetrics().density));
            v.startAnimation(a);
        }

        /**
         * Collapse animation
         * @param v : {@link View}
         */
        public static void collapse(final View v) {
            final int initialHeight = v.getMeasuredHeight();

            Animation a = new Animation()
            {
                @Override
                protected void applyTransformation(float interpolatedTime, Transformation t) {
                    if(interpolatedTime == 1){
                        v.setVisibility(View.GONE);
                    }else{
                        v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
                        v.requestLayout();
                    }
                }

                @Override
                public boolean willChangeBounds() {
                    return true;
                }
            };

            // 1dp/ms
            a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
            v.startAnimation(a);
        }

Using the debugging to verify whether the method is being called more than once, this method being called only once and receives the correct position where it received the click event. But visibly other line receives the event as well.

Adapter

public class QAAdapter extends ArrayAdapter<QA> implements Filterable {

    private List<QA>filteredData;

    private ArrayList<QA> arrayQA;
    private ViewHolder holder;
    private final LayoutInflater inflater;
    private Activity activity;

    public QAAdapter(Activity activity,
            ArrayList<QA> arrayQA) {
        super(activity, 0);

        this.inflater = (LayoutInflater) activity
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.arrayQA = arrayQA;
        this.filteredData = arrayQA; 
        this.activity = activity;
    }

    /**
     * Stores the visual components for better performance.
     */
    static class ViewHolder {
        private TextView tvLocal, tvQuestion, tvAnswer, tvNumLike, tvNumDisLike;
        private LinearLayout llContainer, llLike, llDisLike;
    }

    /**
     * Return the size of {@code arrayQA}.
     */
    @Override
    public int getCount() {
        return filteredData.size();
    }

    /**
     * Return {@link QA}.
     * @param position
     * @return {@link QA}
     */
    public QA getItemQa(int position) {
        return filteredData.get(position);
    }

    /**
     * Insert {@link QA} in the {@code arrayQA}.
     * @param qa: {@link QA}
     */
    public void addNewQA(final QA qa) {
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                filteredData.add(qa);
                notifyDataSetChanged();
            }
        });
    }

    /**
     * Return the {@code id} of the {@link QA}.
     */
    @Override
    public long getItemId(int position) {
        return filteredData.get(position).getId();
    }

    /**
     * Return line of the {@link BaseAdapter}.
     */
    @Override
    public View getView(final int position, View cv, final ViewGroup parent) {
        View convertView = cv;

        final QA qa = filteredData.get(position);

        if (convertView == null) {
            convertView = inflater.inflate(R.layout.item_qa, parent, false);
            holder = new ViewHolder();

            Typeface tfLight = Typeface.createFromAsset(activity.getAssets(),
                    "fonts/Oswald-Light.ttf");

            Typeface tfBold = Typeface.createFromAsset(activity.getAssets(),
                    "fonts/Oswald-Bold.ttf");

            holder.tvLocal = (TextView) convertView
                    .findViewById(R.id.tvLocal);
            holder.tvLocal.setTypeface(tfLight);

            holder.tvQuestion = (TextView) convertView
                    .findViewById(R.id.tvQuestion);
            holder.tvQuestion.setTypeface(tfBold);

            holder.tvAnswer = (TextView) convertView
                    .findViewById(R.id.tvAnswer);
            holder.tvAnswer.setTypeface(tfLight);

            holder.tvNumLike = (TextView) convertView
                    .findViewById(R.id.tvNumLike);
            holder.tvNumLike.setTypeface(tfLight);

            holder.tvNumDisLike = (TextView) convertView
                    .findViewById(R.id.tvNumDisLike);
            holder.tvNumDisLike.setTypeface(tfLight);

            holder.llContainer = (LinearLayout) convertView.findViewById(R.id.llContainer);
            holder.llContainer.setTag(position);
            holder.llLike = (LinearLayout) convertView.findViewById(R.id.llLike);
            holder.llDisLike = (LinearLayout) convertView.findViewById(R.id.llDisLike);

            holder.llContainer.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {

                    int visibility = v.findViewById(R.id.llContainerLikeDislike).getVisibility();

                    if(visibility == View.GONE){
                        final int position2 = ((ListView) parent).getPositionForView(v);
                        expand(v.findViewById(R.id.llContainerLikeDislike), ((ListView) parent), position2);
                    } else {
                        collapse(v.findViewById(R.id.llContainerLikeDislike));
                    }

                    v.findViewById(R.id.llContainerLikeDislike).requestLayout();
                    notifyDataSetChanged();
                }
            });

            holder.llLike.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {

                    int value = (int) (qa.getUp_count() + 1);
                    qa.setUp_count(value);

                    ((LinearLayout) v).setEnabled(false);
                    ((LinearLayout) ((LinearLayout) v.getParent()).findViewById(R.id.llDisLike)).setEnabled(false);
                    ((ImageView) v.findViewById(R.id.ivLike)).setSelected(true);
                    ((TextView) v.findViewById(R.id.tvAjudou)).setTextColor(activity.getResources().getColor(R.color.app_blue));
                    ((TextView) v.findViewById(R.id.tvNumLike)).setTextColor(activity.getResources().getColor(R.color.app_blue));

                    notifyDataSetChanged();

                    MyAsyncTask asyncTask = new MyAsyncTask();
                    asyncTask.execute(URL);
                }
            });

            holder.llDisLike.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {

                    int value = (int) (qa.getDown_count() + 1);
                    qa.setDown_count(value);

                    ((LinearLayout) v).setEnabled(false);
                    ((LinearLayout) ((LinearLayout) v.getParent()).findViewById(R.id.llLike)).setEnabled(false);
                    ((ImageView) v.findViewById(R.id.ivDisLike)).setSelected(true);
                    ((TextView) v.findViewById(R.id.tvNaoAjudou)).setTextColor(activity.getResources().getColor(R.color.app_red));
                    ((TextView) v.findViewById(R.id.tvNumDisLike)).setTextColor(activity.getResources().getColor(R.color.app_red));

                    notifyDataSetChanged();

                    MyAsyncTask asyncTask = new MyAsyncTask();
                    asyncTask.execute(URL);
                }
            });

            convertView.setTag(holder);

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

        String name = qa.getName();
        String city = qa.getCity();
        String state = qa.getState();

        if(qa.getName().equals(null) || qa.getName().equals("null")) {
            name = " ";
        }

        if(qa.getCity().equals(null) || qa.getCity().equals("null")) {
            city = " ";
        }

        if(qa.getState().equals(null) || qa.getState().equals("null")) {
            state = " ";
        }

        holder.tvLocal.setText(activity.getString(R.string.local_item_pergunta, name, city, state));

        holder.tvQuestion.setText(qa.getQuestion());
        holder.tvAnswer.setText(qa.getAnswer());

        holder.tvNumLike.setText(String.valueOf(qa.getUp_count()));
        holder.tvNumDisLike.setText(String.valueOf(qa.getDown_count()));

        return convertView;
    }

    /**
     * Realizes filter of the {@link QA}.
     */
    public Filter getFilter() {

        Filter mFilter = new Filter() {

            @SuppressLint("DefaultLocale")
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {

                String filterString = constraint.toString().toLowerCase();

                FilterResults results = new FilterResults();

                final List<QA> list = arrayQA;

                int count = list.size();
                final ArrayList<QA> nlist = new ArrayList<QA>(count);

                QA filterable;

                for (int i = 0; i < count; i++) {
                    filterable = list.get(i);
                    String questionFiltred = filterable.getQuestion();
                    String anwserFiltred = filterable.getAnswer();

                    if (questionFiltred.toLowerCase().contains(filterString) || anwserFiltred.toLowerCase().contains(filterString)) {
                        nlist.add(filterable);
                    }

                }

                results.values = nlist;
                results.count = nlist.size();

                return results;
            }

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint,
                    final FilterResults results) {
                filteredData = (ArrayList<QA>) results.values;
                notifyDataSetChanged();
            }

        };

        return mFilter;
    }

    /**
     * {@link AsyncTask} for the send data like or dislike.
     */
    public class MyAsyncTask extends AsyncTask<String, Integer, Boolean>{...}

}

我的课堂质量检查

/**
 * Represents entity questions and answers.
 */
public class QA {

    private int id;
    private String question, answer, answer_at, state, city, name;
    public enum state { like, dislike };
    private long up_votes_count, down_votes_count;
    /**
     * @return the question
     */
    public String getQuestion() {
        return question;
    }
    /**
     * @param question the question to set
     */
    public void setQuestion(String question) {
        this.question = question;
    }
    /**
     * @return the answer
     */
    public String getAnswer() {
        return answer;
    }
    /**
     * @param answer the answer to set
     */
    public void setAnswer(String answer) {
        this.answer = answer;
    }
    /**
     * @return the answer_at
     */
    public String getAnswer_at() {
        return answer_at;
    }
    /**
     * @param answer_at the answer_at to set
     */
    public void setAnswer_at(String answer_at) {
        this.answer_at = answer_at;
    }
    /**
     * @return the up_count
     */
    public long getUp_count() {
        return up_votes_count;
    }
    /**
     * @param up_count the up_count to set
     */
    public void setUp_count(long up_count) {
        this.up_votes_count = up_count;
    }
    /**
     * @return the down_count
     */
    public long getDown_count() {
        return down_votes_count;
    }
    /**
     * @param down_count the down_count to set
     */
    public void setDown_count(long down_count) {
        this.down_votes_count = down_count;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    /**
     * @return the state
     */
    public String getState() {
        return state;
    }
    /**
     * @param state the state to set
     */
    public void setState(String state) {
        this.state = state;
    }
    /**
     * @return the city
     */
    public String getCity() {
        return city;
    }
    /**
     * @param city the city to set
     */
    public void setCity(String city) {
        this.city = city;
    }
    /**
     * @return the name
     */
    public String getName() {
        return name;
    }
    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }


}

使用调试验证该方法是否被多次调用,该方法仅被调用一次,并且接收到它接收到点击事件的正确位置。但可见其他线路也收到了事件。 我可能在哪里丢失或我应该改变什么来解决这个问题?

EDIT1:

问题只出现在 ListView 的滚动中!

【问题讨论】:

    标签: android listview adapter


    【解决方案1】:

    您的代码的问题是 listview-item-recycling 不处理子项可见性:

      public View getView(final int position, View cv, final ViewGroup parent)  {
        ...
        if (convertView == null) {
           // create new item here
           ...
        } else {
          // recycle convertView where parts of are visible or not
          // make shure that llContainer starts invisible
          ViewHolder holder = (ViewHolder) convertView.getTag();
          holder.llContainer.setVisibility(View.GONE);        
        }
    

    【讨论】:

    • 我尝试使用您的代码但没有成功。我想我不能很清楚我的问题。 llContainer是LinearLayout,它接收一个点击来控制llContainerLikeDislike中的展开和折叠动画。发生的情况是,如果我选择例如第 0 行的位置,则位置 4 也正在接收动画。即一行 x 接收点击,另一行 y 也接收动画,但是,当使用调试时,该方法仅被调用一次并且位置正确。
    • 执行更多测试发现只有当我在 ListView 中滚动时才会出现此问题。在 ListView 中滚动时,它们的状态是错误的!一条被折叠的线扩大了,反之亦然。如果我点击一个按钮并且它改变颜色,另一行同样的事情会发生。
    • 有人能帮我解决这个问题吗?我一直在研究这个问题,但我相信它已经在讨论论坛中的所有内容......在 viewholder 中使用 setTag() 和 gettag() 并且仍然存在在列表中滚动并且项目出现在错误道。关于展开和折叠动画并点击按钮的问题是错误的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-31
    • 1970-01-01
    • 1970-01-01
    • 2021-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多