2015-09-12 19 views
5

Xin lỗi vì tiếng Anh nghèo nàn của tôi.Làm cách nào để lưu và khôi phục vị trí chính xác của cuộn RecyclerView?

Tôi muốn vị trí chính xác. Và sau khi hoạt động/mảnh đã phá hủy (dù tôi tiêu diệt nó hoặc hệ thống phá hủy nó), tôi mở lại hoạt động/mảnh, các RecyclerView thể cũng như các vị trí tương đương vì nó bị phá hủy.

"cùng" không có nghĩa là "vị trí mục", vì có thể mục chỉ hiển thị một phần thời gian qua.

Tôi muốn khôi phục chính xác cùng một vị trí.

Tôi đã thử một số cách dưới đây, nhưng không ai hoàn hảo.

Ai đó có thể trợ giúp?

1.Đầu tiên.

Tôi sử dụng onScrolled để tính toán vị trí chính xác của cuộn, khi di chuyển dừng, lưu vị trí. Khi spinner thay đổi tập dữ liệu hoặc fragment onCreat, khôi phục vị trí của tập dữ liệu đã chọn. Một số bộ dữ liệu có thể có nhiều hàng.

Nó có thể lưu và khôi phục sau khi ứng dụng bị phá hủy, nhưng có thể có quá nhiều calculte?

Nó sẽ gây ra

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.

Và nếu scrollY là rất lớn, giống như 111111, khi mở đoạn, đầu tiên RecyclerView sẽ hiển thị danh sách bắt đầu từ mục đầu tiên, và sau khi một số chậm trễ, nó cuộn đến vị trí scrollY.

Làm thế nào để làm cho nó không chậm trễ?

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 cách.

Điều này chạy hoàn hảo, nhưng nó chỉ có thể hoạt động trong vòng đời phân đoạn.

Tôi cố gắng sử dụng ObjectOutputStreamObjectInputStream để lưu và khôi phục HashMap nhưng không hoạt động.

Cách lưu bộ nhớ trong bộ nhớ?

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 cách.

tôi thay đổi cách thứ hai, sử dụng Bundle thay vì HashMap, nhưng nó có cùng một câu hỏi, không hoạt động khi ra khỏi vòng đời mảnh.

Tôi sử dụng manually serialize/deserialize android bundle để lưu bundle trong bộ nhớ, nhưng khi khôi phục, nó không nhận được bundle hợp lệ.

============================================== ========================= giải pháp

Cảm ơn tất cả mọi người!

Cuối cùng tôi giải quyết vấn đề này, bạn có thể nhìn thấy nó trong my code, chỉ cần nhìn thấy những 2 phương pháp: saveRecyclerViewPositionrestoreWordRecyclerViewPosition.

+0

có thể chỉ nhận được id mục được hiển thị đầu tiên và giá trị bù trừ của nó từ trình bố cục, sau đó bạn có thể di chuyển đến vị trí khi tạo lại và điều chỉnh cuộn theo độ lệch. điều này có thể cũng sẽ hoạt động nếu tập dữ liệu của bạn thay đổi trong thời gian chờ đợi. –

+0

@ bleeding182 thanks.I cũng đã nghĩ theo cách này, nhưng tôi không biết làm thế nào để có được bù đắp của một mục? mỗi mục có chiều cao khác nhau – DawnYu

+0

bạn chỉ có thể xem được từ layoutmanager, sau đó đọc getTop() hoặc getDecoratedTop(), sự khác biệt giữa điều đó và recyclerview.top sẽ là bù đắp của bạn;) Và ngay cả khi bạn không nhận được bù đắp phải, bạn sẽ có ít nhất vào đúng vị trí mà không bỏ qua các khung hình –

Trả lời

4

Vì vậy, bây giờ tôi có thời gian để lãng phí;) Đây là một câu trả lời đầy đủ:

cơ bản bố trí. Có, nó có thể được tối ưu hóa. Đó không phải là vấn đề;)

<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> 

Điều bạn muốn làm là nhận được mục hiển thị đầu tiên và vị trí của nó. Nếu bạn có id cố định, bạn có thể thêm logic để tra cứu id/vị trí trong bộ điều hợp cho id.

Một số Adaptor cơ bản:

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; 
    } 
} 

Bạn tiết kiệm đó là nhà nước tuy nhiên bạn thích, tôi đã sử dụng SharedPreferences, và đọc nó một lần nữa vào đầu. Tôi postDelayed di chuyển, bởi vì nó không hoạt động nếu nó di chuyển đến vị trí ngay lập tức theo sau nó. Nó chỉ là một ví dụ cơ bản, và có lẽ là một cách tốt hơn nếu bạn chơi xung quanh với LayoutManager nhiều hơn một chút.

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(); 
    } 
} 

Điều này sẽ làm, nó sẽ hiển thị lại mục chính xác và sau 500 ms đó quay lại đúng vị trí đã lưu.

Hãy chắc chắn thêm các kiểm tra rỗng và các loại!

Hy vọng điều này sẽ hữu ích!

+0

yess !! Xuất sắc – SergioLucas

Các vấn đề liên quan