2015-09-12 24 views
5

私の貧しい私の英語のため申し訳ありません。RecyclerViewのスクロール正確な位置を保存して復元する方法は?

正確な位置が欲しいです。そしてまた、それは破壊されているよう同じ位置としてRecyclerViewができ、は(私はそれを破壊したり、システムがそれを破壊に関係なく)を破壊されている活動/フラグメントの後、私は活動/フラグメントを再度開きます。

「同じ」は、項目が最後に部分的に表示されている可能性があるため、「項目の位置」を意味しません。

正確に同じ位置に復元します。

私は以下のいくつかの方法を試しましたが、誰も完璧ではありません。

誰かが役に立ちますか?

1.最初の方法。

スクロールの正確な位置を計算するのにonScrolledを使用します。スクロールを停止して位置を保存します。 スピナーのデータセットまたはのフラグメントonCreatを変更する場合は、選択したデータセットの位置を復元します。多くの行が多数ある場合があります。

アプリが破棄された後に保存して復元することはできますが、多すぎるかもしれません。

それは

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は、フラグメントオープン、RecyclerViewは、最初のリストは、最初の項目から始まり、いくつかの遅延の後に、それがスクロール表示されます111111、のような、巨大である場合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方法。

これは完全に実行されますが、フラグメントライフサイクルでのみ機能します。

ObjectInputStreamを使用して、HashMapを保存して復元しようとしましたが、機能しません。

メモリに保存するにはどうすればよいですか?

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

私が代わりにHashMapBundleを使用し、第二の方法を変更するが、それは同じ質問を持っているが、フラグメントのライフサイクルのうちのときは動作しません。

私はを使用してメモリにbundleを保存しますが、復元すると有効なbundleが得られません。

============================================== ========================= ソリューション

みんなありがとう!

最後に、私はこの問題を解決するため、あなただけのこれらの2つの方法を参照してください、my codeでそれを見ることができます。saveRecyclerViewPositionrestoreWordRecyclerViewPosition

+0

おそらく、最初に表示されたアイテムIDとそのオフセットをlayoutmanagerから取得するだけで、再作成時にその位置に移動し、スクロールをオフセットだけ調整することができます。その間にデータセットが変更された場合は、これもおそらく動作します。 –

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

あなたがしたいのは、最初に表示されるアイテムとその位置を取得することです。 IDを固定している場合、IDのID /位置を調べるロジックを追加できます。

いくつかの基本的なアダプター:

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を使用して、起動時に再びそれを読んで、好きな状態だセーブ。私はpostをスクロールしています。スクロールするとうまくいきません。基本的な例ですが、もう少しLayoutManagerを使いこなす方が良い方法があります。これが何をするか

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

yess !!優れた – SergioLucas

関連する問題