如何在单击图像时更改ListView中的图像?

时间:2016-04-01 11:35:24

标签: android listview android-studio

编辑:我已经解决了这个问题,如果有兴趣,请看看我的答案,看看我是怎么做到的!

我目前在Android Studio工作。我有一个ListView,我填充了几个项目。在每个项目中都有一个ImageButton,它有一个" +"作为形象。我想要做的是,无论何时单击该图像(不是整个ListView项目,只是图像),我想要" +"的图像。成为另一个形象。任何帮助将不胜感激,因为这一直是一个持续的问题!

以下是我尝试使用的当前代码:

final ImageButton movieSeen = (ImageButton convertView.findViewById(R.id.movieWatched);
        movieSeen.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                movieSeen.setImageResource(R.drawable.ic_check_circle_black_24dp);
            }
        });

目前这确实更新了我正确点击的图像,但它也更新了尚未在屏幕上呈现的图像,因此当我向下滚动列表视图时,其他对象也会更改为ic_check_circle_black_24dp。

我想要的是非常简单的,我只是不知道如何实现它。我只想单击一个ImageButton,它位于ListView上的一个项目内,并让ImageButton更改其图像资源。

这是我要求的自定义数组适配器!

private class MovieScrollAdapter extends ArrayAdapter<Movie> {//custom array adapter
    private Context context;
    private List<Movie> movies;

    public MovieScrollAdapter(Context context, List<Movie> movies){
        super(context, -1, movies);
        this.context = context;
        this.movies = movies;

        if(this.movies.isEmpty()){//if no results were returned after all processing, display a toast letting the user know
            Toast.makeText(getApplicationContext(), R.string.no_matches, Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent){
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) context.
                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.movie_layout, parent, false);
        }

        TextView title = (TextView) convertView.findViewById(R.id.title);
        title.setText(movies.get(position).getTitle());

        TextView plot = (TextView) convertView.findViewById(R.id.plot);
        plot.setText(movies.get(position).getPlot());

        TextView genre = (TextView) convertView.findViewById(R.id.genre);
        genre.setText(movies.get(position).getGenre());


        TextView metaScore = (TextView) convertView.findViewById(R.id.metascore);

        if(movies.get(position).getMetaScore() == -1){//if the metaScore is set to -1, that means movie has not been rated, which by inference means it is not yet released
            metaScore.setText(R.string.movie_not_released);
            metaScore.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9.5f);//smaller text so it fits without breaking anything
            metaScore.setTextColor(getColor(R.color.colorAccent));
        } else {
            metaScore.setText("    " + Integer.valueOf(movies.get(position).getMetaScore()).toString() + " ");//using white space for minor formatting, instead of altering margins each time this is rendered
            metaScore.setTextSize(TypedValue.COMPLEX_UNIT_SP, 25);

            //setting up a "highlighted" background to achieve metacritic square effect
            Spannable spanText = Spannable.Factory.getInstance().newSpannable(metaScore.getText());
            spanText.setSpan(new BackgroundColorSpan(getColor(R.color.metaScore)), 3, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            metaScore.setText(spanText);
            metaScore.setTextColor(getColor(android.R.color.primary_text_dark));

        }

        ImageView image = (ImageView) convertView.findViewById(R.id.imageView);
        new ImageDownloadTask((ImageView)image).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, movies.get(position).getPosterURL());//because there are several images to load here, we let these threads run parallel

        title.setOnClickListener(new View.OnClickListener() {//setting up a simple onClickListener that will open a link leading to more info about the movie in question!
            @Override
            public void onClick(View v) {
                Uri uri = Uri.parse(movies.get(position).getMovieURL());
                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                startActivity(intent);
            }
        });

        final ImageButton movieSeen = (ImageButton) convertView.findViewById(R.id.movieWatched);
        movieSeen.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                movieSeen.setImageResource(R.drawable.ic_check_circle_black_24dp);
            }
        });




        return convertView;
    }

}

4 个答案:

答案 0 :(得分:3)

问题在于ListView,视图正被重用以节省内存并避免创建大量视图,因此当您更改视图时,它会保留状态,同时重用它以显示另一个项目。 / p>

例如,您有100个元素,触摸第一个元素ImageButton并更改该按钮。也许在屏幕上显示列表的5个元素,并且您更改了第一个元素。但是,如果滚动到元素编号15,系统不会创建15个视图,而是使用之前单击的第一个视图并更改内容。 所以,你期望有一个&#34; +&#34; ImageButton图标但你看到另一个图标,因为你必须将视图状态保持在模型对象中,并且每次都设置状态&#39; getView&#39;被称为。

发布您的列表适配器以了解如何实施。

更新: 现在我看到你的适配器实现我建议你在Movie类中添加一个int字段来保存你想要在ImageButton上显示的资源ID。然后在onClickListener内,您必须将要在视图点击时显示的资源设置为此字段,然后调用notifyDataSetChanged()。之后你必须在getView()里面做:

movieSeen.setImageResource(movies.get(position).getButtonImageResource());

答案 1 :(得分:1)

使用RecyclerView并在视图持有者的ImageButton上设置OnItemClickListener。

这已经回答了question应该有所帮助。

以下改编的代码来自这个不错的tutorial。将ReciclerView与这样的适配器一起使用将解决您的问题。

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

private ArrayList<String> mDataset;

public class ViewHolder extends RecyclerView.ViewHolder {
    public ImageView imageView;
    public TextView txtHeader;

    public ViewHolder(View v) {
        super(v);
        txtHeader = (TextView) v.findViewById(R.id.xxx);
        imageView = (ImageView) v.findViewById(R.id.yyy);
    }
}

public MyAdapter(ArrayList<String> myDataset) {
    mDataset = myDataset;
}

@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.rowlayout, parent, false);
    ViewHolder vh = new ViewHolder(v);
    return vh;
}

// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    final String name = mDataset.get(position);
    holder.txtHeader.setText(mDataset.get(position));


    holder.imageView.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            // Do here what you need to change the image content
        }
    });

    holder.itemView.setBackground(....); // Initialize your image content here...

}

//...

}

答案 2 :(得分:0)

以下是我想要达到你想要的建议:

  1. 在适配器中创建接口:
  2. public interface YourInterface{
            void selectedImage(int position,ImageView iamgeView);   
        }
    
    1. 在您刚刚创建的适配器中创建变量接口:
    2. private YourInterface yourInterface;
      

      并使你的适配器构造函数如下:

      public YourAdapterConstructor(YourInterface yourInterface){
      
              this.yourInterface = yourInterface;
          }
      
      在ImageView onClickListener中

      final ImageButton movieSeen = (ImageButton) convertView.findViewById(R.id.movieWatched);
              movieSeen.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      yourInterface.selectedImage(position, imageView);
                  }
              });
      

      然后最后在你的类活动中,实现你的接口并在那里改变你的ImageView:

      @Override
      
          public void selectedImage(final int position,final ImageView imageView) {
      //change your image view here
      }
      

答案 3 :(得分:0)

我要感谢大家的支持。不幸的是,随着我的代码编写方式(相当混乱而且不太考虑我的教授教给我的东西),我无法使大多数这些解决方案起作用。但是,我确实找到了一个符合我自己的框架的解决方案。不幸的是,我无法重做我的整个适配器方法,或实现各种接口,这些接口会导致我必须重写大量的代码,看似微不足道。

所以,如果将来发现自己处于这种情况,这是我的解决方案:

在Movie类中,我添加了一个表示我的值的布尔值,以及一些getter和setter:

private boolean watchedStatus;
public boolean hasSeen() {return watchedStatus;}

public void toggleWatchedStatus(){
    watchedStatus = !watchedStatus;
}

在getView方法中,我只是获取对ImageButton的引用,然后根据&#34; hasSeen,&#34;返回的布尔值。我将ImageResource设置为以下两种状态之一:

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

ImageButton movieSeen = (ImageButton) convertView.findViewById(R.id.movieSeen);
        if(movies.get(position).hasSeen()){
            movieSeen.setImageResource(R.drawable.ic_check_circle_black_24dp);
        } else {
            movieSeen.setImageResource(R.drawable.ic_add_circle_black_24dp);
        }

}

接下来,我重写OnClickListener,并使其无论何时单击该按钮,都会切换Movie.java类中的布尔值。这里的关键是使用ArrayAdapter的方法&#34; notifyDataSetChanged()&#34;这样就完成了这个过程,让ListView知道它应该自行更新:

final ImageButton movieSeenForClick = (ImageButton) convertView.findViewById(R.id.movieSeen);
movieSeen.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    //movieSeenForClick.setImageResource(R.drawable.ic_check_circle_black_24dp);
        movies.get(position).toggleWatchedStatus();
        System.out.println(movies.get(position).hasSeen() + " ------- position: " + position);
        notifyDataSetChanged();
    }
});

再次感谢您提供信息所花费的时间,其中很多确实引导我指向正确的方向,我只需要正确地使用我的代码构建方式的信息。