AdapterView
关键方法解析:
-
- AdapterView 关键方法解析:
- setEmptyView
- rememberSyncState
- AdapterDataSetObserver
- checkFocus
- SelectionNotifier
- dispatchOnItemSelected
- getPositionForView
- selectionChanged
- AdapterView 关键方法解析:
setEmptyView
public void setEmptyView(View emptyView) {
mEmptyView = emptyView;
// If not explicitly specified this view is important for accessibility.
if (emptyView != null
&& emptyView.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
emptyView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
final T adapter = getAdapter();
final boolean empty = ((adapter == null) || adapter.isEmpty());
// 如果当前没有 adapter, 或者 adapter 没有数据 empty == true;
updateEmptyStatus(empty);
}
/** * empty 如果 true, 并且emptyView 存在,就显示 EmptyView ,隐藏 AdpterView ; * 否则 显示 AdapterView */
private void updateEmptyStatus(boolean empty) {
if (isInFilterMode()) {
empty = false;
}
if (empty) {
if (mEmptyView != null) {
mEmptyView.setVisibility(View.VISIBLE);
setVisibility(View.GONE);
} else {
// If the caller just removed our empty view, make sure the list view is visible
setVisibility(View.VISIBLE);
}
// We are now GONE, so pending layouts will not be dispatched.
// Force one here to make sure that the state of the list matches
// the state of the adapter.
if (mDataChanged) {
this.onLayout(false, mLeft, mTop, mRight, mBottom);
}
} else {
if (mEmptyView != null) mEmptyView.setVisibility(View.GONE);
setVisibility(View.VISIBLE);
}
}
rememberSyncState
/** * 如果当前的子View 数量大于0: * mNeedSync = true; * mSyncHeight = mLayoutHeight; * 如果当前选中的View 位置大于0,则 * 获取当前选中的View , 记录该View 的值;否则记录第一个View 的值。 */
void rememberSyncState() {
if (getChildCount() > 0) {
mNeedSync = true;
mSyncHeight = mLayoutHeight;
if (mSelectedPosition >= 0) {
// Sync the selection state
View v = getChildAt(mSelectedPosition - mFirstPosition);
mSyncRowId = mNextSelectedRowId;
mSyncPosition = mNextSelectedPosition;
if (v != null) {
mSpecificTop = v.getTop();
}
mSyncMode = SYNC_SELECTED_POSITION;
} else {
// Sync the based on the offset of the first view
View v = getChildAt(0);
T adapter = getAdapter();
if (mFirstPosition >= 0 && mFirstPosition < adapter.getCount()) {
mSyncRowId = adapter.getItemId(mFirstPosition);
} else {
mSyncRowId = NO_ID;
}
mSyncPosition = mFirstPosition;
if (v != null) {
mSpecificTop = v.getTop();
}
mSyncMode = SYNC_FIRST_POSITION;
}
}
}
AdapterDataSetObserver
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
// 该方法被调用时,
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
// Detect the case where a cursor that was previously invalidated has
// been repopulated with new data.
if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
&& mOldItemCount == 0 && mItemCount > 0) {
// 恢复当前 view 的状态
AdapterView.this.onRestoreInstanceState(mInstanceState);
mInstanceState = null;
} else {
rememberSyncState();
}
checkFocus();
requestLayout();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (AdapterView.this.getAdapter().hasStableIds()) {
// Remember the current state for the case where our hosting activity is being
// stopped and later restarted
// 记录当前view 的状态
mInstanceState = AdapterView.this.onSaveInstanceState();
}
// Data is invalid so we should reset our state
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;
checkFocus();
requestLayout();
}
public void clearSavedState() {
mInstanceState = null;
}
}
checkFocus
void checkFocus() {
final T adapter = getAdapter();
final boolean empty = adapter == null || adapter.getCount() == 0;
// 如果当前 adpter 是有数据的,focusable == true
final boolean focusable = !empty || isInFilterMode();
// The order in which we set focusable in touch mode/focusable may matter
// for the client, see View.setFocusableInTouchMode() comments for more
// details
super.setFocusableInTouchMode(focusable && mDesiredFocusableInTouchModeState);
super.setFocusable(focusable ? mDesiredFocusableState : NOT_FOCUSABLE);
if (mEmptyView != null) {
updateEmptyStatus((adapter == null) || adapter.isEmpty());
}
}
SelectionNotifier
private class SelectionNotifier implements Runnable {
public void run() {
mPendingSelectionNotifier = null;
if (mDataChanged && getViewRootImpl() != null
&& getViewRootImpl().isLayoutRequested()) {
// Data has changed between when this SelectionNotifier was
// posted and now. Postpone the notification until the next
// layout is complete and we run checkSelectionChanged().
if (getAdapter() != null) {
// 满足上述条件,将 mPendingSelectionNotifier 重置为当前对象
mPendingSelectionNotifier = this;
}
} else {
dispatchOnItemSelected();
}
}
}
dispatchOnItemSelected
private void dispatchOnItemSelected() {
fireOnSelected();
performAccessibilityActionsOnSelected(); // 不用管
}
// 调用 item 点击的回调接口
// 如果当前选中的 id <0 , 回调 onNothingSelected()
// 如果当前选中的 id >=0 , 回调 onItemSelected()
private void fireOnSelected() {
if (mOnItemSelectedListener == null) {
return;
}
final int selection = getSelectedItemPosition();
if (selection >= 0) {
View v = getSelectedView();
mOnItemSelectedListener.onItemSelected(this, v, selection,
getAdapter().getItemId(selection));
} else {
mOnItemSelectedListener.onNothingSelected(this);
}
}
getPositionForView
// 获取 当前 view 所在的位置 position
public int getPositionForView(View view) {
View listItem = view;
try {
View v;
while ((v = (View) listItem.getParent()) != null && !v.equals(this)) {
listItem = v;
}
} catch (ClassCastException e) {
// We made it up to the window without find this list view
return INVALID_POSITION;
}
if (listItem != null) {
// Search the children for the list item
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
if (getChildAt(i).equals(listItem)) {
return mFirstPosition + i;
}
}
}
// Child not found!
return INVALID_POSITION;
}
selectionChanged
void selectionChanged() {
// We're about to post or run the selection notifier, so we don't need
// a pending notifier.
mPendingSelectionNotifier = null;
if (mOnItemSelectedListener != null
|| AccessibilityManager.getInstance(mContext).isEnabled()) {
if (mInLayout || mBlockLayoutRequests) {
// If we are in a layout traversal, defer notification
// by posting. This ensures that the view tree is
// in a consistent state and is able to accommodate
// new layout or invalidate requests.
if (mSelectionNotifier == null) {
mSelectionNotifier = new SelectionNotifier();
} else {
removeCallbacks(mSelectionNotifier);
}
post(mSelectionNotifier);
} else {
dispatchOnItemSelected();
}
}
// Always notify AutoFillManager - it will return right away if autofill is disabled.
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
if (afm != null) {
afm.notifyValueChanged(this);
}
}
没太看懂这个方法,大致应该是去执行
dispatchOnItemSelected();
。
//
void setNextSelectedPositionInt(int position) {
mNextSelectedPosition = position;
mNextSelectedRowId = getItemIdAtPosition(position);
// If we are trying to sync to the selection, update that too
if (mNeedSync && mSyncMode == SYNC_SELECTED_POSITION && position >= 0) {
mSyncPosition = position;
mSyncRowId = mNextSelectedRowId;
}
}
/** * Searches the adapter for a position matching mSyncRowId. The search starts at mSyncPosition * and then alternates between moving up and moving down until 1) we find the right position, or * 2) we run out of time, or 3) we have looked at every position * * @return Position of the row that matches mSyncRowId, or {@link #INVALID_POSITION} if it can't * be found */
int findSyncPosition() {
int count = mItemCount;
if (count == 0) {
return INVALID_POSITION;
}
long idToMatch = mSyncRowId;
int seed = mSyncPosition;
// If there isn't a selection don't hunt for it
if (idToMatch == INVALID_ROW_ID) {
return INVALID_POSITION;
}
// Pin seed to reasonable values
seed = Math.max(0, seed);
seed = Math.min(count - 1, seed);
long endTime = SystemClock.uptimeMillis() + SYNC_MAX_DURATION_MILLIS;
long rowId;
// first position scanned so far
int first = seed;
// last position scanned so far
int last = seed;
// True if we should move down on the next iteration
boolean next = false;
// True when we have looked at the first item in the data
boolean hitFirst;
// True when we have looked at the last item in the data
boolean hitLast;
// Get the item ID locally (instead of getItemIdAtPosition), so
// we need the adapter
T adapter = getAdapter();
if (adapter == null) {
return INVALID_POSITION;
}
while (SystemClock.uptimeMillis() <= endTime) {
rowId = adapter.getItemId(seed);
if (rowId == idToMatch) {
// Found it!
return seed;
}
hitLast = last == count - 1;
hitFirst = first == 0;
if (hitLast && hitFirst) {
// Looked at everything
break;
}
if (hitFirst || (next && !hitLast)) {
// Either we hit the top, or we are trying to move down
last++;
seed = last;
// Try going up next time
next = false;
} else if (hitLast || (!next && !hitFirst)) {
// Either we hit the bottom, or we are trying to move up
first--;
seed = first;
// Try going down next time
next = true;
}
}
return INVALID_POSITION;
}
void handleDataChanged() {
final int count = mItemCount;
boolean found = false;
if (count > 0) {
int newPos;
// Find the row we are supposed to sync to
if (mNeedSync) {
// Update this first, since setNextSelectedPositionInt inspects
// it
mNeedSync = false;
// See if we can find a position in the new data with the same
// id as the old selection
newPos = findSyncPosition();
if (newPos >= 0) {
// Verify that new selection is selectable
int selectablePos = lookForSelectablePosition(newPos, true);
if (selectablePos == newPos) {
// Same row id is selected
setNextSelectedPositionInt(newPos);
found = true;
}
}
}
if (!found) {
// Try to use the same position if we can't find matching data
newPos = getSelectedItemPosition();
// Pin position to the available range
if (newPos >= count) {
newPos = count - 1;
}
if (newPos < 0) {
newPos = 0;
}
// Make sure we select something selectable -- first look down
int selectablePos = lookForSelectablePosition(newPos, true);
if (selectablePos < 0) {
// Looking down didn't work -- try looking up
selectablePos = lookForSelectablePosition(newPos, false);
}
if (selectablePos >= 0) {
setNextSelectedPositionInt(selectablePos);
checkSelectionChanged();
found = true;
}
}
}
if (!found) {
// Nothing is selected
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;
checkSelectionChanged();
}
notifySubtreeAccessibilityStateChangedIfNeeded();
}
/** * Called after layout to determine whether the selection position needs to * be updated. Also used to fire any pending selection events. */
void checkSelectionChanged() {
if ((mSelectedPosition != mOldSelectedPosition) || (mSelectedRowId != mOldSelectedRowId)) {
selectionChanged();
mOldSelectedPosition = mSelectedPosition;
mOldSelectedRowId = mSelectedRowId;
}
// If we have a pending selection notification -- and we won't if we
// just fired one in selectionChanged() -- run it now.
if (mPendingSelectionNotifier != null) {
mPendingSelectionNotifier.run();
}
}
这几个方法,部分代码比较多,不过也算不上复杂。但是不明觉厉,不清楚是在干什么。大致知道是在操作变量。估计是跟 用户触摸选择 item 有关联。
今天的文章android adapter详解_android API「建议收藏」分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/58352.html