【问题标题】:Android, RecyclerView: Correct view is being loaded but inflated at wrong positionAndroid,RecyclerView:正在加载正确的视图,但在错误的位置充气
【发布时间】:2018-11-22 12:28:36
【问题描述】:

我正在制作一个可以在您的手机上播放音乐文件的应用。它在回收站视图中列出所有歌曲及其艺术家姓名、歌曲名称和专辑封面(如果有)。

在我尝试加载专辑封面之前一切正常。 虽然在开始时(在我滚动之前)一切看起来都很好,但经过一些滚动后,封面开始出现在它们不应该出现的地方。所以说我刚刚看过歌曲 X 的专辑封面,然后在滚动一些之后,歌曲 X 的同一封面出现在歌曲 y 旁边。 这特别奇怪,因为它确实正确加载了专辑名称和其他所有内容。它只是混淆位图。这就是我初始化回收站视图的方式:

    private async Task InitRecView()
    {
         await Task.Run(() =>
         {

             // Instantiate the adapter and pass in its data source:

             LinearLayout lnBg = (LinearLayout)FindViewById(Resource.Id.background_recView);

             mAdapter = new PhotoAlbumAdapter(GetSortedListWithAllSongs(), this, dbSeekObj, seekObj, mAudioManager, this, lnBg);

             // Get our RecyclerView layout:
             mRecyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView);

             // Plug the adapter into the RecyclerView:
             mRecyclerView.SetAdapter(mAdapter);

             mRecyclerView.SetItemViewCacheSize(15);
             mRecyclerView.DrawingCacheEnabled = true;
             mRecyclerView.DrawingCacheQuality = DrawingCacheQuality.High;

             mLayoutManager = new LinearLayoutManager(this);
             mRecyclerView.SetLayoutManager(mLayoutManager);
         });
    }

这是我的适配器,我在其中检索数据(问题可能出现在“SetContentAsync()”中

    public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
    {
        View itemView = LayoutInflater.From(parent.Context).
                    Inflate(Resource.Layout.CardView, parent, false);
        PhotoViewHolder vh = new PhotoViewHolder(itemView, mp3Obj, act, reader, db, seekObj, audioManager, lnBg, ctx);
        return vh;
    }

    public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
    {
        PhotoViewHolder vh = holder as PhotoViewHolder;

        SetContent(vh, position);
    }

    private async void SetContent(PhotoViewHolder vh, int position)
    {
        await SetContentAsync(vh, position);
    }



    private async Task SetContentAsync(PhotoViewHolder vh, int position)
    {
        string SongName = "";
        string ArtistName = "";
        Bitmap bitmap = null;
        byte[] data = null;

        try
        {
            reader.SetDataSource(mp3Obj[position].Mp3Uri);
        }
        catch { }


        await Task.Run(() => // cause problems with the reload
        {
            SongName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyTitle);
            ArtistName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyArtist);

            data = reader.GetEmbeddedPicture();
            if (data != null)
            {
                bitmap = BitmapFactory.DecodeByteArray(data, 0, data.Length);

            }
        });

        ((Activity)ctx).RunOnUiThread(() =>
        {
            vh.SongName.SetTypeface(tf, TypefaceStyle.Normal);
            vh.AristName.SetTypeface(tf, TypefaceStyle.Normal);
            vh.SongName.Text = SongName;
            vh.AristName.Text = ArtistName;

            if (bitmap != null)
            {
                ConvertBitmapToBackground(bitmap, vh); // Set As Backgorund, blurry and black ( just sets the variable)
                CutImageIntoForm(bitmap, vh); // Set as the musical note button
            }

        });


    }

这是视图持有者,除了初始化视图之外什么都没有发生:

    public PhotoViewHolder(View itemView, List<MP3objectSmall> mp3Obj, Activity act, MediaMetadataRetriever reader, DataBase db, List<SeekObj> seekObj, AudioManager audioManager, LinearLayout lnBg, Context ctx) : base(itemView)
    {
        // Locate and cache view references:
        SongName = itemView.FindViewById<TextView>(Resource.Id.textView);
        AristName = itemView.FindViewById<TextView>(Resource.Id.textView2);
        lnContainer = itemView.FindViewById<LinearLayout>(Resource.Id.linlay_cardview);
        CoverArt = itemView.FindViewById<ImageButton>(Resource.Id.musical_note);

        this.mp3Obj = mp3Obj;
        this.act = act;
        this.reader = reader;
        this.db = db;
        this.seekObj = seekObj;
        this.ctx = ctx;
        this.audioManager = audioManager;
        this.lnBg = lnBg;


        lnContainer.Click += delegate
        {
            int pos = AdapterPosition;
            ClickEvent(pos, AristName.Text, SongName.Text, CoverArt, lnBg);
        };
    }

如果有人可以在这里帮助我。为什么我的回收站视图会混淆位图并将它们放入错误的布局和/或将它们加倍?

谢谢!

编辑:

我注意到相同项目的再次出现总是在相同的时间间隔内发生。对我来说,每 24 个项目包含该项目之前第 24 个项目位置的图像。另外我读到这是因为回收者视图是回收者所说的视图,但我不明白如何找出一个项目是否被回收,什么时候,只需使用新数据......

【问题讨论】:

    标签: c# android xamarin android-recyclerview


    【解决方案1】:

    由于您使用异步方法,因此它显示旧数据也就不足为奇了。在viewHolder中创建clear方法并在onViewRecycledMethod中调用:

    public class TextRecyclerAdapter extends RecyclerView.Adapter<TextRecyclerAdapter.TextViewHolder> {
        ...        
        @Override
        public void onViewRecycled(@NonNull TextViewHolder holder) {
            //clear your view here
            holder.clear();
        }
        ....
    
        public static class TextViewHolder extends RecyclerView.ViewHolder {    
            ...
            public void clear(){
                //clear your view here
                _textView.setText("");
            }
    
            private TextView _textView;
            ...    
        }
    

    更新 这也可能发生,因为来自异步调用的旧数据比新数据来得晚。您需要使用取消令牌取消旧请求。

    【讨论】:

    • 您好,非常感谢。我找到了覆盖方法 OnViewRecycled,但是,持有者似乎没有“清除”属性......你会碰巧知道 C# 中的等价物吗?
    • @InnomotionMedia 当然没有它,你必须自己实现它,因为持有者是特定于视图的。
    • @InnomotionMedia 我在回答中添加了一个更重要的事实。
    • @InnomotionMedia 简而言之,您需要创建 CancellationToken 并且在为您的 UI 设置值之前,您应该检查它是否被取消并且不要将更改应用于您的视图。
    猜你喜欢
    • 2013-08-23
    • 2013-02-27
    • 1970-01-01
    • 1970-01-01
    • 2017-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多