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

Posted

技术标签:

【中文标题】单击图像时如何更改ListView中的图像?【英文标题】:How to change an image in a ListView, when the image is clicked? 【发布时间】:2016-07-21 04:50:06 【问题描述】:

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

我目前在 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;
    


【问题讨论】:

显示你的适配器,我会给你一个解决方案 在这里查看我的答案。您需要在适配器中放置一个 else 条件。维护一些其他列表以跟踪单击了哪个项目。 ***.com/a/36327143/3145960 【参考方案1】:

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

例如,您有 100 个元素,您触摸第一个元素 ImageButton 并更改该按钮。也许屏幕上显示了列表的 5 个元素,而您更改了第一个元素。但是,如果您滚动到元素编号 15,则系统不会创建 15 个视图,而是采用您之前单击的第一个视图并更改内容。 因此,您希望有一个带有“+”ImageButton 图标的视图,但您看到另一个图标,这是因为您必须将视图状态保留在模型对象中,并在每次调用 'getView' 时设置状态。

发布您的列表适配器以查看如何实现。

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

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

【讨论】:

你是对的,但是使用 ListView frank 将无法仅在图像上设置点击侦听器(他说“每当单击该图像时(不是整个 ListView 项目,只是图像)”) 如果视图是ImageButton,在ImageButton上设置点击监听提供了预期的行为,所以他的点击监听的实现是可以的。【参考方案2】:

使用 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...



//...

【讨论】:

【参考方案3】:

这是我实现您想要的目标的建议:

    在您的适配器中创建一个接口:
public interface YourInterface
        void selectedImage(int position,ImageView iamgeView);   
    
    在您刚刚创建的适配器中创建变量接口:
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);
            
        );

最后在你的课堂活动中,实现 YourInterface 并在那里改变你的 ImageView:

@Override

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

【讨论】:

我总是使用这种解决方法来让我的列表视图和我的活动相互交流,对我来说,当您将来想要更改或修改您的项目时也更容易【参考方案4】:

我要感谢大家的支持。不幸的是,由于我的代码编写方式(相当混乱,并且没有太多考虑我的教授教我的内容),我无法让大多数这些解决方案发挥作用。然而,我确实找到了一个符合我自己的框架的解决方案。不幸的是,我无法重做整个适配器方法,或实现各种接口,这将导致我不得不为看似微不足道的事情重写大量代码。

因此,如果将来有人发现自己处于这种情况,这是我的解决方案:

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

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

public void toggleWatchedStatus()
    watchedStatus = !watchedStatus;

在 getView 方法中,我只是简单地获取 ImageButton 的引用,然后根据“hasSeen”返回的布尔值,我将 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 的方法“notifyDataSetChanged()” 这完成了这个过程,并让 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();
    
);

再次感谢您花时间提供信息,其中很多确实将我引向正确的方向,我只需要按照我的代码结构正确使用这些信息。

【讨论】:

以上是关于单击图像时如何更改ListView中的图像?的主要内容,如果未能解决你的问题,请参考以下文章

如何在单击图像时动态更改模态的内容

Swift:如何在单击提交按钮时更改 UIButton 图像

VB中如何在ListView中添加ImageList中的图像

如何根据 android studio 中的 listview 项目点击更改活动图像和文本? java 或 kotlin

单击tableview单元iphone时如何更改图像

Jquery Mobile UI:如何在listview中更改图像大小