Страница 1 из 1

Одинаковый хэш-код элементов ListView

Добавлено: 16 фев 2015, 01:27
ne1c
У меня есть ListView с кастомным элементом. В этом кастомном элементе есть кнопка для которой я прописал селектор и состояния. Для адаптера использовал BaseAdapter. Для примера установил 9 элементов списка. Вот содержимое метода getView:

Код: Выделить всё

    @Override
    public View getView(final int position, View convertView, final ViewGroup parent) {
        View view = convertView;
        ViewHolderItem viewHolderItem;
        if (view == null){
            view = mInflater.inflate(R.layout.item_news, parent, false);

            viewHolderItem = new ViewHolderItem();

            viewHolderItem.infoText = (TextView) view.findViewById(R.id.info_text_textView);
            viewHolderItem.likeButton = (FButton) view.findViewById(R.id.like_post_button);
            viewHolderItem.card = (CardView) view.findViewById(R.id.news_item_cardView);

            view.setTag(viewHolderItem);

        } else {
            viewHolderItem = (ViewHolderItem) view.getTag();
        }

        viewHolderItem.infoText.setText(items[position]);
        viewHolderItem.likeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                v.setSelected(!v.isSelected());
            }
        });

        viewHolderItem.card.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("MYTAG", "Position: " + position + " Hash: " + Long.toString(v.hashCode()));
            }
        });

        return view;
    }

    static class ViewHolderItem {
        TextView infoText;
        FButton likeButton;
        CardView card;
    }
Когда я нажимаю на кнопку в первом элемента, то я фиксирую её как нажатая:

Код: Выделить всё

v.setSelected(!v.isSelected());
Но пролистав вниз, когда все первые элементы ушли с экрана и заменились другими у меня каким-то чудом стала выделенная кнопка и в 6 элементе. Не много расследовав эту магию, мне удалось выяснить что хэш-код первого элемента и 6-го совпадают, соответственно хэш-код 2-го с 7-м совпадают и т.д. Как можно это исправить, чтобы при прокрутке состояние элемента оставалось неизменным и не повторялось на других?
Если заметить, то я использую паттерн ViewHolder.

Re: Одинаковый хэш-код элементов ListView

Добавлено: 16 фев 2015, 11:06
Foenix
изменять нажато или отжато (так же как и радиобаттоны, и все что угодно другое - т.е. состояние строк листвью) нужно в getView в зависимости от ДАННЫХ, а не просто бац-бац. Там ведь идет работа с памятью и листвью использует уже созданные строки для отображения следующих - почитай о нем хотя бы теорию и все поймешь.

Re: Одинаковый хэш-код элементов ListView

Добавлено: 16 фев 2015, 15:19
ne1c
Хорошо. Я сохраняю значение isSelected в Map-e, но результат тот же:

Код: Выделить всё

public class NewsAdapter extends BaseAdapter{
    private String[] items = new String[]{"TEST TEXT 1","TEST TEXT 2","TEST TEXT 3","TEST TEXT 4",
            "TEST TEXT 5", "TEST TEXT 6", "TEST TEXT 7","TEST TEXT 8","TEST TEXT 9"};

    private Context mContext;
    private LayoutInflater mInflater;
    private Map<Integer, Boolean> mItemStatus;

    public NewsAdapter(Context _context){
        mContext = _context;
        mInflater = (LayoutInflater) _context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        mItemStatus = new HashMap<>();
        for (int i = 0; i < 9; i++){
            mItemStatus.put(i, false);
        }
    }

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

    @Override
    public Object getItem(int position) {
        return null;
    }

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

    @Override
    public View getView(final int position, View convertView, final ViewGroup parent) {
        View view = convertView;
        ViewHolderItem viewHolderItem;
        if (view == null){
            view = mInflater.inflate(R.layout.item_news, parent, false);

            viewHolderItem = new ViewHolderItem();

            viewHolderItem.infoText = (TextView) view.findViewById(R.id.info_text_textView);
            viewHolderItem.likeButton = (FButton) view.findViewById(R.id.like_post_button);
            viewHolderItem.card = (CardView) view.findViewById(R.id.news_item_cardView);
            if (mItemStatus.get(position)){
                viewHolderItem.likeButton.setSelected(true);
            }

            view.setTag(viewHolderItem);

        } else {
            viewHolderItem = (ViewHolderItem) view.getTag();
        }

        viewHolderItem.infoText.setText(items[position]);
        viewHolderItem.likeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mItemStatus.get(position)) {
                    v.setSelected(false);
                    mItemStatus.put(position, false);
                } else {
                    v.setSelected(true);
                    mItemStatus.put(position, true);
                }
            }
        });

        viewHolderItem.card.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(mContext, "Position: " + position + " Hash: " + Long.toString(v.hashCode()), Toast.LENGTH_SHORT).show();
                Log.d("MYTAG", "Position: " + position + " Hash: " + Long.toString(v.hashCode()));
            }
        });

        return view;
    }

    static class ViewHolderItem {
        TextView infoText;
        FButton likeButton;
        CardView card;
    }
}

Re: Одинаковый хэш-код элементов ListView

Добавлено: 16 фев 2015, 21:10
Foenix
а где у тебя выделение пункта списка в зависимости от данных?
вот ты пишешь
viewHolderItem.infoText.setText(items[position]);
а где такое же, Только для чекбокса?

Re: Одинаковый хэш-код элементов ListView

Добавлено: 18 фев 2015, 07:39
mdzht
ne1c писал(а):Хорошо. Я сохраняю значение isSelected в Map-e, но результат тот же:
братишка, лучше обработай likeButton, для обоих случаев
[syntax=java5]public class Like implements View.OnClickListener {
private String mId;
private ViewHolder holder;
private String owner_id;
private int lPosition;

public Like(String id, String oid, ViewHolder h, int lPosition) {
mId = id;
holder = h;
owner_id = oid;
this.lPosition = lPosition;
}

@Override
public void onClick(final View view) {

final ViewHolder v = holder;
v.like.setText("likes");
getItem(lPosition).setCanLike(0);
getItem(lPosition).setLikeCount(likeCount);
if(Constants.Extra.SDK < android.os.Build.VERSION_CODES.JELLY_BEAN) {
v.like.setBackgroundDrawable(like_full);
} else {
v.like.setBackground(like_full);
}

v.like.setOnClickListener(new Dislike(mId,owner_id, v, lPosition));
}
});
}
}[/syntax]
и для Dislike
[syntax=java5]public class Dislike implements View.OnClickListener {
private String mId;
private ViewHolder holder;
private String owner_id;
private int lPosition;

public Dislike(String id, String oid, ViewHolder h, int lPosition) {
mId = id;
holder = h;
owner_id = oid;
this.lPosition = lPosition;
}

@Override
public void onClick(final View view) {

final ViewHolder v = holder;
v.like.setText("dislikes");
getItem(lPosition).setCanLike(1);
getItem(lPosition).setLikeCount(likeCount);
if(Constants.Extra.SDK < android.os.Build.VERSION_CODES.JELLY_BEAN) {
v.like.setBackgroundDrawable(like_empty);
} else {
v.like.setBackground(like_empty);
}
v.like.setOnClickListener(new Like(mId,owner_id, v, lPosition));
}
});
}
}[/syntax]
зы: если опечатки есть, сам разберешься

Re: Одинаковый хэш-код элементов ListView

Добавлено: 18 фев 2015, 15:33
KamiSempai
mdzht писал(а):
ne1c писал(а):Хорошо. Я сохраняю значение isSelected в Map-e, но результат тот же:
братишка, лучше обработай likeButton, для обоих случаев
[syntax=java5]public class Like implements View.OnClickListener {
private String mId;
private ViewHolder holder;
private String owner_id;
private int lPosition;

public Like(String id, String oid, ViewHolder h, int lPosition) {
mId = id;
holder = h;
owner_id = oid;
this.lPosition = lPosition;
}

@Override
public void onClick(final View view) {

final ViewHolder v = holder;
v.like.setText("likes");
getItem(lPosition).setCanLike(0);
getItem(lPosition).setLikeCount(likeCount);
if(Constants.Extra.SDK < android.os.Build.VERSION_CODES.JELLY_BEAN) {
v.like.setBackgroundDrawable(like_full);
} else {
v.like.setBackground(like_full);
}

v.like.setOnClickListener(new Dislike(mId,owner_id, v, lPosition));
}
});
}
}[/syntax]
и для Dislike
[syntax=java5]public class Dislike implements View.OnClickListener {
private String mId;
private ViewHolder holder;
private String owner_id;
private int lPosition;

public Dislike(String id, String oid, ViewHolder h, int lPosition) {
mId = id;
holder = h;
owner_id = oid;
this.lPosition = lPosition;
}

@Override
public void onClick(final View view) {

final ViewHolder v = holder;
v.like.setText("dislikes");
getItem(lPosition).setCanLike(1);
getItem(lPosition).setLikeCount(likeCount);
if(Constants.Extra.SDK < android.os.Build.VERSION_CODES.JELLY_BEAN) {
v.like.setBackgroundDrawable(like_empty);
} else {
v.like.setBackground(like_empty);
}
v.like.setOnClickListener(new Like(mId,owner_id, v, lPosition));
}
});
}
}[/syntax]
зы: если опечатки есть, сам разберешься
Дурной пример.

Re: Одинаковый хэш-код элементов ListView

Добавлено: 18 фев 2015, 15:37
ne1c
Я сделал так:

Код: Выделить всё

public class NewsAdapter extends BaseAdapter{ 
    private String[] items = new String[]{"TEST TEXT 1","TEST TEXT 2","TEST TEXT 3","TEST TEXT 4", 
            "TEST TEXT 5", "TEST TEXT 6", "TEST TEXT 7","TEST TEXT 8","TEST TEXT 9"}; 
 
    private Context mContext; 
    private LayoutInflater mInflater; 
    private Map<Integer, Boolean> mItemStatus; 
 
    public NewsAdapter(Context _context){ 
        mContext = _context; 
        mInflater = (LayoutInflater) _context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
 
        mItemStatus = new HashMap<>(); 
        for (int i = 0; i < 9; i++){ 
            mItemStatus.put(i, false); 
        } 
    } 
 
    @Override 
    public int getCount() { 
        return items.length; 
    } 
 
    @Override 
    public Object getItem(int position) { 
        return null; 
    } 
 
    @Override 
    public long getItemId(int position) { 
        return position; 
    } 
 
    @Override 
    public View getView(final int position, View convertView, final ViewGroup parent) { 
        View view = convertView; 
        ViewHolderItem viewHolderItem; 
        if (view == null){ 
            view = mInflater.inflate(R.layout.item_news, parent, false); 
 
            viewHolderItem = new ViewHolderItem(); 
 
            viewHolderItem.infoText = (TextView) view.findViewById(R.id.info_text_textView); 
            viewHolderItem.likeButton = (FButton) view.findViewById(R.id.like_post_button); 
            viewHolderItem.card = (CardView) view.findViewById(R.id.news_item_cardView); 
            if (mItemStatus.get(position)){ 
                viewHolderItem.likeButton.setSelected(true); 
            } 
 
            view.setTag(viewHolderItem); 
 
        } else { 
            viewHolderItem = (ViewHolderItem) view.getTag(); 
        } 
 
        viewHolderItem.infoText.setText(items[position]); 
        viewHolderItem.likeButton.setSelected(mItemStatus.get(position)); //Эта строка всё порешала
        viewHolderItem.likeButton.setOnClickListener(new View.OnClickListener() { 
            @Override 
            public void onClick(View v) { 
                if (mItemStatus.get(position)) { 
                    v.setSelected(false); 
                    mItemStatus.put(position, false); 
                } else { 
                    v.setSelected(true); 
                    mItemStatus.put(position, true); 
                } 
            } 
        }); 
 
        viewHolderItem.card.setOnClickListener(new View.OnClickListener() { 
            @Override 
            public void onClick(View v) { 
                Toast.makeText(mContext, "Position: " + position + " Hash: " + Long.toString(v.hashCode()), Toast.LENGTH_SHORT).show(); 
                Log.d("MYTAG", "Position: " + position + " Hash: " + Long.toString(v.hashCode())); 
            } 
        }); 
 
        return view; 
    } 
 
    static class ViewHolderItem { 
        TextView infoText; 
        FButton likeButton; 
        CardView card; 
    } 
} 
Но теперь не сохраняет состояние нажатой кнопки при смене ориентации. Я полагаю mItemStatus надо сохранять в Bundle, но как передать Bundle в адаптер?

Re: Одинаковый хэш-код элементов ListView

Добавлено: 18 фев 2015, 15:46
mdzht
KamiSempai писал(а): Дурной пример.
обоснуй.

Re: Одинаковый хэш-код элементов ListView

Добавлено: 18 фев 2015, 15:58
KamiSempai
ne1c писал(а):Я сделал так:
Вот это уже на много лучше.
Что я посоветую:
1) Проверять полученные значения из HashMap на null;
2) Добавить в адаптер метод onSaveInstanceState, где будет сохраняться HashMap.
3) Когда в активити будет вызываться onSaveInstanceState, вызывать этот же метод и у адаптера, тем самым сохраняя данные адаптера в Bundle.
4) Добавить в адаптер метод onRestoreInstanceState, где будет восстанавливаться HashMap.
5) Когда в активити будет вызываться onRestoreInstanceState, вызывать этот же метод и у адаптера.

PS: Описанный мной метод не идеален, но для новичка сойдет.
mdzht писал(а):KamiSempai писал(а):
Дурной пример.

обоснуй.
Мало того, что в примере View и Model перемешаны в одном месте, так еще при клике, каждый раз, создаются новые объекты.

Re: Одинаковый хэш-код элементов ListView

Добавлено: 18 фев 2015, 18:35
mdzht
KamiSempai писал(а):Мало того, что в примере View и Model перемешаны в одном месте, так еще при клике, каждый раз, создаются новые объекты.
как сделать лучше?

Re: Одинаковый хэш-код элементов ListView

Добавлено: 18 фев 2015, 19:08
KamiSempai
mdzht писал(а):как сделать лучше?
ne1c писал(а):Я сделал так:

Код: Выделить всё

public class NewsAdapter extends BaseAdapter{ 
    private String[] items = new String[]{"TEST TEXT 1","TEST TEXT 2","TEST TEXT 3","TEST TEXT 4", 
            "TEST TEXT 5", "TEST TEXT 6", "TEST TEXT 7","TEST TEXT 8","TEST TEXT 9"}; 
 
    private Context mContext; 
    private LayoutInflater mInflater; 
    private Map<Integer, Boolean> mItemStatus; 
 
    public NewsAdapter(Context _context){ 
        mContext = _context; 
        mInflater = (LayoutInflater) _context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
 
        mItemStatus = new HashMap<>(); 
        for (int i = 0; i < 9; i++){ 
            mItemStatus.put(i, false); 
        } 
    } 
 
    @Override 
    public int getCount() { 
        return items.length; 
    } 
 
    @Override 
    public Object getItem(int position) { 
        return null; 
    } 
 
    @Override 
    public long getItemId(int position) { 
        return position; 
    } 
 
    @Override 
    public View getView(final int position, View convertView, final ViewGroup parent) { 
        View view = convertView; 
        ViewHolderItem viewHolderItem; 
        if (view == null){ 
            view = mInflater.inflate(R.layout.item_news, parent, false); 
 
            viewHolderItem = new ViewHolderItem(); 
 
            viewHolderItem.infoText = (TextView) view.findViewById(R.id.info_text_textView); 
            viewHolderItem.likeButton = (FButton) view.findViewById(R.id.like_post_button); 
            viewHolderItem.card = (CardView) view.findViewById(R.id.news_item_cardView); 
            if (mItemStatus.get(position)){ 
                viewHolderItem.likeButton.setSelected(true); 
            } 
 
            view.setTag(viewHolderItem); 
 
        } else { 
            viewHolderItem = (ViewHolderItem) view.getTag(); 
        } 
 
        viewHolderItem.infoText.setText(items[position]); 
        viewHolderItem.likeButton.setSelected(mItemStatus.get(position)); //Эта строка всё порешала
        viewHolderItem.likeButton.setOnClickListener(new View.OnClickListener() { 
            @Override 
            public void onClick(View v) { 
                if (mItemStatus.get(position)) { 
                    v.setSelected(false); 
                    mItemStatus.put(position, false); 
                } else { 
                    v.setSelected(true); 
                    mItemStatus.put(position, true); 
                } 
            } 
        }); 
 
        viewHolderItem.card.setOnClickListener(new View.OnClickListener() { 
            @Override 
            public void onClick(View v) { 
                Toast.makeText(mContext, "Position: " + position + " Hash: " + Long.toString(v.hashCode()), Toast.LENGTH_SHORT).show(); 
                Log.d("MYTAG", "Position: " + position + " Hash: " + Long.toString(v.hashCode())); 
            } 
        }); 
 
        return view; 
    } 
 
    static class ViewHolderItem { 
        TextView infoText; 
        FButton likeButton; 
        CardView card; 
    } 
} 
KamiSempai писал(а):Что я посоветую:
1) Проверять полученные значения из HashMap на null;
2) Добавить в адаптер метод onSaveInstanceState, где будет сохраняться HashMap.
3) Когда в активити будет вызываться onSaveInstanceState, вызывать этот же метод и у адаптера, тем самым сохраняя данные адаптера в Bundle.
4) Добавить в адаптер метод onRestoreInstanceState, где будет восстанавливаться HashMap.
5) Когда в активити будет вызываться onRestoreInstanceState, вызывать этот же метод и у адаптера.
:lol:

А вообще, это долго объяснять. Советую изучить паттерны MVC, MVP, MVVM. Тогда все станет ясно.

Re: Одинаковый хэш-код элементов ListView

Добавлено: 18 фев 2015, 19:12
mdzht
KamiSempai писал(а):Что я посоветую:
1) Проверять полученные значения из HashMap на null;
2) Добавить в адаптер метод onSaveInstanceState, где будет сохраняться HashMap.
3) Когда в активити будет вызываться onSaveInstanceState, вызывать этот же метод и у адаптера, тем самым сохраняя данные адаптера в Bundle.
4) Добавить в адаптер метод onRestoreInstanceState, где будет восстанавливаться HashMap.
5) Когда в активити будет вызываться onRestoreInstanceState, вызывать этот же метод и у адаптера.
:lol:

А вообще, это долго объяснять. Советую изучить паттерны MVC, MVP, MVVM. Тогда все станет ясно.
я использую ArrayList<MYOBJECT> почему хэшмап? может(определенно) я что то не знаю.

Re: Одинаковый хэш-код элементов ListView

Добавлено: 19 фев 2015, 11:14
KamiSempai
По тому, что HashMap заполняется по мере выбора элементов, а ArrayList нужно сразу создавать нужного размера.
Кстати, здесь даже лучше подойдет HashSet. Советую использовать его.