如何创建使用Firebase中的图像的RecyclerView

我在Firebase存储器上名为Icons的文件夹中有超过1.5K张图像,我想将这些图像加载到使用GridLayoutManager的RecyclerView中。显然,我只希望仅在Recyclerview中可见这些图像时才请求加载这些图像,否则这会浪费对存储的读取权限?

因此,我首先创建R.layout.card_icons_rcv_item xml布局-在ConstraintLayout内部包含一个ImageView,以表示RecyclerView中的每个单元。

然后我创建了Adapter类,您可以在onBindViewHolder的下面和内部看到它,我试图加载与各自RecyclerView单元中的每个单元相关联的图像,但是效果不佳。

首先,我仅获得约2张图像,或仅加载了2张图像(大部分时间),没有其他任何东西,其次,我可以看到Log.d("Adapter","Error loading card icon from firestore for card #" + position);语句被调用了最后3个单元格,最后我在logcat中看到,即使recyclerView中没有图像,所有对firebase的所有请求都同时发出。

更新

reycler视图似乎成功加载了图标,但是非常非常缓慢。所以我在文档中做了一些挖掘,偶然发现了位图。文档说,由于我的图像视图比Firebase存储上的图像小,因此我可能应该对它们进行下采样。正如您在更新的onBindViewHolder方法中看到的那样,我正是这样做的。图像加载速度明显加快,但仍然不够快。我还遇到了一个我似乎无法解决的怪异异常。我在下面更新我的问题:

[更新]所以这是我的问题:

  1. 我如何仅请求从Firebase加载仅可见单元的图像,因为从我现在在Logcat中看到的情况来看,发出的请求等于getItemCount()返回的数字。
  2. li>
  3. getItemCount()方法中,我返回一个随机数50,因为我不知道如何返回数据库中的图像数。那里至少有1.5K图像,数据库将逐渐变大。这会引起任何问题吗?如果是的话,我该如何克服呢?
  4. 如何使这些图像的下载速度更快?
  5. 在将图像下载到onBindViewHolder方法中看到的临时文件中之后,我应该删除它们还是填充回收者视图的图像视图所需?
  6. 运行几次后,未对我的代码进行任何更改,我开始在Logcat中收到以下错误:
  

E / AuthUI:发生登录错误。       com.firebase.ui.auth.FirebaseUiException:保存凭据时出错。           在com.firebase.ui.auth.viewmodel.smartlock.SmartLockHandler $ 1.onComplete(SmartLockHandler.java:99)           在com.google.android.gms.tasks.zzj.run(未知来源:4)           在android.os.Handler.handleCallback(Handler.java:883)           在android.os.Handler.dispatchMessage(Handler.java:100)           在android.os.Looper.loop(Looper.java:221)           在android.app.activityThread.main(activityThread.java:7520)           在java.lang.reflect.Method.invoke(本机方法)           在com.android.internal.os.RuntimeInit $ MethodAndArgsCaller.run(RuntimeInit.java:539)           在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)        引起原因:com.google.android.gms.common.api.ApiException:16:当前应用程序的保存提示被禁用。要还原,请删除   密码智能锁中“从不保存”列表中的此应用   此设备上所有帐户的设置。           在com.google.android.gms.common.internal.ApiExceptionUtil.fromStatus(com.google.android.gms:play-services-base @@ 17.1.0:4)           在com.google.android.gms.common.internal.zai.zaf(com.google.android.gms:play-services-base @@ 17.1.0:2)           在com.google.android.gms.common.internal.zak.onComplete(com.google.android.gms:play-services-base @@ 17.1.0:6)           在com.google.android.gms.common.api.internal.BasePendingResult.zaa(com.google.android.gms:play-services-base @@ 17.1.0:176)           com.google.android.gms.common.api.internal.BasePendingResult.setResult(com.google.android.gms:play-services-base @@ 17.1.0:135)           com.google.android.gms.common.api.internal.BaseImplementation $ ApiMethodImpl.setResult(com.google.android.gms:play-services-base @@ 17.1.0:36)           在com.google.android.gms.internal.auth-api.zzo.zzc中(未知来源:4)           在com.google.android.gms.internal.auth-api.zzv.dispatchTransaction(未知   资料来源:9)           在com.google.android.gms.internal.auth-api.zzd.onTransact上(未知   资料来源:12)           在android.os.Binder.execTransactInternal(Binder.java:1021)           在android.os.Binder.execTransact(Binder.java:994)

我尝试以用户(所有者)身份登录的用户已根据我的Log.d语句成功进行了身份验证,但出于某些奇怪的原因,它不允许我从Firebase读取数据。该错误提到我应该更改Google SmartLock中的设置,但我发誓天哪,我到处都在手机上寻找该设置,但找不到。我也在Google上进行了搜索,但是没有运气。关于在哪里可以找到该设置的任何想法?

CardIconRCVAdapter.java:

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

    private LayoutInflater inflater;
    private Context mContext;
    private StorageReference storageReference;

    public CardIconsRCVAdapter(Context context,StorageReference storageRef) {
        mContext = context;
        inflater = LayoutInflater.from(context);
        storageReference = storageRef;
    }

    // stores and recycles views as they are scrolled off screen
    class ViewHolder extends RecyclerView.ViewHolder {
        ImageView iconImgView;

        ViewHolder(@NonNull View itemView) {
            super(itemView);
            iconImgView = itemView.findViewById(R.id.cardIcon);
        }
    }

    // Inflates the cell layout from xml when needed
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,int viewType) {
        View view = inflater.inflate(R.layout.card_icons_rcv_item,parent,false);
        return new ViewHolder(view);
    }

    // Binds the data to the views in each cell
    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder,final int position) {
        //Log.d("Adapter","OnBindViewHolder called for #" + position);
        StorageReference iconRefJpeg = storageReference.child("Icons/icon" + position + ".jpeg");
        final StorageReference iconRefPng = storageReference.child("Icons/icon" + position + ".png");
        try {
            final File localFile = File.createTempFile("icon" + position,".jpeg");
            iconRefJpeg.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.Tasksnapshot>() {
                @Override
                public void onSuccess(FileDownloadTask.Tasksnapshot tasksnapshot) {
                    holder.iconImgView.setImageBitmap(decodeSampledBitmapFromResource(localFile.getabsolutePath(),150,150));
                }
            }).addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    Log.d("AdapterError","Failed fetching .jpeg image. Trying to fetch .png");
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static Bitmap decodeSampledBitmapFromResource(String filePath,int reqWidth,int reqHeight) {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath,options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(filePath,options);
    }

    private static int calculateInSampleSize(BitmapFactory.Options options,int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        Log.d("CalcSampleSize","height = " + height + " width = " + width + " Image type : " + options.outMimeType);
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) >= reqHeight
                    && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }

    @Override
    public int getItemCount() {
        return 50;
    }
}

Mainactivity.kt:

class Mainactivity : AppCompatactivity() {

private val TAG: String = Mainactivity::class.java.simpleName // Tag used for debugging

// View declarations
private lateinit var iconsRCV : RecyclerView

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val storageRef = FirebaseStorage.getInstance().reference // Create a storage reference

    // Setting Toolbar default settings
    val toolbar : Toolbar = findViewById(R.id.mainToolbar)
    setSupportactionBar(toolbar) // set the custom toolbar as the support action bar
    supportactionBar?.setDisplayShowTitleEnabled(false) // remove the default action bar title

    // RecyclerView initializations
    iconsRCV = findViewById(R.id.cardIconsRCV)
    val numOfColumns = 5
    iconsRCV.layoutManager = GridLayoutManager(this,numOfColumns)
    val iconsAdapter = CardIconsRCVAdapter(this,storageRef)
    iconsRCV.adapter = iconsAdapter
}

}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/mainToolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:elevation="4dp"
        android:background="@color/colorPrimary"
        android:minHeight="?attr/actionBarSize"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/toolbar_title"
            android:text="@string/app_name"
            android:textSize="18sp"
            android:textColor="@android:color/white"
            android:layout_gravity="center"/>

    </androidx.appcompat.widget.Toolbar>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/cardIconsRCV"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginStart="8dp"
        android:layout_marginTop="64dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/mainToolbar" />
</androidx.constraintlayout.widget.ConstraintLayout>

card_icons_rcv_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/cardIcon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="cardIcon"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:srcCompat="@tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>

更新

我在下面发布我对我的应用程序中的用户进行身份验证的方式,以帮助人们理解为什么我在第五个问题中遇到了身份验证错误。这是相应的代码:

class Userauthenticationactivity : AppCompatactivity() {

    private val TAG = Userauthenticationactivity::class.java.simpleName
    private val RC_SIGN_IN: Int = 101 // A request code used to authenticate users

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Choose authentication providers
        val providers = arrayListOf(
            AuthUI.IdpConfig.EmailBuilder().build(),AuthUI.IdpConfig.GoogleBuilder().build()
        )

        // Create and launch sign-in intent
        startactivityForResult(
            AuthUI.getInstance()
                .createSignInIntentBuilder()
                .setavailableProviders(providers)
                .setIsSmartLockEnabled(true)
                .setLogo(R.drawable.applogotmp) // Set logo drawable-temp
                .build(),RC_SIGN_IN)
    }

    override fun onactivityResult(requestCode: Int,resultCode: Int,data: Intent?) {
        super.onactivityResult(requestCode,resultCode,data)

        if (requestCode == RC_SIGN_IN) {
            val response = IdpResponse.fromResultIntent(data)

            if (resultCode == activity.RESULT_OK) {
                // Successfully signed in
                // Consider passing the user object to the Mainactivity using a bundle
                val user = FirebaseAuth.getInstance().currentUser
                user?.let {
                    // Name,email address,and profile photo Url
                    val name = user.displayName
                    val email = user.email
                    Log.d(TAG,"User $user just logged in. Name $name and email $email")
                }
                startactivity(Intent(this,Mainactivity::class.java)) // User was authenticated,allow app access
                finish()
            } else { // Sign in failed
                if(response == null) { // If response is null the user canceled the sign-in flow using the back button.
                    startactivity(Intent(this,Userauthenticationactivity::class.java))// restart the activity/prompt for auth again
                    finish()
                } else {// Otherwise check response.getError().getErrorCode() and handle the error.
                    Log.d(TAG,"Error logging in user. Reason : ${response.error?.errorCode}")
                }
            }
        }
    }
}

在onactivityResult()中,如果登录成功,我只是从当前活动到我的Mainactivity(RecyclerView所在的位置)创建一个新的Intent,或者在用户登录之前重新启动当前活动。

wenm2009 回答:如何创建使用Firebase中的图像的RecyclerView

  

问题1和2

我建议您将FirebaseStorage中所有图像下载URL的列表存储在某个位置(例如FireStore)中。然后,您可以使用Android体系结构组件中的分页库逐页请求图像网址列表,并将其提交给回收商的适配器(如果您将网址存储在FireStore中,则可以使用startFrom和您查询中的endAt方法以帮助进行分页)。这也将有助于解决您不知道要加载到RecyclerView的项目数的问题。您可以阅读有关Paging Library here

的更多信息
  

问题3和4

关于将图像下载到ImageViews中,有一些不错的图像加载库,您可以将它们与各种选项一起使用,以将图像缓存到磁盘上。一些流行的示例是GlidePicasso等。这样可以节省您的时间,并有助于您更加专注于实现应用程序逻辑。

  

问题5

从FirebaseUI github自述文件

  

默认情况下,FirebaseUI使用Smart Lock for Passwords来存储用户的凭据,并在以后尝试时自动将用户登录到您的应用程序。建议使用Smart Lock提供最佳的用户体验,但是在某些情况下,您可能希望禁用Smart Lock进行测试或开发。

在您的AuthUI构建器中,您可能希望将setIsSmartLockEnabled(true)更改为setIsSmartLockEnabled(!BuildConfig.DEBUG /* credentials */,true /* hints */)或使用setIsSmartLockEnabled(false)将其完全关闭。您可以在这里FirebaseUI Android

了解更多信息
本文链接:https://www.f2er.com/3098711.html

大家都在问