android AdapterView 关键方法分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DucklikeJAVA/article/details/82021946

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 有关联。

猜你喜欢

转载自blog.csdn.net/DucklikeJAVA/article/details/82021946