維持-保存/復元のスクロール位置に戻る際、ListView
-
26-09-2019 - |
質問
まだまだ先がありま ListView
ユーザーがスクロールの周りに帰国する前に、以前の画面になります。ユーザがす ListView
もう一度、といったリストをスクロールによる同一地点でした。そのアイデアをどう実現。
解決
これを試してみてください
// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());
// ...
// restore index and position
mList.setSelectionFromTop(index, top);
説明:
ListView.getFirstVisiblePosition()
は、上部可視リスト項目を返します。しかし、この項目は部分的にビューの外にスクロールすることができる、そしてあなたは、リストの正確なスクロール位置を復元したい場合は、このオフセットを取得する必要があります。トップリスト項目のListView.getChildAt(0)
を返し、View
の先頭からのオフセット、その後View.getTop() - mList.getPaddingTop()
リターンがその相対的ListView
はそう。その後、ListView
のスクロール位置を復元するために、私たちは、私たちが望む項目のインデックスとListView.setSelectionFromTop()
を呼び出し、ListView
の上からそのトップエッジを配置するオフセットます。
他のヒント
Parcelable state;
@Override
public void onPause() {
// Save ListView state @ onPause
Log.d(TAG, "saving listview state @ onPause");
state = listView.onSaveInstanceState();
super.onPause();
}
...
@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Set new items
listView.setAdapter(adapter);
...
// Restore previous state (including selected item index and scroll position)
if(state != null) {
Log.d(TAG, "trying to restore listview state..");
listView.onRestoreInstanceState(state);
}
}
私は、@(カークWoll)によって提案されたソリューションを採用し、それは私のために動作します。私はまた、彼らは同様の手法を使用することを、「連絡先」アプリのためのAndroidのソースコードで見てきました。私はいくつかの詳細を追加したいと思います: 私のListActivity派生クラスでトップの場合:
private static final String LIST_STATE = "listState";
private Parcelable mListState = null;
次に、いくつかのメソッドをオーバーライドます:
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
mListState = state.getParcelable(LIST_STATE);
}
@Override
protected void onResume() {
super.onResume();
loadData();
if (mListState != null)
getListView().onRestoreInstanceState(mListState);
mListState = null;
}
@Override
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
mListState = getListView().onSaveInstanceState();
state.putParcelable(LIST_STATE, mListState);
}
もちろん「loaddataのは、」DBからデータを取得し、リスト上にそれを置くために私の関数である。
私のフローズンヨーグルトデバイスで、この作品の両方のアイテムを編集して、リストに戻ったときにあなたが電話の向きを変更し、ときます。
非常に単純な方法:
/** Save the position **/
int currentPosition = listView.getFirstVisiblePosition();
//Here u should save the currentPosition anywhere
/** Restore the previus saved position **/
listView.setSelection(savedPosition);
setSelection にリセットされます付属品のリスト。タッチモードでアイテムが実際にタッチモードであれば、選択されていない場合は項目だけ画面上に配置されます。
Aより複雑なアプローチます:
listView.setOnScrollListener(this);
//Implements the interface:
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
mCurrentX = view.getScrollX();
mCurrentY = view.getScrollY();
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
//Save anywere the x and the y
/** Restore: **/
listView.scrollTo(savedX, savedY);
私はこれについて何か興味深いを見つけます。
私はsetSelectionとscrolltoXYを試みたが、それがすべてで仕事をしませんでした、リストには、私が仕事
し、次のコードを持って、いくつかの試行錯誤の後に、同じ位置に留まっfinal ListView list = (ListView) findViewById(R.id.list);
list.post(new Runnable() {
@Override
public void run() {
list.setSelection(0);
}
});
の代わりにRunnableを掲載のおrunOnUiThreadをしようとした場合、それはしない作業のどちらか(いくつかのデバイス上に少なくとも)
このはまっすぐ進むべきである何かのための非常に奇妙な回避策です。
CAUTION !! ListView.getFirstVisiblePosition()が0の場合、正しく動作するようにonSaveStateを()ことはできませんAbsListViewにバグがあります。
あなたは、画面のほとんどを取るの拡大イメージを持っている、とあなたは第二の画像にスクロールしますが、最初の少しが表示されている場合は、だから、スクロール位置は保存されません...
の AbsListView.java:1650するから(コメント鉱山)
// this will be false when the firstPosition IS 0
if (haveChildren && mFirstPosition > 0) {
...
} else {
ss.viewTop = 0;
ss.firstId = INVALID_POSITION;
ss.position = 0;
}
しかし、このような状況では、以下のコードで「トップ」は正しく復元される状態を防ぐため、他の問題が発生し、負の数になります。だから、「トップが」負のとき、
次の子を取得します// save index and top position
int index = getFirstVisiblePosition();
View v = getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
if (top < 0 && getChildAt(1) != null) {
index++;
v = getChildAt(1);
top = v.getTop();
}
// parcel the index and top
// when restoring, unparcel index and top
listView.setSelectionFromTop(index, top);
private Parcelable state;
@Override
public void onPause() {
state = mAlbumListView.onSaveInstanceState();
super.onPause();
}
@Override
public void onResume() {
super.onResume();
if (getAdapter() != null) {
mAlbumListView.setAdapter(getAdapter());
if (state != null){
mAlbumListView.requestFocus();
mAlbumListView.onRestoreInstanceState(state);
}
}
}
それは十分にあります。
は、この問題に対する解決策を探していくつかのために、問題の根であってもよいです。あなたがリストビューにアダプタを設定した後、それがスクロール位置をリセットします。ただ、何かを検討します。我々はリストビューへの参照をつかむした後、私は私のonCreateViewにアダプターをセットに移動し、それは私のために問題を解決しました。 =)
私は誰もがこれを言及しなかった。
驚いていますので、Amはこれを投稿ユーザーが、彼はそれで出て行ったと彼は同じ状態で、リストビューに戻ります戻るボタンをクリックした後。
このコードは、[戻る]ボタンと同じように動作するボタン「アップ」を上書きしますので、リストビューの場合 - >詳細 - >リストビューに戻る(および他のオプションなし)これはscrollpositionを維持するための最も簡単なコードですそして、リストビューのコンテンツます。
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return(true);
}
return(super.onOptionsItemSelected(item)); }
注意:詳細アクティビティから別のアクティビティに行くことができるならば、あなたが仕事に、このためには戻るボタン履歴を操作する必要がありますので、アップボタンが戻ってその活動に戻ることができます。
単にリストビューXML宣言は十分にandroid:saveEnabled="true"
ていませんか?
ベスト液には:
// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());
// ...
// restore index and position
mList.post(new Runnable() {
@Override
public void run() {
mList.setSelectionFromTop(index, top);
}
});
必ずお電話ポストやスレッド!
あなたはリロード後、スクロール状態を維持することができます。私の場合は、非同期ネットワーク要求を行い、それが完了した後、コールバックでリストを再読み込み。私は状態を復元する場所です。コードサンプルはKotlinです。
val state = myList.layoutManager.onSaveInstanceState()
getNewThings() { newThings: List<Thing> ->
myList.adapter.things = newThings
myList.layoutManager.onRestoreInstanceState(state)
}
あなたが活動にホストされた断片を使用している場合は、このような何かを行うことができます:
public abstract class BaseFragment extends Fragment {
private boolean mSaveView = false;
private SoftReference<View> mViewReference;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (mSaveView) {
if (mViewReference != null) {
final View savedView = mViewReference.get();
if (savedView != null) {
if (savedView.getParent() != null) {
((ViewGroup) savedView.getParent()).removeView(savedView);
return savedView;
}
}
}
}
final View view = inflater.inflate(getFragmentResource(), container, false);
mViewReference = new SoftReference<View>(view);
return view;
}
protected void setSaveView(boolean value) {
mSaveView = value;
}
}
public class MyFragment extends BaseFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setSaveView(true);
final View view = super.onCreateView(inflater, container, savedInstanceState);
ListView placesList = (ListView) view.findViewById(R.id.places_list);
if (placesList.getAdapter() == null) {
placesList.setAdapter(createAdapter());
}
}
}
の場合は保存/復元のスクロール位置 ListView
自分で基本的に複製機能を既に実施スケーラビリティの枠組みの ListView
復元微細なスクロール位置だけでも独自のものを除く点に注意:として@aaronvargas記載があるバグを修正 AbsListView
ないように復元微細なスクロール位置のリストの商品です。それにもかかわらず、ベストするという簡単な方法によってスクロール位置を復元します。Androidフレームワークまいります。でいただくには、以下の条件:
- いただくには呼び出されません
setSaveEnabled(false)
方法、設定しておりませんandroid:saveEnabled="false"
属性のリストをxmlファイルのレイアウト - のための
ExpandableListView
オーバーライドlong getCombinedChildId(long groupId, long childId)
方法で返します正のlong番号(デフォルトの実装クラスBaseExpandableListAdapter
負の数を返します).ここでの例:
.
@Override
public long getChildId(int groupPosition, int childPosition) {
return 0L | groupPosition << 12 | childPosition;
}
@Override
public long getCombinedChildId(long groupId, long childId) {
return groupId << 32 | childId << 1 | 1;
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getCombinedGroupId(long groupId) {
return (groupId & 0x7FFFFFFF) << 32;
}
- の場合
ListView
またはExpandableListView
使用される断片な再現を断片活動レクリエーション(後の画面の回転など)を行います。取得フラグメントとfindFragmentByTag(String tag)
方法。 - を確認し
ListView
はandroid:id
ではユニークです。
め、上記問題点と最初のリスト項目を表示できるクラフトアダプターで返します特にダミーゼロのピクセルの高さの ListView
位置0になります。こちらではの簡単な例プロジェクトを示し ListView
や ExpandableListView
復活のスクロール位置はそのスクロール位置は明示的に保存/復帰します。微細なスクロール位置を復元完全にとっても複雑なシナリオの時間への切り替え一部の他のアプリケーション、ダブル画面の回転、切り戻し、試験願います。おいて明示的に撤退は、アプリケーション(戻るボタンをタップす)スクロール位置な保存することなどすべてのその他の意見なの保存状態しています。https://github.com/voromto/RestoreScrollPosition/releases
活動はほとんど常に再起動されたと詳細ビューが閉じられた時に、アダプターが再ロードされたためSimpleCursorAdapterを使用して実装するLoaderManager.LoaderCallbacksが、それは、onReset()内の位置を復元するために動作しませんでしたことをListActivity由来の活動のために。
:トリックはonLoadFinished()内の位置を復元することでした onListItemClickでの()の
// save the selected item position when an item was clicked
// to open the details
index = getListView().getFirstVisiblePosition();
View v = getListView().getChildAt(0);
top = (v == null) ? 0 : (v.getTop() - getListView().getPaddingTop());
の onLoadFinishedで():の
// restore the selected item which was saved on item click
// when details are closed and list is shown again
getListView().setSelectionFromTop(index, top);
onBackPressedでの()の
// Show the top item at next start of the app
index = 0;
top = 0;
どちらのソリューションの私のために動作するように見え、ここで提供しました。私の場合、私がListView
に置換していので、新しいFragment
インスタンスがフラグメントが示されるたびに作成され、FragmentTransaction
状態がFragment
のメンバーとして格納することができないその手段ListView
でFragment
を有しますます。
その代わり、私は私のカスタムApplication
クラスの状態を保存することになりました。以下のコードは、あなたのアイデアを与える必要がありますどのようにこの作品ます:
public class MyApplication extends Application {
public static HashMap<String, Parcelable> parcelableCache = new HashMap<>();
/* ... code omitted for brevity ... */
}
public class MyFragment extends Fragment{
private ListView mListView = null;
private MyAdapter mAdapter = null;
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mAdapter = new MyAdapter(getActivity(), null, 0);
mListView = ((ListView) view.findViewById(R.id.myListView));
Parcelable listViewState = MyApplication.parcelableCache.get("my_listview_state");
if( listViewState != null )
mListView.onRestoreInstanceState(listViewState);
}
@Override
public void onPause() {
MyApplication.parcelableCache.put("my_listview_state", mListView.onSaveInstanceState());
super.onPause();
}
/* ... code omitted for brevity ... */
}
基本的な考え方は、フラグメントインスタンスの外に状態を保存することです。あなたのアプリケーションクラスに静的フィールドを持つという考えが気に入らない場合、私はあなたがフラグメントインターフェイスを実装し、あなたの活動状態を格納することによってそれを行うことができると思います。
別の解決策はSharedPreferences
に保管することですが、それは少し複雑になりますし、あなたは状態はアプリが起動間で保持される場合を除き、必ずアプリケーションの起動時に、それをクリアにする必要があります。
また、回避するために、「スクロール位置を最初の項目が表示されているときに保存されていない」、あなたは0px
の高さを持つダミーの最初の項目を表示することができます。これは、このように、あなたのアダプタでgetView()
をオーバーライドすることによって達成することができます:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if( position == 0 ) {
View zeroHeightView = new View(parent.getContext());
zeroHeightView.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
return zeroHeightView;
}
else
return super.getView(position, convertView, parent);
}
私の答えはFirebaseのためのものであり、位置0は、回避策です。
Parcelable state;
DatabaseReference everybody = db.getReference("Everybody Room List");
everybody.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
state = listView.onSaveInstanceState(); // Save
progressBar.setVisibility(View.GONE);
arrayList.clear();
for (DataSnapshot messageSnapshot : dataSnapshot.getChildren()) {
Messages messagesSpacecraft = messageSnapshot.getValue(Messages.class);
arrayList.add(messagesSpacecraft);
}
listView.setAdapter(convertView);
listView.onRestoreInstanceState(state); // Restore
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
とconvertView
位置0あなたが
を使用していないことを空白の項目を追加public class Chat_ConvertView_List_Room extends BaseAdapter {
private ArrayList<Messages> spacecrafts;
private Context context;
@SuppressLint("CommitPrefEdits")
Chat_ConvertView_List_Room(Context context, ArrayList<Messages> spacecrafts) {
this.context = context;
this.spacecrafts = spacecrafts;
}
@Override
public int getCount() {
return spacecrafts.size();
}
@Override
public Object getItem(int position) {
return spacecrafts.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@SuppressLint({"SetTextI18n", "SimpleDateFormat"})
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.message_model_list_room, parent, false);
}
final Messages s = (Messages) this.getItem(position);
if (position == 0) {
convertView.getLayoutParams().height = 1; // 0 does not work
} else {
convertView.getLayoutParams().height = RelativeLayout.LayoutParams.WRAP_CONTENT;
}
return convertView;
}
}
私はそれがあなたの
のために働く願って、ユーザーを妨げることなく、一時的にこの作品を見てきましたこのコードの下に使用します
int index,top;
@Override
protected void onPause() {
super.onPause();
index = mList.getFirstVisiblePosition();
View v = challengeList.getChildAt(0);
top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());
}
とするたびに、リフレッシュあなたのデータは、コードの下に、これを使用します:
adapter.notifyDataSetChanged();
mList.setSelectionFromTop(index, top);
を使用してい FirebaseListAdapter とことができず、その解決策です。その結を行っています。私の推測であり優雅な方法ではあるがこれは完全で働きます。
前onCreate:
private int reset;
private int top;
private int index;
内FirebaseListAdapter:
@Override
public void onDataChanged() {
super.onDataChanged();
// Only do this on first change, when starting
// activity or coming back to it.
if(reset == 0) {
mListView.setSelectionFromTop(index, top);
reset++;
}
}
onStart:
@Override
protected void onStart() {
super.onStart();
if(adapter != null) {
adapter.startListening();
index = 0;
top = 0;
// Get position from SharedPrefs
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
top = sharedPref.getInt("TOP_POSITION", 0);
index = sharedPref.getInt("INDEX_POSITION", 0);
// Set reset to 0 to allow change to last position
reset = 0;
}
}
onStop:
@Override
protected void onStop() {
super.onStop();
if(adapter != null) {
adapter.stopListening();
// Set position
index = mListView.getFirstVisiblePosition();
View v = mListView.getChildAt(0);
top = (v == null) ? 0 : (v.getTop() - mListView.getPaddingTop());
// Save position to SharedPrefs
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
sharedPref.edit().putInt("TOP_POSITION" + "", top).apply();
sharedPref.edit().putInt("INDEX_POSITION" + "", index).apply();
}
}
私もこれを解決するための FirebaseRecyclerAdapter 私に掲載した溶液はこちられています:
前onCreate:
private int reset;
private int top;
private int index;
内FirebaseRecyclerAdapter:
@Override
public void onDataChanged() {
// Only do this on first change, when starting
// activity or coming back to it.
if(reset == 0) {
linearLayoutManager.scrollToPositionWithOffset(index, top);
reset++;
}
}
onStart:
@Override
protected void onStart() {
super.onStart();
if(adapter != null) {
adapter.startListening();
index = 0;
top = 0;
// Get position from SharedPrefs
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
top = sharedPref.getInt("TOP_POSITION", 0);
index = sharedPref.getInt("INDEX_POSITION", 0);
// Set reset to 0 to allow change to last position
reset = 0;
}
}
onStop:
@Override
protected void onStop() {
super.onStop();
if(adapter != null) {
adapter.stopListening();
// Set position
index = linearLayoutManager.findFirstVisibleItemPosition();
View v = linearLayoutManager.getChildAt(0);
top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop());
// Save position to SharedPrefs
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
sharedPref.edit().putInt("TOP_POSITION" + "", top).apply();
sharedPref.edit().putInt("INDEX_POSITION" + "", index).apply();
}
}