Recyclerview Ändern von Elementen während des Bildlaufs

77

Ich habe eine RecyclerView. Jede Zeile verfügt über eine Wiedergabetaste, eine Textansicht und eine Fortschrittsleiste. Wenn Sie auf die Wiedergabetaste klicken, müssen Sie Audio von meiner SD-Karte abspielen und fortschreiten. Fortschrittsbalken Das Problem ist, wenn ich in der Recycling-Ansicht nach unten scrolle, ändern Sie den Fortschrittsbalken in der nächsten Zeile. Das bedeutet, ich kann 5 Elemente gleichzeitig auf den Bildschirm bringen. Wenn ich zur Suchleiste der 6. und 6. Zeile scrolle, ändert sich diese plötzlich.

public class ListAdapter extends RecyclerView.Adapter {

    private List<Historyitem> stethitems;
    public Context mContext;
    public Activity activity;
    public Handler mHandler;

    static MediaPlayer mPlayer;
    static Timer mTimer;

    public ListAdapter(Activity activity,Context mContext,List<Historyitem> stethitems) {
        this.stethitems = stethitems;
        this.mContext = mContext;
        this.activity = activity;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {

        View rootView = LayoutInflater.
                from(mContext).inflate(R.layout.stethoscopeadapteritem, null, false);
        RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        rootView.setLayoutParams(lp);
        mHandler = new Handler();
        return new MyViewHolder(rootView);
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, int position) {

        final Historyitem dataItem = stethitems.get(position);
        final MyViewHolder myViewHolder = (MyViewHolder) viewHolder;
        myViewHolder.progressplay.setProgress(0);
        myViewHolder.stethdatetime.setText(dataItem.getReported_Time());
        myViewHolder.stethhosname.setText(dataItem.getdiv());

        if(dataItem.getPatient_Attribute().replaceAll(" ","").equals("")){
            myViewHolder.stethdoctorname.setText(dataItem.getunit());
        } else {
            myViewHolder.stethdoctorname.setText(dataItem.getPatient_Attribute());
        }

        myViewHolder.stethstreamplay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                   FileDownload(dataItem.getmsg(),
                            myViewHolder.progressplay);

            }
        });
    }

    @Override
    public int getItemCount() {

        return stethitems.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {

        final CustomTextRegular stethdatetime;
        final CustomTextView stethhosname;
        final CustomTextBold stethdoctorname;
        final ImageButton stethstreamplay;
        final NumberProgressBar progressplay;

        public MyViewHolder(View itemView) {
            super(itemView);

            stethdatetime = (CustomTextRegular)
                    itemView.findViewById(R.id.stethdatetime);
            stethhosname = (CustomTextView)
                    itemView.findViewById(R.id.stethhosname);
            stethdoctorname = (CustomTextBold)
                    itemView.findViewById(R.id.stethdoctorname);
            stethstreamplay = (ImageButton)
                    itemView.findViewById(R.id.stethstreamplay);
            progressplay= (NumberProgressBar)
                    itemView.findViewById(R.id.progressplay);


        }
    }
    public void  FileDownload(final String downloadpath,

                                     final NumberProgressBar progressplay) {

        new AsyncTask<NumberProgressBar, Integer, NumberProgressBar>() {
            NumberProgressBar progress;
            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                try {
                    if(mPlayer!=null){
                        mPlayer.stop();
                    }
                }catch (Exception e){
                }
                try {
                    if(mTimer != null){
                        mTimer.purge();
                        mTimer.cancel();
                    }
                }catch (Exception e){
                }
            }

            @Override
            protected NumberProgressBar doInBackground(NumberProgressBar... params) {
                int count;
                progress = progressplay;
                try {

                    final List<NameValuePair> list = new ArrayList<NameValuePair>();
                    list.add(new BasicNameValuePair("pid",id));

                    URL url = new URL(Config.requestfiledownload + "?path=" +
                            downloadpath);
                    URLConnection connection = url.openConnection();
                    connection.connect();

                    int lenghtOfFile = connection.getContentLength();
                    // download the file
                    InputStream input = new BufferedInputStream(url.openStream());
                    OutputStream output = new FileOutputStream(Environment.getExternalStorageDirectory() +
                            "record.wav");
                    byte data[] = new byte[1024];
                    long total = 0;
                    while ((count = input.read(data)) != -1) {
                        total += count;
                        // publishing the progress....
                        publishProgress((int) (total * 100 / lenghtOfFile));
                        output.write(data, 0, count);
                    }
                    output.flush();
                    output.close();
                    input.close();
                } catch (Exception e) {

                }
                return progress;
            }
            @Override
            protected void onPostExecute(final NumberProgressBar numberProgressBar) {
                super.onPostExecute(numberProgressBar);

                        try {
                            StartMediaPlayer(numberProgressBar);
                        } catch (Exception e){
                            e.printStackTrace();
                        }

            }
        }.execute();
    }

    public void StartMediaPlayer(final NumberProgressBar progressbar){
        Uri playuri = Uri.parse("file:///sdcard/record.wav");
        mPlayer = new MediaPlayer();
        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mPlayer.reset();
        try {
            mPlayer.setDataSource(mContext, playuri);
        } catch (IllegalArgumentException e) {

        } catch (SecurityException e) {

        } catch (IllegalStateException e) {

        } catch (Exception e) {

        }
        try {
            mPlayer.prepare();
        } catch (Exception e) {

        }

        mPlayer.start();
        progressbar.setMax(mPlayer.getDuration());
        mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                if(mPlayer!=null) {
                    mPlayer.release();
                    progressbar.setProgress(0);
                }
                if(mTimer != null){
                    mTimer.purge();
                    mTimer.cancel();
                }
            }
        });
        mTimer = new Timer();
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        progressbar.setProgress(mPlayer.getCurrentPosition());
                    }
                });
            }
        },0,500);
    }}
Arya
quelle
Da meine Antwort vielen Menschen hilft, können Sie bitte meine Antwort akzeptieren. Danke
Surendar D

Antworten:

230

Bitte versuchen Sie dies

  1. Wenn Sie verwenden, ListViewüberschreiben Sie die folgenden Methoden.

     @Override
     public int getViewTypeCount() {    
         return getCount();
     }
    
     @Override
     public int getItemViewType(int position) {    
         return position;
     }
    
  2. Wenn Sie die Methode " RecyclerViewNur überschreiben" getItemViewType()verwenden.

     @Override
     public int getItemViewType(int position) {    
         return position;
     }
    
Surendar D.
quelle
5
tolle Lösung .. rettete meinen Tag
Däñish Shärmà
1
beste Antwort bro.really hilft
Nitheesh KP
2
OMG es funktioniert perfekt. :) Diese Antwort muss akzeptiert werden
Ajay Shrestha
4
Warum getItemViewTypekommt zurück position? Neugierig auf diese Antwort.
Jimit Patel
10
Dieser Ansatz kann sich auf die Leistung auswirken, insbesondere bei einer großen Liste von Elementen: Ein großer Vorteil von RecyclerView besteht darin, dass ViewHolder-Instanzen und die entsprechende Ansichtshierarchie wiederverwendet werden. Wenn der Benutzer einen Bildlauf durchführt und neue Elemente sichtbar werden, anstatt neue Ansichten zu erstellen, werden frühere ViewHolders, die nicht mehr sichtbar sind, recycelt und an neu sichtbare Elemente gebunden. Durch die Rückgabe der Position als Typ behandelt RecyclerView jeden ViewHolder als nicht wiederverwendbar für ein neues Element, da er einen anderen Typ hat. Daher wird die "Recycling-Funktion" von RecyclerView auf diese Weise grundsätzlich deaktiviert
pakat
59

Fügen Sie setHasStableIds(true);Ihren Adapterkonstruktor hinzu und überschreiben Sie diese beiden Methoden im Adapter. Es hat auch funktioniert, wenn jemand ein RecyclerViewInside a verwendet, ViewPagerdas sich auch in einem befindet NestedScrollView.

@Override
public long getItemId(int position) {
            return position;
}

@Override
public int getItemViewType(int position) {
       return position;
}
Nathan Teyou
quelle
1
Dies ist die beste Antwort für mich und meinen RecyclerView. Wenn Sie nur "getItemViewType" überschreiben, wird eine gewisse Zufälligkeit in meiner recyclerView entfernt (nur wenn Sie nach unten scrollen, auf Positionen nach oben gehen, aber nicht rückwärts arbeiten). Die Implementierung von "getItemId" überschreiben und "setHasStableIds (true)" erledigt den Job perfekt, sowohl nach unten als auch nach oben.
Alessandro Iudicone
7
Aber das Problem ist vorhanden, wenn getItemViewType verschiedene viewTypes zurückgibt, nicht nur die reguläre Position. Weiß jemand heiß, um dies zu lösen?
RadoVidjen
Vielen Dank, ich hatte einen ganzen Tag mit einem ähnlichen Problem zu kämpfen, mit Texteingaben, die Werte zufällig ändern könnten. Du bist ein Retter.
Geoff
Ich
komme
28

Wie der Name schon sagt, werden die Ansichten in a beim RecyclerViewScrollen nach unten wiederverwendet. Dies bedeutet, dass Sie den Status jedes Elements in Ihrem Hintergrundmodell, in diesem Fall ein Historyitem, beibehalten und in Ihrem wiederherstellen müssen onBindViewHolder.

1) Erstellen Sie Position, Maximum und alle anderen Variablen, die Sie zum Speichern des Status ProgressBarin Ihrem Modell benötigen .

2) Legen Sie den Status Ihrer ProgressBarDaten anhand der Daten in Ihrem Hintergrundmodell fest. Übergeben Sie beim Klicken die Position des Elements an Ihre FileDownload/ StartMediaPlayerMethoden.

public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, int position) {
    final Historyitem dataItem = stethitems.get(position);
    final MyViewHolder myViewHolder = (MyViewHolder) viewHolder;
    myViewHolder.progressplay.setMax(dataItem.getMax());
    myViewHolder.progressplay.setProgress(dataItem.getPosition());
    ...
    myViewHolder.stethstreamplay.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
               FileDownload(dataItem.getmsg(), position);
        }
    });

3) Aktualisieren Sie den Fortschrittsbalken, indem Sie das Hintergrundmodell aktualisieren und benachrichtigen, dass es geändert wurde.

stethitems.get(position).setPosition(mPlayer.getCurrentPosition());
notifyItemChanged(position);
Tachyonflux
quelle
11

Ich hatte das gleiche Problem, als ich versuchte, eine Recycling-Ansicht zu implementieren, die ein Edittex und ein Kontrollkästchen als Zeilenelemente enthält. Ich habe das Problem der Änderung des Bildlaufwerts gelöst, indem ich der Adapterklasse die folgenden zwei Zeilen hinzugefügt habe.

@Override
public long getItemId(int position) {
    return position;
}

@Override
public int getItemViewType(int position) {
    return position;
}

Ich hoffe, es wird eine mögliche Lösung sein. Vielen Dank

Shadat
quelle
3

Geben Sie recylerViewein NestedScroll Viewin Ihr XMLund fügen Sie die Eigenschaft hinzu nestedScrollingEnabled = false.

Und onBindViewHolderfügen Sie auf Ihrem Adapter diese Zeile hinzu

final MyViewHolder viewHolder = (MyViewHolder)holder;

Verwenden Sie dieses viewHolderObjekt mit Ihren Ansichten für Klickereignisse setTextoder führen Sie diese aus.

z.B viewHolder.txtSubject.setText("Example");

Indranil Chatterjee
quelle
Fügen Sie recylerView in eine NestedScroll-Ansicht in Ihrem XML ein und fügen Sie die Eigenschaft nestedScrollingEnabled = false hinzu. danke das hilft mir :)
Sachin Solanki
2

recyclerview.setItemViewCacheSize(YourList.size());

AMI Software Engineering
quelle
Normalerweise ist es kein guter Weg, Ihre Liste zwischenzuspeichern, aber heute hatte ich eine kleine recyclerView, so dass dies meinen Tag rettete. Vielen Dank
Okan Serdaroğlu
2

Wenn wir RecyclerViewElemente dynamisch ändern (dh wenn wir die Hintergrundfarbe eines bestimmten RecyclerViewElements ändern), kann dies das Erscheinungsbild der Elemente beim Scrollen auf unerwartete Weise ändern, da die Elemente RecyclerViewwiederverwendet werden.

Um jedoch zu vermeiden, dass es möglich ist, das Scrollen android.support.v4.widget.NestedScrollViewum RecyclerViewden NestedScrollViewGriff zu wickeln und den Griff zu lassen.

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

   </LinearLayout>
</android.support.v4.widget.NestedScrollView>

Und dann können Sie im Code das verschachtelte Scrollen deaktivieren, um das Scrollen RecyclerViewzu glätten, indem Sie nur das NestedScrollViewScrollen zulassen.

ViewCompat.setNestedScrollingEnabled(recyclerView, false);
haZya
quelle
0

Ich hatte das gleiche Problem beim Umgang mit vielen Daten, es funktioniert mit 5, weil es die fünf Elemente rendert, die auf dem Bildschirm sichtbar sind, aber das Prob mit mehr Elementen gibt. Die Sache ist ..

Manchmal überspringen RecyclerView und listView nur das Auffüllen von Daten. Wenn die RecyclerView-Bindungsfunktion beim Scrollen übersprungen wird, aber wenn Sie versuchen, den recyclerView-Adapter zu debuggen, funktioniert er einwandfrei, da er jedes Mal onBind aufruft. Sie können auch die offizielle Google-Entwickleransicht The World of listView anzeigen . Etwa 20 bis 30 Minuten erklären sie, dass Sie niemals davon ausgehen können, dass getView by position jedes Mal aufgerufen wird.

Also werde ich vorschlagen, zu verwenden

RecyclerView DataBinder erstellt von satorufujiwara.

oder

RecyclerView MultipleViewTypes Binder erstellt von yqritc.

Dies sind andere Ordner, die verfügbar sind, wenn Sie diese leicht zu umgehen finden.

Auf diese Weise können Sie mit MultipleView-Typen umgehen oder wenn Sie große Datenmengen verwenden. Diese Ordner können Ihnen helfen, die Dokumentation sorgfältig zu lesen, um das Problem zu beheben.

Hemant Shori
quelle
0

Warum versuchst du es nicht so?

    HashMap<String, Integer> progressHashMap = new HashMap<>();

    //...
    if(!progressHashMap.containsKey(downloadpath)){
        progressHashMap.put(downloadpath, mPlayer.getCurrentPosition());
    }

    progressbar.setProgress(progressHashMap.get(downloadpath));
Saideveloper
quelle
0

Versuche dies

@Override public void smoothScrollToPosition(RecyclerView    recyclerView, RecyclerView.State state,
       int position) {    LinearSmoothScroller linearSmoothScroller =
           new LinearSmoothScroller(recyclerView.getContext()) {
               @Override
               public PointF computeScrollVectorForPosition(int targetPosition) {
                   return LinearLayoutManager.this
                           .computeScrollVectorForPosition(targetPosition);
               }
           };    linearSmoothScroller.setTargetPosition(position);    startSmoothScroll(linearSmoothScroller); }

siehe auch das

Vaishali Sutariya
quelle
0

Diese Zeile ändert den Fortschritt bei jeder Bindung auf 0

myViewHolder.progressplay.setProgress(0);

Speichern Sie den Status irgendwo und laden Sie ihn in dieselbe Zeile.

Ali Karaca
quelle
0

Überschreiben Sie die Methode getItemViewType im Adapter. in Kotlin verwenden

override fun getItemViewType(position: Int): Int {
    return position
}
Javlon Tulkinov
quelle
0

Wenn Ihr Recyclerview ViewHolder mehr Logik oder eine andere Ansicht hat, sollten Sie Folgendes versuchen:

 **order_recyclerView.setItemViewCacheSize(x);**

Dabei ist x die Größe der Liste. Das obige funktioniert für mich, ich hoffe es funktioniert auch für Sie.

Jyotiprakash Ghorai
quelle
-2

Dies ist das erwartete Verhalten von recyclerView. Da die Ansicht recycelt wird, werden Ihre Artikel möglicherweise in zufällige Ansichten verschoben. Um dies zu überwinden, müssen Sie selbst angeben, welches Element in welche Ansicht gebracht wird. Diese Informationen können in einem SparseBooleanArray gespeichert werden. Sie können ein SparseBooleanArray in Ihrem Adapter wie folgt erstellen

SparseBooleanArray selectedItems = new SparseBooleanArray();

Wenn sich Ihre Ansicht ändert, gehen Sie wie folgt vor:

selectedItems.put(viewItemIndex,true);

Jetzt in Ihrem onBindViewHolder tun

 if(selectedItems.get(position, false)){
    //set progress bar of related to the view to desired position
    }
    else {
        //do the default
    }

Dies ist die Grundlage, um Ihr Problem zu lösen. Sie können diese Logik in recyclerView an jedes ähnliche Problem anpassen.

Rockfight
quelle
Wie man viewItemIndex der Ansicht einstellt
Arya
Das ist der Punkt in Ihrer Adapterliste. Sie können getAdapterPostion oder stethitems.get(position)wie in Ihrem Code verwenden.
Rockfight
Trotzdem stehe ich vor dem gleichen Problem. Wenn ich in der Recycling-Ansicht nach unten scrolle, ändere den Fortschrittsbalken in der nächsten Zeile.
Arya
Es geht nicht in den else-Teil ein.
Arya
ah sorry, wechsle if(selectedItems.get(position, true))zu if(selectedItems.get(position, false)). Ich werde die ursprüngliche Antwort bearbeiten.
Rockfight
-2

Ich hatte das ähnliche Problem und suchte viel nach der richtigen Antwort. Grundsätzlich handelt es sich eher um ein Design der Recycler-Ansicht, bei dem die Ansicht auf der Schriftrolle aktualisiert wird, da die Ansicht aktualisiert wird.

Alles, was Sie tun müssen, ist, zum Bindezeitpunkt anzuweisen, es nicht zu aktualisieren.

So sollte Ihr onBindViewHolder aussehen

@Override
@SuppressWarnings("unchecked")
public void onBindViewHolder(final BaseViewHolder holder, final int position) {
    holder.bind(mList.get(position));

    // This is the mighty fix of the issue i was having
    // where recycler view was updating the items on scroll.
    holder.setIsRecyclable(false);
}
Jay Schneider
quelle
1
Es macht den gesamten Zweck der Verwendung der "RECYCLER" -Ansicht zunichte
Alex