Все уже видели Android-карты с сенсорным отзывом. Селектор рисуется на переднем плане, а не на заднем плане (как мы обычно это реализуем). Этот эффект на самом деле довольно прост в реализации, и в некоторых случаях он уже реализован.
Вот скриншот эффекта состояния печати в приложении Google Play:
I. Если ваш взгляд FrameLayout
Это самый простой способ добавить селектор переднего плана, потому что для этого есть метод ! На самом деле, вам просто нужно передать свой селектор, как android:foreground
в xml или программно, вызывая setForeground(Drawable)
.
II. Если ваш взгляд неFrameLayout
Не волнуйся, это довольно просто. По сути, нам просто нужно установить правильное состояние селектора (нажатие, фокусировка и т. Д.), Установить границы и нарисовать его после самого вида. Таким образом, селектор будет нарисован после и, как следствие, над видом.
Смена государства
В View
классе метод вызывается каждый раз, когда изменяется состояние представления. Этот метод drawableStateChanged()
( DOC ЗДЕСЬ ).
@Override protected void drawableStateChanged() { super.drawableStateChanged(); mForegroundSelector.setState(getDrawableState()); //redraw invalidate(); }
Обновление границ рисования
Метод вызывается каждый раз, когда изменяется размер представления. Этот метод onSizeChanged(int, int, int, int)
( DOC ЗДЕСЬ ).
@Override protected void onSizeChanged(int width, int height, int oldwidth, int oldheight) { super.onSizeChanged(width, height, oldwidth, oldheight); mForegroundSelector.setBounds(0, 0, width, height); }
Рисование селектора
Есть 2 случая:
1. Ваше мнение не являетсяViewGroup
Селектор должен быть нарисован после вызова onDraw(Canvas canvas)
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mForegroundSelector.draw(canvas); }
2. Ваше мнение ViewGroup
Селектор должен быть нарисован после всех его детей, то есть после вызова dispatchDraw(Canvas canvas)
@Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); mForegroundSelector.draw(canvas); }
Рисование анимированного рисунка
Если ваш рисованный рисунок анимирован, вам нужно сделать еще кое-что. Давайте предположим, что у нас есть селектор с атрибутом android:exitFadeDuration
. Это означает, что когда селектор меняет свое состояние, старое состояние исчезает.
Сначала мы должны переместить метод draw селектора из onDraw()
(для представлений) или dispatchDraw()
(для ViewGroups) в draw(Canvas)
метод, вот так:
@Override public void draw(Canvas canvas) { super.draw(canvas); mForegroundDrawable.draw(canvas); }
Затем мы должны переопределить, jumpDrawablesToCurrentState
чтобы указать наш селектор, чтобы сделать анимацию перехода между состояниями, и verifyDrawable
чтобы указать представление, которое мы отображаем в нашей собственной прорисовке.
@Override protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || (who == mForegroundDrawable); } @TargetApi(11) @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); mForegroundDrawable.jumpToCurrentState(); }
Но что делает jumpToCurrentState ()? Давайте посмотрим немного исходного кода, в DrawableContainer
классе.
@Override public void jumpToCurrentState() { boolean changed = false; if (mLastDrawable != null) { mLastDrawable.jumpToCurrentState(); mLastDrawable = null; changed = true; } if (mCurrDrawable != null) { mCurrDrawable.jumpToCurrentState(); mCurrDrawable.mutate().setAlpha(mAlpha); } if (mExitAnimationEnd != 0) { mExitAnimationEnd = 0; changed = true; } if (mEnterAnimationEnd != 0) { mEnterAnimationEnd = 0; changed = true; } if (changed) { invalidateSelf(); } }
Мы можем заметить, что jumpToCurrentState()
звонки invalidateSelf()
. И вот invalidateSelf()
исходный код метода:
/** * Use the current {@link Callback} implementation to have this Drawable * redrawn. Does nothing if there is no Callback attached to the * Drawable. * * @see Callback#invalidateDrawable * @see #getCallback() * @see #setCallback(android.graphics.drawable.Drawable.Callback) */ public void invalidateSelf() { final Callback callback = getCallback(); if (callback != null) { callback.invalidateDrawable(this); } }
Мы ясно видим, что если обратный вызов не установлен, рисование не будет перерисовано. Итак, давайте установим обратный вызов, когда мы начнем наш селектор.
private void init(Context context) { mForegroundDrawable = getResources().getDrawable(R.drawable.myselector); //set a callback, or the selector won't be animated mForegroundDrawable.setCallback(this); }
Ваш селектор должен исчезнуть сейчас!
EXTRA
Получить фон по умолчанию и установить его в качестве переднего плана
Вы можете получить фоновый селектор по умолчанию для вашей темы и установить его в качестве основного, если хотите:
TypedArray a = getContext().obtainStyledAttributes(new int[]{android.R.attr.selectableItemBackground}); mForegroundDrawable = a.getDrawable(0); if (mForegroundDrawable != null) { mForegroundDrawable.setCallback(this); } a.recycle();