Все уже видели 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();
