Picasso не загружает изображения из хранилища Firebase в RecyclerView

Я пытаюсь получить изображения из Firebase Storage через библиотеку Picasso и отобразить их в RecyclerView.

Я также попытался загрузить изображения с помощью других библиотек, таких как Glide и Fresco, но изображения все равно не отображаются.

Ниже приведен код этого приложения:

BlogRecyclerAdapter.java:

public BlogRecyclerAdapter(Context context, List<Blog> blogList) {
        this.context = context;
        this.blogList = blogList;
        this.firebaseStorage = firebaseStorage;
    }
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.post_row, parent, false);

        return new ViewHolder(view, context);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {

        Blog blog = blogList.get(position);
        //String imageUrl = null;

        holder.title.setText(blog.getTitle());
        holder.desc.setText(blog.getDesc());


        DateFormat dateFormat = DateFormat.getDateInstance();
        String formattedDate = dateFormat.format(new Date(Long.valueOf(blog.getTimestamp())).getTime());

        holder.timestamp.setText(formattedDate);

        String imageUrl = blog.getImage();

        //TODO: Use Picasso library to load image

        Picasso.get()
                .load(imageUrl)
                .into(holder.image);
    }

    @Override
    public int getItemCount() {
        return blogList.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView title;
        public TextView desc;
        public TextView timestamp;
        public ImageView image;
        String userid;

        public ViewHolder(View view, Context ctx) {
            super(view);


            title = (TextView) view.findViewById(R.id.postTitleList);
            desc = (TextView) view.findViewById(R.id.postTextList);
            image = (ImageView) view.findViewById(R.id.postImageList);
            timestamp = (TextView) view.findViewById(R.id.timestampList);

            userid = null;

            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // we can go to the next activity...

                }
            });

        }
    }
}

PostListActivity.java (действие, в котором отображается recyclerView):

public class PostListActivity extends AppCompatActivity {
    private DatabaseReference mDatabaseReference;
    private RecyclerView recyclerView;
    private BlogRecyclerAdapter blogRecyclerAdapter;
    private List<Blog> blogList;
    private FirebaseDatabase mDatabase;
    private FirebaseUser mUser;
    private FirebaseAuth mAuth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_post_list);

        mAuth = FirebaseAuth.getInstance();
        mUser = mAuth.getCurrentUser();

        mDatabase = FirebaseDatabase.getInstance();
        mDatabaseReference = mDatabase.getReference().child("MBlog");
        mDatabaseReference.keepSynced(true);

        blogList = new ArrayList<>();

        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case R.id.action_add:
                if (mUser != null && mAuth != null) {
                    startActivity(new Intent(PostListActivity.this, AddPostActivity.class));
                    finish();
                }
                break;
            case R.id.action_signout:

                if (mUser != null && mAuth != null) {
                    mAuth.signOut();
                    startActivity(new Intent(PostListActivity.this, MainActivity.class));
                    finish();
                }
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onStart() {
        super.onStart();

        mDatabaseReference.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

                Blog blog = dataSnapshot.getValue(Blog.class);

                blogList.add(blog);

                Collections.reverse(blogList);


                blogRecyclerAdapter = new BlogRecyclerAdapter(PostListActivity.this, blogList);
                recyclerView.setAdapter(blogRecyclerAdapter);
                blogRecyclerAdapter.notifyDataSetChanged();
            }

            @Override
            public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

            }

            @Override
            public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {

            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
}

Blog.java (Модель):

public class Blog {

    public String title;
    public String desc;
    public String image;
    public String timestamp;
    public String userid;

    public Blog() {
    }

    public Blog(String title, String desc, String image, String timestamp, String userid) {
        this.title = title;
        this.desc = desc;
        this.image = image;
        this.timestamp = timestamp;
        this.userid = userid;
    }


    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }

    public String getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }

    public String getUserid() {
        return userid;
    }

    public void setUserid(String userid) {
        this.userid = userid;
    }
}

post_row.xml:

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    android:layout_margin="5dp">



    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">


        <ImageView
            android:id="@+id/postImageList"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:cropToPadding="false"
            android:scaleType="centerCrop"
            android:src="@mipmap/add_btn"/>


        <TextView
            android:id="@+id/postTitleList"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="Post Title"
            android:textSize="18sp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/postTextList"
            android:paddingLeft="15dp"
            android:paddingRight="15dp"
            android:paddingTop="15dp"
            android:padding="10dp"
            android:text="Post Description Here"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <TextView
            android:id="@+id/timestampList"
            android:text="Date Created"
            android:padding="5dp"
            android:textStyle="italic"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>

</android.support.v7.widget.CardView>

activity_post_list.xml:

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.myblogapp.Activities.PostListActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginRight="8dp"
        android:layout_marginLeft="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="8dp" />
</android.support.constraint.ConstraintLayout>

build.gradle (приложение):

apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.myblogapp"
        minSdkVersion 16
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }


    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'com.android.support:appcompat-v7:28.0.0'
        implementation 'com.android.support.constraint:constraint-layout:1.1.3'
        implementation 'com.google.firebase:firebase-core:16.0.8'
        implementation 'com.google.firebase:firebase-core:16.0.8'
        implementation 'com.google.firebase:firebase-database:16.1.0'
        implementation 'com.firebaseui:firebase-ui-storage:4.3.1'
        implementation 'com.google.firebase:firebase-auth:16.2.0'
        implementation 'com.google.firebase:firebase-storage:16.0.4'
        implementation 'com.github.bumptech.glide:glide:3.7.0'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'com.android.support.test:runner:1.0.2'
        androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
        implementation 'com.android.support:recyclerview-v7:28.0.0'
        implementation 'com.android.support:cardview-v7:28.0.0'
        implementation 'com.squareup.picasso:picasso:2.71828'
        implementation 'com.theartofdev.edmodo:android-image-cropper:2.4.+'
        implementation 'com.facebook.fresco:fresco:1.10.0'
        implementation 'com.firebaseui:firebase-ui-storage:4.3.2'
    }


    apply plugin: 'com.google.gms.google-services'
}

При запуске кода я также обнаружил это сообщение об ошибке:

E / RecyclerView: адаптер не подключен; пропуск макета

Если вы обнаружите, что в моем коде что-то не так, сообщите мне, и я с нетерпением жду вашего ответа.


person Areen    schedule 19.05.2019    source источник
comment
дайте мне URL-адрес изображения, я проверю наличие проблемы с загрузкой и сначала установлю вам адаптер, а затем получу список, а затем установлю новый список через геттер и сеттер в адаптере и вызову notifydatasetchanged   -  person notTdar    schedule 19.05.2019
comment
измените android:layout_height="match_parent" на android:layout_height="wrap_content" в вашем recyclerview.   -  person Mohammed Farhan    schedule 20.05.2019
comment
Я пытаюсь получить много изображений из хранилища firebase, а не только одно, так какой URL вам дать?   -  person Areen    schedule 20.05.2019


Ответы (2)


Проблема заключается в порядке выполнения событий - адаптер должен быть настроен на представление ресайклера в onCreate (). В конце концов, это может быть не проблема с вашей библиотекой изображений.

Надеюсь, вы поймете, что проблему сложно воссоздать (потому что мне нужно создать тестовую базу данных Firebase и т. Д.) И быть на 100% уверенным в ответе. Я настоятельно рекомендую переупорядочить код адаптера.

1) Переместите эти две строки в конец onCreate () в PostListActivity:

  blogRecyclerAdapter = new BlogRecyclerAdapter(PostListActivity.this, blogList);
            recyclerView.setAdapter(blogRecyclerAdapter);

2) Добавьте метод в класс BlogRecyclerAdapter, который позволяет обновлять элементы, установленные для адаптера:

public void updateBlogList(ArrayList<Blog> newBlogs){
this.blogList = newBlogs;
notifyDataSetChanged();  }
  1. В методе onChildAdded () удалите три строки кода, которые были перемещены в onCreate () на шаге 1 выше, чтобы onChildAdded () содержал:

    Blog blog = dataSnapshot.getValue(Blog.class);
    
    blogList.add(blog);
    
    Collections.reverse(blogList);
    
    blogRecyclerAdapter.updateBlogList(blogList); 
    

Как я уже сказал, проблему сложно воссоздать, и в коде могут быть другие проблемы, но, надеюсь, это устранит ошибку «E / RecyclerView: адаптер не подключен; пропуск макета».

Кроме того, Glide кажется более сильной библиотекой для загрузки изображений из внешних источников. В одном из моих проектов мне удалось загрузить изображения в onBind () в адаптере, используя следующий код:

 mFirebaseDatabase = FirebaseDatabase.getInstance();
    mRef = mFirebaseDatabase.getReference(Constants.USERS_PATH).child(offeredRoute.getUserID());
    mFirebaseStorage = FirebaseStorage.getInstance();
    mStorageReference = mFirebaseStorage.getReference().child(Constants.IMAGES_PATH).child(offeredRoute.getUserID());

//download all other values
mRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
        mUserProfile = dataSnapshot.getValue(UserProfile.class);
        //set data to views
        if (mUserProfile != null) {
            holder.resultUsernameTv.setText(mUserProfile.getUser());

            if (mUserProfile.getPhotoUrl() == null || mUserProfile.getPhotoUrl().isEmpty()) {
                Timber.v("No photo saved yet");
            } else {
                StorageReference downloadRef = mStorageReference.child(mUserProfile.getPhotoUrl());
                Glide.with(mContext)
                        .using(new FirebaseImageLoader())
                        .load(downloadRef)
                        .into(holder.ImageViewRv);
            }
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {

    }
});

Спасибо

person Izzy Stannett    schedule 19.05.2019
comment
Ошибка E / RecyclerView: адаптер не подключен; пропуск макета исчезает, но изображения по-прежнему не видны. - person Areen; 20.05.2019
comment
Что теперь говорят журналы? Кроме того, какие зависимости у вас есть в build.gradle? - person Izzy Stannett; 20.05.2019
comment
Я добавил файл build.gradle в вопрос. - person Areen; 20.05.2019
comment
И вы сохранили allow read, write: if true; в правилах хранения Firebase? - person Izzy Stannett; 20.05.2019
comment
У вас есть дубликат implementation 'com.firebaseui:firebase-ui-storage:4.3.x в вашем build.gradle - person Izzy Stannett; 20.05.2019
comment
Да, у меня allow read, write: if true; сохранены правила хранилища Firebase. - person Areen; 20.05.2019
comment
Ok. И если вы выйдете из своего адаптера по URL-адресам изображений, что вы увидите? Они успешно справляются? String imageUrl = blog.getImage(); Log.i(TAG, "ImageUrl: " + imageUrl); - person Izzy Stannett; 20.05.2019
comment
Нет, URL-адреса изображений не отображаются в LogCat. - person Areen; 21.05.2019
comment
у вас есть ссылка на Firebase Storage? Я этого не вижу. Я добавил в свой ответ код, который использовал в своем адаптере, может быть, это вам поможет. Я получаю от адаптера ссылки на Firebase. Набор данных, который я передаю, это и ArrayList ‹› () of Users, которые в вашем случае будут блогами. - person Izzy Stannett; 21.05.2019
comment
Что я должен поставить вместо getPhotoUrl, поскольку он говорит: «Не удается разрешить метод getPhotoUrl ()»? Следует ли мне поставить вместо него getImage ()? - person Areen; 21.05.2019
comment
Эквивалент вашего блога - getImage () - person Izzy Stannett; 21.05.2019
comment
Когда я добавляю Glide.with (context) .using (new FirebaseImageLoader ()) .load (downloadRef) .into (holder.image); к моему коду он говорит, что не может разрешить метод using. - person Areen; 21.05.2019
comment
Взгляните на это, вам нужно изменить версию Glide stackoverflow.com/questions/44949432/ - person Izzy Stannett; 21.05.2019
comment
И какими должны быть значения Constants.USERS_PATH и Constants.IMAGES_PATH - person Areen; 21.05.2019
comment
Это имена папок Firebase Storge и Firebase Database, из которых вы хотите получить изображения и данные. Для меня это были пользователи и изображения, которые я сохранил как константы в другом месте. Это полностью зависит от того, как вы настроили свою базу данных / хранилище. Вы стремитесь к прямой ссылке на файлы / данные - person Izzy Stannett; 21.05.2019
comment
Изображения по-прежнему не отображаются, но в LogCat не отображается ошибка. - person Areen; 21.05.2019

Похоже, ваш список детей не работает. Вместо ChildEventListener попробуйте ValueEventListener. Узнайте больше о слушателях в документе firebase.

mDatabaseReference.addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    for (DataSnapshot data : dataSnapshot.getChildren()){
       Blog blog = dataSnapshot.getValue(Blog.class);
       blogList.add(blog);
    }
    Collections.reverse(blogList);
    blogRecyclerAdapter = new BlogRecyclerAdapter(PostListActivity.this, blogList);
    recyclerView.setAdapter(blogRecyclerAdapter);
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    System.out.println("The read failed: " + databaseError.getCode());
  }
});
person sanoJ    schedule 19.05.2019
comment
Спасибо за ответ, но когда я запускаю ваш код, приложение вылетает. - person Areen; 20.05.2019