Android入門ListViewでの正確な位置付け
Androidの開発では,あるリストItemの位置を自発的に設定する必要があるというニーズに遭遇することが多い.位置を設定する関数は
ListView.setSelection(int position)
ListView.setSelectionFromTop(int position, int y);
内
positionとは、指定されたitemのListViewにおけるインデックスを指し、Headerが存在する場合、インデックスはHeaderから算出されることに注意する.
yは、ListView可視範囲の最上辺までの距離を指す.
関数ができました.今は自分のニーズに合わせて設定します.
今回遭遇した需要は、ListViewの要求が下から上へ表示され、Cursorが更新されると、元の一番上のitem(headerを除く)の位置を維持し、新しい履歴データが元のitemの上に表示され続けます.図:
ListViewは下から上へ、つまり
では、Cursorが更新されると、最初のインデックスが変わります.(図中のR)の位置を保つには.手順は次のとおりです.
(1)新しいCursorでの位置を取得する(posiition)
(2)Cursor交換後のListViewの位置を取得する.
(4)ListViewのスクロール可能な属性のため、Cursorを交換する前に表示される最初のitemのインデックスを記録する必要がある(ListView.getFirstVisiblePosition()
(3)FirstVisiblePositionが0以上である場合を区別する.ヘッド、つまり図中のLoadingは新しいデータが出てから消えるからです.
(4)FirstVisiblePositionが0の場合は実際にヘッダを指し、ヘッダの下の最初の(R)の位置を維持します.ではこのときFirstVisiblePositionを1に設定します
(5)FirstVisiblePositionが0より大きい場合は実際にitemを指しますが、FirstVisiblePositionを0に設定する必要があります.*
(6)FirstVisiblePosition用ListView.getChildAt(int position)関数は、対応するitemのViewを取得し、View.getTop()関数は、ListViewの上部までの距離Yを取得する.
これでsetSelectionFromTop(int position,int y)に必要な2つのパラメータpositionとyがあります.
*注記:ListView.getChildAt(int position)、このpositionは可視itemにおけるインデックスを指し、cursorの位置とは大きく異なる.見てもいいよgetChildCount()関数は、個数がCursorの個数以下である(headerを考慮しない)ことを得る.全部で20個のデータがあるかもしれませんが、インタフェースは8個しか見えません.では、このChildCountは約8個です.一方,FirstVisiblePositionは全体の本数の中にあるインデックスを取り出し,再び消えるヘッダを考慮するため,FirstVisiblePositionが0の場合は1,0より大きい場合は0とする.
次のコードは次のとおりです.
呼び出されたコード:
getOffsettYには図中のTimeStampの高さを計算するコードがあり、気にしないで自分でスキップすることができます.履歴データを検索すると、元のTimeStampがあるものがリフレッシュ後にTimeStampを表示しなくなる可能性があるので(前のものと同じ時間帯に統合されている)、その高さも計算します.
ListView.setSelection(int position)
ListView.setSelectionFromTop(int position, int y);
内
positionとは、指定されたitemのListViewにおけるインデックスを指し、Headerが存在する場合、インデックスはHeaderから算出されることに注意する.
yは、ListView可視範囲の最上辺までの距離を指す.
関数ができました.今は自分のニーズに合わせて設定します.
今回遭遇した需要は、ListViewの要求が下から上へ表示され、Cursorが更新されると、元の一番上のitem(headerを除く)の位置を維持し、新しい履歴データが元のitemの上に表示され続けます.図:
ListViewは下から上へ、つまり
android:stackFromBottom="true"
しかし、この属性の設定はインデックスのソート順序に影響しないことが分かった.すなわち、itemのインデックスはすべて上から下へ増加し、下から上へ増加しない.インデックスが0のitemは、いずれもListViewの一番上にあるitem(またはheader)である.では、Cursorが更新されると、最初のインデックスが変わります.(図中のR)の位置を保つには.手順は次のとおりです.
(1)新しいCursorでの位置を取得する(posiition)
(2)Cursor交換後のListViewの位置を取得する.
(4)ListViewのスクロール可能な属性のため、Cursorを交換する前に表示される最初のitemのインデックスを記録する必要がある(ListView.getFirstVisiblePosition()
(3)FirstVisiblePositionが0以上である場合を区別する.ヘッド、つまり図中のLoadingは新しいデータが出てから消えるからです.
(4)FirstVisiblePositionが0の場合は実際にヘッダを指し、ヘッダの下の最初の(R)の位置を維持します.ではこのときFirstVisiblePositionを1に設定します
(5)FirstVisiblePositionが0より大きい場合は実際にitemを指しますが、FirstVisiblePositionを0に設定する必要があります.*
(6)FirstVisiblePosition用ListView.getChildAt(int position)関数は、対応するitemのViewを取得し、View.getTop()関数は、ListViewの上部までの距離Yを取得する.
これでsetSelectionFromTop(int position,int y)に必要な2つのパラメータpositionとyがあります.
*注記:ListView.getChildAt(int position)、このpositionは可視itemにおけるインデックスを指し、cursorの位置とは大きく異なる.見てもいいよgetChildCount()関数は、個数がCursorの個数以下である(headerを考慮しない)ことを得る.全部で20個のデータがあるかもしれませんが、インタフェースは8個しか見えません.では、このChildCountは約8個です.一方,FirstVisiblePositionは全体の本数の中にあるインデックスを取り出し,再び消えるヘッダを考慮するため,FirstVisiblePositionが0の場合は1,0より大きい場合は0とする.
次のコードは次のとおりです.
呼び出されたコード:
int headerCount = mListContainer.getListView().getHeaderViewsCount();
int firstVisiblePos = mListContainer.getListView().getFirstVisiblePosition();
int newCursorPosition = getPositionInNewCursor(cursor.getCount(), firstVisiblePos);
int offsetY = getOffsetY(cursor, firstVisiblePos, newCursorPosition);
mAdapter.changeCursor(cursor);
mUpRefreshLayout.setVisibility(View.GONE);
mListContainer.getListView().setSelectionFromTop(newCursorPosition + headerCount, offsetY);
getPositionInNewCursor関数: private int getPositionInNewCursor(int newCursorCount, int firstVisiblePos){
if(firstVisiblePos == 0){
firstVisiblePos += 1;
}
int headerCount = mListContainer.getListView().getHeaderViewsCount();
int newCursorPos = newCursorCount - mAdapter.getCount() + firstVisiblePos - headerCount;
return newCursorPos;
}
getOffsettY関数:private int getOffsetY(Cursor cursor, int firstVisiblePos, int newCursorPosition){
int y;
View firstVisibleItem = null;
if(firstVisiblePos == 0){
firstVisibleItem = mListContainer.getListView().getChildAt(1);
}else{
firstVisibleItem = mListContainer.getListView().getChildAt(0);
}
y = firstVisibleItem.getTop();
View timeView = firstVisibleItem.findViewById(R.id.time_text_view);
if(timeView != null && timeView.getVisibility() == View.VISIBLE){
Cursor curItem = (Cursor)mAdapter.getItem(newCursorPosition);
Cursor preItem = (Cursor)mAdapter.getItem(newCursorPosition - 1);
if(curItem != null || preItem != null){
long curTimeStamp = curItem.getLong(MessagesProjection.JEDI_CREATE_DATE_INDX);
long preTimeStamp = preItem.getLong(MessagesProjection.JEDI_CREATE_DATE_INDX);
if(Math.abs(curTimeStamp - preTimeStamp) <= SHOW_TIME_STAMP_TEN_MINS){
LayoutParams param = (LinearLayout.LayoutParams)mTimeView.getLayoutParams();
y += mTimeView.getHeight() + param.topMargin + param.bottomMargin;
}
}
}
return y;
}
getOffsettYには図中のTimeStampの高さを計算するコードがあり、気にしないで自分でスキップすることができます.履歴データを検索すると、元のTimeStampがあるものがリフレッシュ後にTimeStampを表示しなくなる可能性があるので(前のものと同じ時間帯に統合されている)、その高さも計算します.