Firebase(Android):我的listView上没有显示图像,一旦我第一次将它上传到存储

Posted

技术标签:

【中文标题】Firebase(Android):我的listView上没有显示图像,一旦我第一次将它上传到存储【英文标题】:Firebase (Android): image not being displayed on my listView, once I first upload it to Storage 【发布时间】:2020-04-18 16:33:02 【问题描述】:

我正在 android 中实现一个即时消息应用(顺便说一下,FriendlyChat 是 Firebase Udacity 课程的一部分)。

它会保存并显示每条消息的书面消息或图像。这些已保存项目的类称为 FriendlyMessage,其中包含消息的作者,然后是文本或图像。

我在 onAuthStateChanged 中设置了 ChildEventListener,它是在 onCreate 上编码的。话虽如此,AuthListener 仅在 onResume(将其附加/添加到 DatabaseReference)和 onPause(分离/删除)时调用。

发送按钮的 onClickListener 也在 onCreate 中设置。将 EditText 中的文本传递给 String,创建 FriendlyMessage 实例,然后通过 .push().setValue(object) 方法保存到数据库中。

每当我这样做时,都会调用 onChildAdded,我可以在其中将最新的 FriendlyMessage 实例添加到适配器,并将写入的消息显示在 ListView 中。

对于图像,我有一个打开图像文件的按钮,然后我选择一个,然后被调用到 onActivityResult。

在这里,我可以检索图像,将其存储在 FirebaseStorage 中,甚至将其保存到数据库中(也可以使用 push().setValue(object) 方法)。

然而,在我编写的一些日志的帮助下,我注意到 onChildAdded 没有被调用,因此,也是因为该应用具有可能同时被暂停,我的 ListView 保持空白(ListView 在 onPause 时被清除)。当我旋转手机或重启应用程序时,会出现所有内容,包括上传的图像。当然,正如您所知,在用户上传图片后立即显示图片非常重要。

我做错了什么?或者我该怎么做才能在图像 FriendlyMessage 实例保存到数据库时调用 onChildAdded(知道现在正在 onActivityResult 上调用它)?

MainActivity.java:

package com.google.firebase.udacity.friendlychat;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.fragment.app.Fragment;

import com.firebase.ui.auth.AuthUI;
import com.google.android.gms.tasks.Continuation;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity 

    private static final String TAG = "MainActivity";

    public static final String ANONYMOUS = "anonymous";
    public static final int DEFAULT_MSG_LENGTH_LIMIT = 1000;
    public static final int RC_SIGN_IN = 1;
    public static final int RC_PHOTO_PICKER = 2;

    private ListView mMessageListView;
    private MessageAdapter mMessageAdapter;
    private ProgressBar mProgressBar;
    private ImageButton mPhotoPickerButton;
    private EditText mMessageEditText;
    private Button mSendButton;
    private TextView mDisplayNameTextView;

    private String mUsername;

    private int countChildEventCalls; //for testing ChildEventListener calls

    //Firebase variables
    private FirebaseDatabase mFirebaseDatabase;
    private DatabaseReference mDatabaseReference;
    private FirebaseAuth mFirebaseAuth;
    private FirebaseAuth.AuthStateListener mAuthListener;
    private ChildEventListener mChildEventListener;
    private FirebaseStorage mFirebaseStorage;
    private StorageReference mChatPhotosStorageReference;

    private List<AuthUI.IdpConfig> mAuthProviders = Arrays.asList(
            new AuthUI.IdpConfig.EmailBuilder().build(),
            new AuthUI.IdpConfig.GoogleBuilder().build());

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

        mUsername = ANONYMOUS;
        countChildEventCalls = 0;

        mFirebaseDatabase = FirebaseDatabase.getInstance();
        mFirebaseStorage = FirebaseStorage.getInstance();
        mFirebaseAuth = FirebaseAuth.getInstance();

        mDatabaseReference = mFirebaseDatabase.getReference().child("messages");
        mChatPhotosStorageReference = mFirebaseStorage.getReference().child("chat_photos");

        // Initialize references to views
        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
        mMessageListView = (ListView) findViewById(R.id.messageListView);
        mPhotoPickerButton = (ImageButton) findViewById(R.id.photoPickerButton);
        mMessageEditText = (EditText) findViewById(R.id.messageEditText);
        mSendButton = (Button) findViewById(R.id.sendButton);
        mDisplayNameTextView = (TextView) findViewById(R.id.display_name);

        // Initialize message ListView and its adapter
        List<FriendlyMessage> friendlyMessages = new ArrayList<>();
        mMessageAdapter = new MessageAdapter(this, R.layout.item_message, friendlyMessages);
        mMessageListView.setAdapter(mMessageAdapter);

        // Initialize progress bar
        mProgressBar.setVisibility(ProgressBar.INVISIBLE);

        // Enable Send button when there's text to send
        mMessageEditText.addTextChangedListener(new TextWatcher() 
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) 
            

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) 
                if (charSequence.toString().trim().length() > 0) 
                    mSendButton.setEnabled(true);
                 else 
                    mSendButton.setEnabled(false);
                
            

            @Override
            public void afterTextChanged(Editable editable) 
            
        );

        mMessageEditText.setFilters(new InputFilter[]new InputFilter.LengthFilter(DEFAULT_MSG_LENGTH_LIMIT));

        // Send button sends a message and clears the EditText
        mSendButton.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                // TODO: Send messages on click
                FriendlyMessage friendlyMessage = new FriendlyMessage(mMessageEditText.getText().toString(), mUsername, null);

                mDatabaseReference.push().setValue(friendlyMessage);

                // Clear input box
                mMessageEditText.setText("");
            
        );

        // ImagePickerButton shows an image picker to upload a image for a message
        mPhotoPickerButton.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.setType("image/jpeg");
                intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
                startActivityForResult(Intent.createChooser(intent, "Complete action using"), RC_PHOTO_PICKER);
              
        );

        mAuthListener = new FirebaseAuth.AuthStateListener() 

            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) 
                FirebaseUser user = firebaseAuth.getCurrentUser();

                if(user != null)  //the user is logged in
                    onSignedInInitialize(user.getDisplayName());
                

                else  //the user isn't logged in yet. Gotta launch FirebaseAuthUI
                    onSignedOutCleanup();
                    startActivityForResult(AuthUI.getInstance()
                        .createSignInIntentBuilder()
                        .setIsSmartLockEnabled(false)
                        .setAvailableProviders(mAuthProviders)
                        .build(),
                            RC_SIGN_IN);
                
            
        ;
    

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        return true;
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        switch(item.getItemId())
            case R.id.sign_out_menu:
                AuthUI.getInstance().signOut(this);
                return true;

            default: return super.onOptionsItemSelected(item);
        
    

    @Override
    protected void onResume()
        super.onResume();
        mFirebaseAuth.addAuthStateListener(mAuthListener);
    

    @Override
    protected void onPause()
        super.onPause();
        if(mAuthListener != null) 
            mFirebaseAuth.removeAuthStateListener(mAuthListener);
        
        detachDatabaseReadListener();
        mMessageAdapter.clear();
    

    public void onSignedInInitialize(String displayName)
        mUsername = displayName;
        mDisplayNameTextView.setText(mUsername);

        if(mChildEventListener == null)
            attachDatabaseReadListener();
        
    

    public void attachDatabaseReadListener()
        mChildEventListener = new ChildEventListener() 

            @Override
            public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) 
                countChildEventCalls++;
                Log.v(TAG, "Could be Text or Photos: calling ChildEventListener");
                FriendlyMessage message = dataSnapshot.getValue(FriendlyMessage.class);
                mMessageAdapter.add(message);
                Log.v(TAG, "Text or Photos: message added to Adapter. Calls: " + countChildEventCalls);
            

            @Override
            public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) 
                Log.v(TAG, "Text or Photos: calling onChildChange!");
            

            @Override
            public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) 
            

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

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) 
                Log.v(TAG, "Text or Photos: onCancelled called.");
            
        ;

        mDatabaseReference.addChildEventListener(mChildEventListener);
    

    public void onSignedOutCleanup()
        mUsername = ANONYMOUS;
        mMessageAdapter.clear();
        detachDatabaseReadListener();
    

    public void detachDatabaseReadListener()
        if(mChildEventListener != null)
            mDatabaseReference.removeEventListener(mChildEventListener);
        
    

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data)
        super.onActivityResult(requestCode, resultCode, data);

        Log.v(TAG, "Photos: Started activitiyForResult");
        Log.v(TAG, "for Photos purpose, requestCode = " + requestCode + ", resultCode = " + resultCode);

        if(requestCode == RC_SIGN_IN)
            if(resultCode == RESULT_OK)
                Toast.makeText(MainActivity.this, "You're logged in! Type with your friends", Toast.LENGTH_LONG).show();
            
            else if (resultCode == RESULT_CANCELED)
                Toast.makeText(MainActivity.this, "Sign-in cancelled", Toast.LENGTH_LONG).show();
                finish();
            
        
        else if (requestCode == RC_PHOTO_PICKER && resultCode == RESULT_OK) 
            uploadImageToChat(data);
        

    

    public void uploadImageToChat(Intent dataIntent)
        Log.v(TAG, "Photos: Reached photo picker RC request");
        Uri imageUri = dataIntent.getData();
        final StorageReference photoRef = mChatPhotosStorageReference.child(imageUri.getLastPathSegment());
        UploadTask uploadTask = photoRef.putFile(imageUri);

        uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() 
            @Override
            public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) 
                taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() 
                    @Override
                    public void onSuccess(Uri uri) 
                        String stringUrl = uri.toString();
                        Log.v(TAG, "Photos, uploaded, path: " + stringUrl);

                        FriendlyMessage imageMessage = new FriendlyMessage(null, mUsername, stringUrl);
                        mDatabaseReference.push().setValue(imageMessage);
                        Log.v(TAG, "Photos: Added Message to database");
                    
                );

            
        );
        //mMessageAdapter.notifyDataSetChanged();
        /*
            Task<Uri> taskUri = uploadTask.continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() 
                @Override
                public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception 
                    if (!task.isSuccessful()) 
                        throw task.getException();
                    

                    // Continue with the task to get the download URL
                    return photoRef.getDownloadUrl();
                
            ).addOnCompleteListener(new OnCompleteListener<Uri>() 
                @Override
                public void onComplete(@NonNull Task<Uri> task) 
                    if (task.isSuccessful()) 
                        Log.v(TAG, "Photos: upload successful");
                        Uri downloadUri = task.getResult();
                        String stringUrl = task.toString();
                        FriendlyMessage imageMessage = new FriendlyMessage(null, mUsername, stringUrl);
                     else 
                        // Handle failures
                        // ...
                        Log.v(TAG, "Photos: wasn't able to upload photo");
                    
                
            ); */


    


MessageAdapter.java:

package com.google.firebase.udacity.friendlychat;

import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;

import java.util.List;

public class MessageAdapter extends ArrayAdapter<FriendlyMessage> 
    public MessageAdapter(Context context, int resource, List<FriendlyMessage> objects) 
        super(context, resource, objects);
    

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
        if (convertView == null) 
            convertView = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.item_message, parent, false);
        

        ImageView photoImageView = (ImageView) convertView.findViewById(R.id.photoImageView);
        TextView messageTextView = (TextView) convertView.findViewById(R.id.messageTextView);
        TextView authorTextView = (TextView) convertView.findViewById(R.id.nameTextView);

        FriendlyMessage message = getItem(position);

        boolean isPhoto = message.getPhotoUrl() != null;
        if (isPhoto) 
            messageTextView.setVisibility(View.GONE);
            photoImageView.setVisibility(View.VISIBLE);
            Glide.with(photoImageView.getContext())
                    .load(message.getPhotoUrl())
                    .into(photoImageView);
         else 
            messageTextView.setVisibility(View.VISIBLE);
            photoImageView.setVisibility(View.GONE);
            messageTextView.setText(message.getText());
        
        authorTextView.setText(message.getName());

        return convertView;
    

【问题讨论】:

【参考方案1】:

与此同时,我想我找到了解决方案。

在我的 onSignedInInitialize 方法中,我曾声明如果 ChildEventListener 为空,那么我才会将 ChildEventListener 添加到数据库引用中。由于我在 onPause 上删除了此侦听器,并且当我返回 mainActivity(onActivityResult 上)时它可能不为空,我相信我没有将事件侦听器附加到数据库引用,因此之后没有显示任何内容正在上传的图片。

所以我删除了提到的条件,并在我回到 onResume 后立即将 ChildEventListener 实例添加到数据库引用中。它奏效了。

谢谢你,仍然! 最好的问候,

【讨论】:

尽管如此,请随时告诉我有哪些最佳实践。非常感谢;)

以上是关于Firebase(Android):我的listView上没有显示图像,一旦我第一次将它上传到存储的主要内容,如果未能解决你的问题,请参考以下文章

不兼容的类型 - Android Gridview

firebase 社交登录不适用于我的 android 设备,仅适用于网络

android firebase通知不起作用

我正在开发一个 Android 应用程序并使用 Firebase 作为我的后端

从ArrayAdapter每毫秒更新Android ListView中的一行

无法在 Android 中使用 Firebase 应用内消息?