2015-09-12 8 views
5

मेरी खराब अंग्रेजी के लिए खेद है।RecyclerView की स्क्रॉल सटीक स्थिति को कैसे सहेजना और पुनर्स्थापित करना है?

मुझे सटीक स्थिति चाहिए। और गतिविधि/टुकड़ा के बाद नष्ट (कोई बात नहीं मैं इसे नष्ट या सिस्टम यह नष्ट कर देता है), मैं गतिविधि/टुकड़ा फिर से खोलना कर दिया गया है, RecyclerView भी ही स्थिति के रूप में के रूप में यह नष्ट कर दिया गया है सकते हैं।

"वही" का मतलब "आइटम स्थिति" नहीं है, क्योंकि शायद आइटम आंशिक रूप से आखिरी बार प्रदर्शित हो सकता है।

मैं वही स्थिति बहाल करना चाहता हूं।

मैंने नीचे कुछ तरीकों का प्रयास किया है, लेकिन कोई भी सही नहीं है।

कोई मदद कर सकता है?

1. पहला तरीका।

स्क्रॉल स्टॉप होने पर स्क्रॉल सटीक स्थिति की गणना करने के लिए onScrolled का उपयोग करें, स्थिति को सहेजें। जब स्पिनर डेटासेट या टुकड़े को क्रिएट पर परिवर्तित करें, चयनित डेटासेट की स्थिति को पुनर्स्थापित करें। कुछ डेटासेट में कई पंक्तियां हो सकती हैं।

ऐप नष्ट होने के बाद यह सहेज और बहाल कर सकता है, लेकिन शायद बहुत अधिक गणना हो सकती है?

यह

Skipped 60 frames! The application may be doing too much work on its main thread. attempt to finish an input event but the input event receiver has already been disposed. android total arena pages for jit.

कारण होगा और scrollY विशाल है अगर 111111, जब खुले टुकड़ा, RecyclerView पहले दिखाएगा सूची पहले आइटम से शुरू करते हैं, और कुछ देर के बाद, यह स्क्रॉल की तरह, scrollY स्थिति पर।

इसे कोई देरी कैसे नहीं करें?

recyclerView.addOnScrollListener(new OnScrollListener() { 
     @Override 
     public void onScrolled(RecyclerView recyclerView, int dx, final int dy) { 
      super.onScrolled(recyclerView, dx, dy); 
      handler.post(new Runnable() { 

       @Override 
       public void run() { 
        if (isTableSwitched) { 
         scrollY = 0; 
         isTableSwitched = false; 
        } 
        scrollY = scrollY + dy; 
        Log.i("dy——scrollY——table", String.valueOf(dy) + "——" + String.valueOf(scrollY) + tableName); 
       } 
      }); 
     } 


     @Override 
     public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 
      super.onScrollStateChanged(recyclerView, newState); 

      switch (newState) { 
      case RecyclerView.SCROLL_STATE_IDLE: 
       saveRecyclerPosition(tableName, scrollY); 
       break; 
      case RecyclerView.SCROLL_STATE_DRAGGING: 
       break; 
      case RecyclerView.SCROLL_STATE_SETTLING: 
       break; 
     } 
     } 

    }); 

public void saveRecyclerPosition(String tableName, int y) { 
    prefEditorSettings.putInt(KEY_SCROLL_Y + tableName, y); 
    prefEditorSettings.commit(); 
} 

public void restoreRecyclerViewState(final String tableName) { 
    handler.post(new Runnable() { 

     @Override 
     public void run() { 
      recyclerView.scrollBy(0, prefSettings.getInt(KEY_SCROLL_Y + tableName, 0)); 
     } 
    }); 
} 


//use Spinner to choose dataset 
spnWordbook.setOnItemSelectedListener(new OnItemSelectedListener() { 

     @Override 
     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 
      isTableSwitched = true; 
      tableName= parent.getItemAtPosition(position).toString(); 
      prefEditorSettings.putString(KEY_SPINNER_SELECTED_TABLENAME, tableName); 
      prefEditorSettings.commit(); 
      wordsList = WordsManager.getWordsList(tableName); 
      wordCardAdapter.updateList(wordsList); 

      restoreRecyclerViewState(tableName); 
     } 

     @Override 
     public void onNothingSelected(AdapterView<?> parent) { 
     } 
    }); 

2.Second रास्ता।

यह पूरी तरह से चलता है, लेकिन यह केवल खंड जीवन चक्र में ही काम कर सकता है।

मैं बचाने के लिए और HashMap बहाल करने के लिए ObjectOutputStream और ObjectInputStream इस्तेमाल करने की कोशिश की, लेकिन यह काम नहीं करता।

इसे स्मृति में कैसे सहेजना है?

private HashMap <String, Parcelable> hmRecyclerViewState = new HashMap<String, Parcelable>(); 

public void saveRecyclerPosition(String tableName) {  
    recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState(); 
    hmRecyclerViewState.put(tableName, recyclerViewState);  
} 

public void restoreRecyclerViewState(final String tableName) { 
    if (hmRecyclerViewState.get(tableName) != null) { 
     recyclerViewState = hmRecyclerViewState.get(tableName); 
     recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState); 
     recyclerViewState = null; 
} 

3.Third रास्ता।

मैं दूसरी तरह से बदलने के लिए, HashMap के बजाय Bundle उपयोग करें, लेकिन यह एक ही सवाल है, जब टुकड़ा जीवन चक्र से बाहर काम नहीं करता।

मैं manually serialize/deserialize android bundle का उपयोग स्मृति में bundle को बचाने के लिए, लेकिन जब यह बहाल, यह मान्य bundle नहीं मिलता है।

============================================== ========================= समाधान

सभी को धन्यवाद!

अंत में मैं इस समस्या का समाधान है, आप इसे my code में देख सकते हैं, बस इन 2 तरीकों देखें: saveRecyclerViewPosition और restoreWordRecyclerViewPosition

+0

शायद लेआउटमेनगर से पहले प्रदर्शित आइटम आईडी और उसका ऑफसेट प्राप्त करें, फिर आप ऑफ़सेट द्वारा स्क्रॉल को फिर से बनाने और समायोजित करने की स्थिति में स्थानांतरित कर सकते हैं। यदि संभवतः आपका डेटासेट बदल गया तो यह शायद भी काम करेगा। –

+0

@ bleeding182 धन्यवाद। मैंने इस तरह से सोचा है, लेकिन मुझे नहीं पता कि किसी आइटम का ऑफ़सेट कैसे प्राप्त किया जाए? प्रत्येक आइटम की अलग-अलग ऊंचाई – DawnYu

+0

है, आप लेआउटमैनेजर से केवल दृश्य प्राप्त कर सकते हैं, फिर getTop() या getDecoratedTop() प्राप्त करें, उस और recyclerview.top के बीच का अंतर आपका ऑफसेट होगा;) और यहां तक ​​कि अगर आप ऑफसेट नहीं प्राप्त करते हैं ठीक है, आप फ्रेम को छोड़ने के बिना कम से कम सही स्थिति पर होंगे –

उत्तर

4

तो अब मैं समय बर्बाद करना पड़ा;) यहाँ एक पूर्ण जवाब है:

बेसिक लेआउट। हां इसे अनुकूलित किया जा सकता है। यह बात नहीं है;)

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical" 
    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin" 
    tools:context=".MainActivity"> 

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

आप जो करना चाहते हैं वह पहले दृश्यमान आइटम और इसकी स्थिति प्राप्त करना है। यदि आपने आईडी तय की हैं तो आप आईडी के लिए एडाप्टर में आईडी/स्थिति देखने के लिए तर्क जोड़ सकते हैं।

कुछ बुनियादी एडाप्टर:

public class TestAdapter extends RecyclerView.Adapter { 
    @Override 
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
     return new RecyclerView.ViewHolder(LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false)) { 
     }; 
    } 

    @Override 
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 
     ((TextView) holder.itemView).setText("Position " + position); 
    } 

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

आप को बचाने के लिए यह राज्य है जैसे आप चाहें, मैं SharedPreferences इस्तेमाल किया, और प्रारंभ पर इसे फिर से पढ़ें। मैंने पोस्ट को स्क्रॉल किया, क्योंकि यह काम नहीं करता है अगर यह स्थिति में स्क्रॉल करता है तो तुरंत इसके बाद होता है। यह सिर्फ एक बुनियादी उदाहरण है, और यदि आप लेआउटमैनेजर के साथ थोड़ा और खेलते हैं तो शायद एक बेहतर तरीका है।

import android.content.SharedPreferences; 
import android.os.Bundle; 
import android.os.Handler; 
import android.preference.PreferenceManager; 
import android.support.v7.app.AppCompatActivity; 
import android.support.v7.widget.LinearLayoutManager; 
import android.support.v7.widget.RecyclerView; 
import android.util.Log; 
import android.view.View; 

public class MainActivity extends AppCompatActivity { 

    private static final String TAG = "MainActivity"; 
    private RecyclerView recyclerView; 

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

     recyclerView = (RecyclerView) findViewById(R.id.recycler); 

     recyclerView.setLayoutManager(new LinearLayoutManager(this)); 
     recyclerView.setAdapter(new TestAdapter()); 

    } 

    @Override 
    protected void onResume() { 
     super.onResume(); 
     final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); 
     recyclerView.scrollToPosition(preferences.getInt("position", 0)); 
     new Handler().postDelayed(new Runnable() { 
      @Override 
      public void run() { 
       recyclerView.scrollBy(0, - preferences.getInt("offset", 0)); 
      } 
     }, 500); 
    } 

    @Override 
    protected void onPause() { 
     super.onPause(); 
     SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); 

     View firstChild = recyclerView.getChildAt(0); 
     int firstVisiblePosition = recyclerView.getChildAdapterPosition(firstChild); 
     int offset = firstChild.getTop(); 

     Log.d(TAG, "Postition: " + firstVisiblePosition); 
     Log.d(TAG, "Offset: " + offset); 

     preferences.edit() 
       .putInt("position", firstVisiblePosition) 
       .putInt("offset", offset) 
       .apply(); 
    } 
} 

इस क्या करेंगे, यह फिर से सही मद प्रदर्शित करेगा, और उन 500 एमएस के बाद सही स्थिति यह बचा लिया गया था वापस करने के लिए कूद।

शून्य चेक और प्रकार जोड़ना सुनिश्चित करें!

आशा है कि इससे मदद मिलती है!

+0

येस !! अति उत्कृष्ट – SergioLucas

संबंधित मुद्दे