简体   繁体   中英

ListView with wrong status items when scrolling list

I'm having trouble with my ListView.

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>

I have a ListView that has a number of components in your adapter. By clicking on a line, LinearLayout has an animation of expansion, becomes visible or already is visible hide (collapse).

Items ListView (adapter 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>

The problem is that (means that random mode) by extending a line, for example the first, another line also receives the event.

Animation

    /**
         * 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>{...}

}

My Class QA

/**
 * 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;
    }


}

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. Where can I be missing or what should I change to fix this?

EDIT1:

Problem only occurs in the scroll of the ListView!

The problem with your code is the listview-item-recycling which does not handle subitem-visiblity:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM