草庐IT

android - AsyncTask 加载图片 RecyclerView

coder 2023-12-21 原文

我正在尝试创建一个类似于那个的应用程序: An app with some images and description (cardview) in a recyclerview

首先,我从数据库中加载 CardView 的所有信息 图像的标题、描述和图像的 URL。 当我将所有信息放入 RecyclerView 时,(标题和描述)显示正确,但对于图像,我创建了一个 AsyncTask 类来从 URL 加载图像,让用户不必等待太多时间等待应用程序响应。

如果用户滚动缓慢,图像加载正确,一切都很好,但如果我快速滚动,我会遇到一些问题,例如,第 3 项中显示的图像显示在最后一项中,直到最后一项的图像已加载并刷新....

这里是我加载图像的适配器的一些代码:

@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
    if (holder instanceof EventosViewHolder) {
        ......
        //Load the image (getFoto() get drawable)
        if (eventoInfoAux.getFoto()==null){
            CargarImagen cargarImagen = new CargarImagen(((EventosViewHolder) holder).vRelativeLayout,eventoInfo,position);
            cargarImagen.execute();
        }else{
            ((EventosViewHolder) holder).vRelativeLayout.setBackground(eventoInfoAux.getFoto());
        }   
    }
}

这里是 CargarImagen 类的代码:

//Clase para cargar imagenes con una tarea asíncrona desde un url de la imagen
public class CargarImagen extends AsyncTask<String, String, Boolean>{

//RelativeLayout donde se introduce la imagen de fondo
RelativeLayout relativeLayout=null;
//EventoInfo para obtener la url de la imagen
List<EventoInfo> eventoInfo=null;
//Posición de la imagen clicada
int i;

//Se cargan todos los valores de las variables necesarias desde los datos introducidos
public CargarImagen(RelativeLayout relativeLayout,List<EventoInfo> eventoInfo,int i) {
    this.relativeLayout = relativeLayout;
    this.eventoInfo = eventoInfo;
    this.i = i;
}

//Pintamos el fondo de gris mientras se está cargando la imagen
@Override 
protected void onPreExecute() { 
    super.onPreExecute(); 
    relativeLayout.setBackgroundResource(R.color.fondo_card_view);
}

//Se realiza la carga de la imagen en segundo plano
@SuppressWarnings("deprecation")
@Override
protected Boolean doInBackground(String... params) {
    //Necesario para hacer la llamada a la red
    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();      
    StrictMode.setThreadPolicy(policy);

    //obtenemos la imagen con el metodo getBitmapFromURL
    Bitmap myImage = getBitmapFromURL(eventoInfo.get(i).url);

    //Si se tiene imagen se pinta, si no no se hace nada
    if (myImage !=null){
        Drawable dr = new BitmapDrawable(myImage);
        eventoInfo.get(i).setFoto(dr);
        return true;
    }
    return false;
}

//Al finalizar la carga de la imagen se pinta el fondo del relative layout
protected void onPostExecute(Boolean result) {
    if(result){
        relativeLayout.setBackground(eventoInfo.get(i).foto);
    }   
}

//Metodo para obtener un bitmap desde una url
public Bitmap getBitmapFromURL(String imageUrl) {
    try {

        URL url = new URL(imageUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setDoInput(true);
        connection.connect();
        InputStream input = connection.getInputStream();
        Bitmap myBitmap = BitmapFactory.decodeStream(input);
        return myBitmap;

    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

}

我可以给你 .apk 文件,你可以看到问题。

提前致谢。 :)

这里是我的适配器的完整代码

 import java.util.List;

import com.abdevelopers.navarraongoing.R;
import com.abdevelopers.navarraongoing.detalle.DetalleActivity;

import android.content.Intent;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class EventosAdapter extends RecyclerView.Adapter<ViewHolder>{

    private static final int TYPE_EVENTO = 0;
    private static final int TYPE_FOOTER = 1;

//lista de eventos
private List<EventoInfo> eventoInfo;


private String usuario;
private EventoInfo  eventoInfoAux;
private PaginaInicioActivity paginaInicio;

//Se pasan los valores necesarios para obtener información de los eventos
public EventosAdapter(List<EventoInfo> eventoInfo, String usuario, PaginaInicioActivity paginaInicio ) {
    this.eventoInfo = eventoInfo;
    this.usuario = usuario;
    this.paginaInicio = paginaInicio;
}


//Metodo para obtener el numero de items en la lista que introducimos
@Override
public int getItemCount() {
    return eventoInfo.size();
}

@Override
public int getItemViewType(int position) {
    if (position + 1  == getItemCount()) {
        return TYPE_FOOTER;
    } else {
        return TYPE_EVENTO;
    }
}

//Se asignan los datos a cada uno de los elementos de la cardview
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
    if (holder instanceof EventosViewHolder) {
        //Obtenemos cada uno de los eventos
        eventoInfoAux = eventoInfo.get(position);
        //eventosViewHolder = holder;

        //Introducimos el título del evento
        ((EventosViewHolder) holder).vTitle.setText(eventoInfoAux.titulo);
        //Introdicumos la fecha y el lugar del evento
        ((EventosViewHolder) holder).vFechaLugar.setText(eventoInfoAux.fecha+", "+eventoInfoAux.lugar);
        //obtenemos el numero de asistentes al evento
        String asistentes = Integer.toString(eventoInfoAux.asistentes);
        ((EventosViewHolder) holder).vLikeButton.setText(asistentes);

        //Se pinta el boton de like dependiendo de si está o no like 
        if(eventoInfoAux.like){
            ((EventosViewHolder) holder).vLikeButton.setBackgroundResource(R.drawable.button_like_liked);
        }else{
            ((EventosViewHolder) holder).vLikeButton.setBackgroundResource(R.drawable.button_like);
        }

        //Se pinta la imagen del evento dependiendo de si está en la base de datos o no
        if (eventoInfoAux.foto==null){
            CargarImagen cargarImagen = new CargarImagen(((EventosViewHolder) holder).vRelativeLayout,eventoInfo,position);
            cargarImagen.execute();
        }else{
            ((EventosViewHolder) holder).vRelativeLayout.setBackground(eventoInfoAux.foto);
        }   
    }
}



@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == TYPE_EVENTO) {
        View view = LayoutInflater.
                from(parent.getContext()).
                inflate(R.layout.evento_card_view, parent, false);
        return new EventosViewHolder(view,eventoInfo,usuario,paginaInicio);
    } else if (viewType == TYPE_FOOTER) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.pie_carga_pagina_inicio, parent, false);
        view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
        return new FooterViewHolder(view);
    }
    return null;
}

//Clase para crear el footerview donde se cargan más eventos
class FooterViewHolder extends ViewHolder {

    public FooterViewHolder(View view) {
        super(view);
    }

}

//Clase para crear los eventos con su cardview
class EventosViewHolder extends ViewHolder implements View.OnClickListener{

    private TextView vTitle;
    private TextView vFechaLugar;
    private Button vLikeButton;
    private RelativeLayout vRelativeLayout;
    private List<EventoInfo> eventoInfo;
    private String usuario;
    private LikesDDBB likesManager;
    private PaginaInicioActivity paginaInicio;
    private CardView vCardView;

    public EventosViewHolder(View itemView,List<EventoInfo> eventoInfo,String usuario, 
            PaginaInicioActivity paginaInicio) {
        super(itemView);

        this.paginaInicio = paginaInicio;
        this.usuario = usuario;
        vTitle = (TextView)itemView.findViewById(R.id.titulo);
        vFechaLugar =  (TextView) itemView.findViewById(R.id.fecha_lugar);
        vLikeButton = (Button)  itemView.findViewById(R.id.like_button);
        vRelativeLayout = (RelativeLayout) itemView.findViewById(R.id.layout_cardview);
        vCardView = (CardView)itemView.findViewById(R.id.card_view);

        //Valores por defecto de los botones y del fondo en caso de no haber like ni foto
        vLikeButton.setBackgroundResource(R.drawable.button_like);
        vRelativeLayout.setBackgroundResource(R.color.fondo_card_view);
        vLikeButton.setOnClickListener(this);
        vCardView.setOnClickListener(this);
        vLikeButton.setTag("boton");
        vCardView.setTag("evento");

        this.eventoInfo = eventoInfo;
    }


    @SuppressWarnings("deprecation")
    @Override
    public void onClick(View v) {
        if(v.getTag().equals("boton")){
            likesManager = new LikesDDBB(this.eventoInfo.get(getPosition()).like, usuario,
                    vLikeButton, getPosition(), eventoInfo, paginaInicio);
            likesManager.execute();
        }else if(v.getTag().equals("evento")){
            Intent inicion = new Intent(paginaInicio,DetalleActivity.class);

            paginaInicio.startActivity(inicion);
        }

    }

}

//Para obtener la lista de eventos desde la clase PaginaInicioActivity
public List<EventoInfo> getEventoInfo() {
    return eventoInfo;
}

public void setEventoInfo(List<EventoInfo> eventoInfo) {
    this.eventoInfo = eventoInfo;
}

}

最佳答案

正如名称“RecyclerView”所示,它回收/重复使用创建的 View 来显示您的项目/卡片。

因此,假设您的 RecyclerView 有 3 个 CardView,它会在您滚动时回收,我们有 4 个项目来显示其内容。

最初项目 1 的内容显示在 CardView 1 中,项目 2 显示在 CardView 2 中,项目 3 显示在 CardView 3 中。

现在,当您滚动时,CardView 1 消失,被回收,第 4 项的内容显示在 CardView 1 中。

只要您不重置之前插入的内容,CardView 1 就会显示它们 - 在您的情况下 - 只要 AsyncTask 需要完成之前设置的项目的内容就会显示。

要解决您的问题,您可能需要使用像这样的图像加载(和缓存)库:

您的解决方案也容易出现竞争条件(当后面的任务先于前面的任务完成时)

关于android - AsyncTask 加载图片 RecyclerView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29600630/

有关android - AsyncTask 加载图片 RecyclerView的更多相关文章

  1. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  2. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  3. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

  4. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

  5. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  6. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  7. ruby-on-rails - 使用 gmaps4rails 动态加载谷歌地图标记 - 2

    如何只加载map边界内的标记gmaps4rails?当然,在平移和/或缩放后加载新的。与此直接相关的是,如何获取map的当前边界和缩放级别? 最佳答案 我是这样做的,我只在用户完成平移或缩放后替换标记,如果您需要不同的行为,请使用不同的事件监听器:在你看来(index.html.erb):{"zoom"=>15,"auto_adjust"=>false,"detect_location"=>true,"center_on_user"=>true}},false,true)%>在View的底部添加:functiongmaps4rail

  8. ruby-on-rails - Rails 3,在RAILS_ROOT上方显示来自本地文件系统的jpg图片 - 2

    我正在尝试找出一种方法来显示来自不在RAILS_ROOT下(在RedHat或Ubuntu环境中)的已安装文件系统的图像。我不想使用符号链接(symboliclink),因为这个应用程序实际上是通过Tomcat部署的,而当我关闭Tomcat时,Tomcat会尝试跟随符号链接(symboliclink)并删除挂载中的所有图像。由于这些文件的数量和大小,将图像放在public/images下也不是一种选择。我查看了send_file,但它只会显示一张图片。我需要在一个格式良好的页面中显示6个请求的图像。由于膨胀,我宁愿不使用Base64编码,但我不知道如何将图像数据与呈现的页面一起传递下去。

  9. ruby-on-rails - 是否可以让 ActiveRecord 为使用 :joins option? 加载的行创建对象 - 2

    我需要做这样的事情classUser'User',:foreign_key=>'abuser_id'belongs_to:gameendclassGame['JOINabuse_reportsONusers.id=abuse_reports.abuser_id','JOINgamesONgames.id=abuse_reports.game_id'],:group=>'users.id',:select=>'users.*,count(distinctgames.id)ASgame_count,count(abuse_reports.id)asabuse_report_count',:

  10. ruby - 运行 rackup private_pub.ru -s thin -E production 命令时无法加载此类文件 -- thin (LoadError) - 2

    我指的是pubrailscasttutorial并已正确执行所有步骤,但在运行最后一个命令时,即rackupprivate_pub.ru-sthin-Eproduction为了架设faye服务器,我收到以下错误:/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in`require':cannotloadsuchfile--thin(LoadError)from/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in`require'from/var/lib/gems/1.9.1/gems

随机推荐