2017-02-28 7 views
13

私が実装して、簡単なAPODアプリに取り組んでいる:アプリは、FirebaseFirebase Storageから画像やテキストをつかむFirebase&ChildEventListenerを使ってオリエンテーションを変更した後、RecyclerViewの位置を保持する方法は?

  • RecyclerView
  • CardView
  • Firebase
  • Picasso

それらをCardViewに表示し、 OnClickListenerをそれぞれViewに設定します。ユーザーが画像をクリックすると、新しいActivityIntentで開かれます。 2番目のActivityには、クリックされた元の画像と詳細な情報が表示されます。

GridLayoutManager(ユーザーの電話がVERTICALの場合は1列、HORIZONTALの場合は3列)を使用してこのすべてを実装しました。

私が抱えている問題は、オリエンテーションの変更でRecyclerViewの位置を保存できないようです。私は見つけることができるすべてのオプションを試しましたが、うまくいかないようです。私が思いつくことができる唯一の結論は、ローテーション時に、メモリリークを避けるためにのChildEventListenerを破棄していて、方向が完了すると、新しいインスタンスChildEventListenerのためにFirebaseがデータベースを再クエリします。

RecyclerViewの向きを保存する方法はありますか? android:configChangesはレイアウトを変更できないので、私はすでにParcelableとして保存しようとしましたが、失敗しました。私はそれが私が行方不明になっていることは簡単だと確信していますが、ちょっと、私は開発の初心者です。私のコードに関する助言や提案は非常に高く評価されています。ありがとう!

以下は私のクラスですが、必要なコードだけを短縮しました。

MainActivity

public class MainActivity extends AppCompatActivity { 

private RecyclerAdapter mRecyclerAdapter; 
private DatabaseReference mDatabaseReference; 
private RecyclerView mRecyclerView; 
private Query query; 


@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    PreferenceManager.setDefaultValues(this, R.xml.preferences, false); 
    setContentView(R.layout.activity_main); 
    getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
    setSupportActionBar(toolbar); 

    final int columns = getResources().getInteger(R.integer.gallery_columns); 

    mDatabaseReference = FirebaseDatabase.getInstance().getReference(); 
    query = mDatabaseReference.child("apod").orderByChild("date"); 

    mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); 
    mRecyclerView.setHasFixedSize(true); 
    mRecyclerView.setLayoutManager(new GridLayoutManager(this, columns)); 


    mRecyclerAdapter = new RecyclerAdapter(this, query); 
    mRecyclerView.setAdapter(mRecyclerAdapter); 


    } 

@Override 
public void onDestroy() { 
    mRecyclerAdapter.cleanupListener(); 
    } 

} 

RecyclerAdapter

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ApodViewHolder> { 

private final Context mContext; 
private final ChildEventListener mChildEventListener; 
private final Query mDatabaseReference; 
private final List<String> apodListIds = new ArrayList<>(); 
private final List<Apod> apodList = new ArrayList<>(); 

public RecyclerAdapter(final Context context, Query ref) { 
    mContext = context; 
    mDatabaseReference = ref; 


    ChildEventListener childEventListener = new ChildEventListener() { 


     @Override 
     public void onChildAdded(DataSnapshot dataSnapshot, String s) { 
      int oldListSize = getItemCount(); 
      Apod apod = dataSnapshot.getValue(Apod.class); 

      //Add data and IDs to the list 
      apodListIds.add(dataSnapshot.getKey()); 
      apodList.add(apod); 

      //Update the RecyclerView 
      notifyItemInserted(oldListSize - getItemCount() - 1); 
     } 

     @Override 
     public void onChildChanged(DataSnapshot dataSnapshot, String s) { 

     } 

     @Override 
     public void onChildRemoved(DataSnapshot dataSnapshot) { 
      String apodKey = dataSnapshot.getKey(); 
      int apodIndex = apodListIds.indexOf(apodKey); 

      if (apodIndex > -1) { 

       // Remove data and IDs from the list 
       apodListIds.remove(apodIndex); 
       apodList.remove(apodIndex); 

       // Update the RecyclerView 
       notifyDataSetChanged(); 
      } 
     } 

     @Override 
     public void onChildMoved(DataSnapshot dataSnapshot, String s) { 

     } 

     @Override 
     public void onCancelled(DatabaseError databaseError) { 

     } 

    }; 
    ref.addChildEventListener(childEventListener); 
    mChildEventListener = childEventListener; 

} 

    @Override 
    public int getItemCount() { 
    return apodList.size(); 
} 
    public void cleanupListener() { 
    if (mChildEventListener != null) { 
     mDatabaseReference.removeEventListener(mChildEventListener); 
    } 
    } 

} 
+2

私は問題は、構成変更後の新しいRVがないことだと思いますonCreateの終了時にそのビューの内容について知っておく必要があります。なぜなら、そのデータはすべての子リスナーがトリガーした後に来るからです。その時点で空であると考えれば、完全に復元する方法はわかりません。私は解りませんが、その情報を提供したいと思いました。 RVにスクロールオフセットがあるかどうかを問い合わせることができます。 @DougStevenson。 –

+0

ありがとうDoug、私は位置を保存して復元しようとしましたが、動作させることができませんでした。私はこの記事がもっと注目されると思っていました。なぜなら、何かが完全に欠けているか、Firebaseがこのようなことを実装するのを難しくしているからです。私は何かを見つけたら、私の投稿を更新しようとし続けます。 –

答えて

2

EDIT:

私は最終的に複数の要因を使用して、この作品を作ることができました。これらのうちのどれかが省かれた場合、それは単に機能しません。

private final String KEY_RECYCLER_STATE = "recycler_state"; 
private static Bundle mBundleRecyclerViewState; 
private Parcelable mListState = null;` 


@Override 
protected void onPause() { 
    super.onPause(); 

    mBundleRecyclerViewState = new Bundle(); 
    mListState = mRecyclerView.getLayoutManager().onSaveInstanceState(); 
    mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, mListState); 


} 

AndroidManifest.xml

configChangesを含めるonPause
にRecyclerView状態の私のonCreate

gridLayoutManager = new GridLayoutManager(getApplicationContext(), columns); 

保存で新しいGridLayoutManagerを作成します。

RecyclerViewの状態を保存して復元するだけでは不十分です。私はまた、穀物に反するとし、私のAndroidManifest.xmlを変更する必要がありました:|

<activity 
     android:name=".MainActivity" 
     android:configChanges="orientation|screenSize" 
     > 

「アンドロイドconfigChanges = 『オリエンテーション画面サイズ』」ハンドラは一つであったHandler

を使用してonConfigurationChangedRecyclerView状態を復元します。最も重要な部品のうち、含まれていなければ、それは単に機能しません。RecyclerViewの位置は0にリセットされます。これは数年前の欠陥であり、修正されていないと思います。私はGridLayoutManagersetSpanCountを方向に応じて変更しました。

@Override 
public void onConfigurationChanged(Configuration newConfig) { 
    super.onConfigurationChanged(newConfig); 
    columns = getResources().getInteger(R.integer.gallery_columns); 

    if (mBundleRecyclerViewState != null) { 
     new Handler().postDelayed(new Runnable() { 

      @Override 
      public void run() { 
       mListState = mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE); 
       mRecyclerView.getLayoutManager().onRestoreInstanceState(mListState); 

      } 
     }, 50); 
    } 

    // Checks the orientation of the screen 
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { 

     gridLayoutManager.setSpanCount(columns); 

    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { 

     gridLayoutManager.setSpanCount(columns); 

    } 
    mRecyclerView.setLayoutManager(gridLayoutManager); 
} 

私が言ったように、これらすべての変更は、少なくとも私にとっては必要でした。私はこれが最も効率的な方法であるかどうかは分かりませんが、何時間も費やしてからは私のために働きます。

1

オリエンテーションが変更されるたびに、アクティビティが再作成されます。したがって、バンドル内の位置を保存してから、onCreate()が2回目に呼び出されたときに再利用する必要があります。

@Override 
public void onCreate(Bundle savedInstanceState) { 

    super.onCreate(savedInstanceState); 

    int recyclerViewPosition; 
    if(savedInstanceState != null){ 
    recyclerViewPosition = savedInstanceState.getInt(RECYCLER_VIEW_POSITION); 
    } 

    mRecyclerView.scrollToPosition(recyclerViewPosition); 

} 

@Override 
public void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 

    //save the current recyclerview position 
    outState.putInt(RECYCLER_VIEW_POSITION, mRecyclerView.getAdapterPosition()); 
} 

あなたはリサイクルビューの状態を復元する方法の詳細オプションについてRecyclerView.State()linkを見てしたいことがあります。

5

ローテーションアクティビティが破棄され、再度作成されると、すべてのオブジェクトが破棄されて再作成され、レイアウトが再度描画されます。

アクティビティの再作成を停止したい場合は、android:configChangesを使用することができますが、それは悪い習慣でも正しい方法ではありません。

残っているのは、ローテーションする前にrecyclerviewの位置を保存し、アクティビティを再作成した後に取得することだけです。値

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    PreferenceManager.setDefaultValues(this, R.xml.preferences, false); 
    setContentView(R.layout.activity_main); 
    getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
    setSupportActionBar(toolbar); 

    final int columns = getResources().getInteger(R.integer.gallery_columns); 

    mDatabaseReference = FirebaseDatabase.getInstance().getReference(); 
    query = mDatabaseReference.child("apod").orderByChild("date"); 

    mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); 
    mRecyclerView.setHasFixedSize(true); 
    mRecyclerView.setLayoutManager(new GridLayoutManager(this, columns)); 


    mRecyclerAdapter = new RecyclerAdapter(this, query); 
    mRecyclerView.setAdapter(mRecyclerAdapter); 
    if(savedInstanceState != null){ 
    // scroll to existing position which exist before rotation. 
    mRecyclerView.scrollToPosition(savedInstanceState.getInt("position")); 
    } 

    } 

を復元するためにrecyclerview位置に

@Override 
    public void onSaveInstanceState(Bundle savedInstanceState) { 
     // Save UI state changes to the savedInstanceState. 
     // This bundle will be passed to onCreate if the process is 
     // killed and restarted. 
     savedInstanceState.putInt("position", mRecyclerView.getAdapterPosition()); // get current recycle view position here. 
     super.onSaveInstanceState(savedInstanceState); 
    } 

//を保存する

//これらはあなたを助け願っています。

+0

ありがとう@jitesh、私はこれを前に試しましたが、それは十分ではありませんでした。 1つの理由は、「ハンドラー」を追加する必要があるためでした。私は最後にそれを動作させることができたときに、画面全体をリフレッシュするにはまだまだひどい遅れがありました。 'notifyItemInserted(oldListSize - getItemCount()-1); 'が新しい' Activity' 。この周りの唯一の方法は、 'android:configChanges'を使うことでした。私はそれが嫌になっているのを知っていますが、オリエンテーションの変更中にはあまり醜いことはありません。 –

0

実際には、アクティビティの再作成時にリサイクルビューの位置を復元する方がはるかに優れています。これは、リサイクラビューレイアウトマネージャオプションを使用して最後の位置(最後のリサイクラビューのレイアウト状態)を保存および復元します。このtutorialを見てください。

いくつかの単語を使用すると、対応するアクティビティメソッド内のonSaveInstanceState()およびonRestoreInstanceState(Parcelable状態)のレイアウトマネージャメソッドを呼び出す必要があります。最後に、アダプタにデータを入力した直後にレイアウトマネージャの状態を復元する必要があります。

あなたが例えばLinearLayoutManagerの実装内で見てみる場合は、このクラスは状態を管理する方法について説明します(店/復元)

関連する問題