Android入門ListViewでの正確な位置付け

3915 ワード

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は下から上へ、つまり
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を表示しなくなる可能性があるので(前のものと同じ時間帯に統合されている)、その高さも計算します.