Как справиться с эффектом Ripple на 9-patch и CardView и управлять состоянием селектора?

Задний план

Я хочу добавить простой эффект пульсации для элементов listView из Android Lollipop и выше.

Сначала я хотел бы установить его для простых строк, а затем для 9-патч-строк и даже для CardView.

Проблема

Я был уверен, что это будет очень легко, поскольку он даже не требует, чтобы я определял нормальный селектор. Я не смог сделать это даже для простых строк. По какой-то причине эффект пульсации выходит за пределы границ строки:

Введите описание изображения здесь

Не только это, но в некоторых случаях фон элемента застревает в цвете, который я ему задал.

Код

Вот что я пробовал:

MainActivity.java

public class MainActivity extends ActionBarActivity { @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ListView listView = (ListView) findViewById(android.R.id.list); final LayoutInflater inflater = LayoutInflater.from(this); listView.setAdapter(new BaseAdapter() { @Override public View getView(final int position, final View convertView, final ViewGroup parent) { View rootView = convertView; if (rootView == null) { rootView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false); ((TextView) rootView.findViewById(android.R.id.text1)).setText("Test"); } return rootView; } @Override public long getItemId(final int position) { return 0; } @Override public Object getItem(final int position) { return null; } @Override public int getCount() { return 2; } }); } } 

activity_main.xml

 <?xml version="1.0" encoding="utf-8"?> <ListView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="match_parent" android:cacheColorHint="@android:color/transparent" android:divider="@null" android:dividerHeight="0px" android:fadeScrollbars="false" android:fastScrollEnabled="true" android:listSelector="@drawable/listview_selector" android:scrollingCache="false" android:verticalScrollbarPosition="right" /> 

Res / drawable-v21 / listview_selector.xml (у меня есть обычный селектор для других версий Android)

 <?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" /> 

Что я пробовал

Помимо простого вышеприведенного кода, я также попытался установить свойство фона селектора на элемент, вместо использования «listSelector» в ListView, но это не помогло.

Еще одна вещь, которую я пробовал, – это установить передний план элементов, но он также имеет тот же результат.

Вопрос

Как исправить эту проблему? Почему это происходит? Что я сделал не так?

Как я могу идти дальше, чтобы поддерживать 9-патч и даже CardView?

Также, как я могу установить состояние для нового фона, например, быть выбранным или выбранным?


Обновление: рисунок вне представления фиксируется с помощью следующего:

 <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight" > <item android:id="@android:id/mask"> <color android:color="@color/listview_pressed" /> </item> </ripple> 

Тем не менее, проблема связана с тем, что фокус застрял, и я не могу найти, как справиться с остальными недостающими функциями (9-patch, cardView, …).

Я думаю, что закрашивание цвета имеет какое-то отношение к использованию его как переднего плана.


EDIT: Я вижу, что некоторые люди не понимают, о чем здесь речь.

Речь идет о обработке нового эффекта пульсации, при этом у него есть старый селектор / CardView.

Например, вот селектор:

 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="..." android:state_selected="true"/> <item android:drawable="..." android:state_activated="true"/> <item android:drawable="..." android:state_focused="true" android:state_pressed="true"/> <item android:drawable="..." android:state_pressed="true"/> <item android:drawable="..."/> </selector> 

Это можно использовать как список-селектор или фон одного представления.

Тем не менее, я не могу найти, как использовать его вместе с тиражом.

Я знаю, что рябь уже заботится о некоторых из состояний, но для некоторых это не так. Кроме того, я не могу понять, как заставить его обрабатывать 9-патч и CardView.

Надеюсь, теперь легче понять проблему, которая у меня есть.


О проблеме цвета пульсации «застряли», я думаю, это из-за того, как я сделал макет. Мне нужен макет, который можно проверить (когда я решу), а также эффект нажатия, так что это то, что я сделал (на основе этого сайта и другого, которого я не могу найти):

 public class CheckableRelativeLayout extends RelativeLayout implements Checkable { private boolean mChecked; private static final String TAG = CheckableRelativeLayout.class.getCanonicalName(); private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked }; private Drawable mForegroundDrawable; public CheckableRelativeLayout(final Context context) { this(context, null, 0); } public CheckableRelativeLayout(final Context context, final AttributeSet attrs) { this(context, attrs, 0); } public CheckableRelativeLayout(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CheckableRelativeLayout, defStyle, 0); setForeground(a.getDrawable(R.styleable.CheckableRelativeLayout_foreground)); a.recycle(); } public void setForeground(final Drawable drawable) { this.mForegroundDrawable = drawable; } public Drawable getForeground() { return this.mForegroundDrawable; } @Override protected int[] onCreateDrawableState(final int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) { mergeDrawableStates(drawableState, CHECKED_STATE_SET); } return drawableState; } @Override protected void drawableStateChanged() { super.drawableStateChanged(); final Drawable drawable = getBackground(); boolean needRedraw = false; final int[] myDrawableState = getDrawableState(); if (drawable != null) { drawable.setState(myDrawableState); needRedraw = true; } if (mForegroundDrawable != null) { mForegroundDrawable.setState(myDrawableState); needRedraw = true; } if (needRedraw) invalidate(); } @Override protected void onSizeChanged(final int width, final int height, final int oldwidth, final int oldheight) { super.onSizeChanged(width, height, oldwidth, oldheight); if (mForegroundDrawable != null) mForegroundDrawable.setBounds(0, 0, width, height); } @Override protected void dispatchDraw(final Canvas canvas) { super.dispatchDraw(canvas); if (mForegroundDrawable != null) mForegroundDrawable.draw(canvas); } @Override public boolean isChecked() { return mChecked; } @Override public void setChecked(final boolean checked) { setChecked(checked, true); } public void setChecked(final boolean checked, final boolean alsoRecursively) { mChecked = checked; refreshDrawableState(); if (alsoRecursively) ViewUtil.setCheckedRecursively(this, checked); } @Override public void toggle() { setChecked(!mChecked); } @Override public Parcelable onSaveInstanceState() { // Force our ancestor class to save its state final Parcelable superState = super.onSaveInstanceState(); final SavedState savedState = new SavedState(superState); savedState.checked = isChecked(); return savedState; } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void drawableHotspotChanged(final float x, final float y) { super.drawableHotspotChanged(x, y); if (mForegroundDrawable != null) { mForegroundDrawable.setHotspot(x, y); } } @Override public void onRestoreInstanceState(final Parcelable state) { final SavedState savedState = (SavedState) state; super.onRestoreInstanceState(savedState.getSuperState()); setChecked(savedState.checked); requestLayout(); } // ///////////// // SavedState // // ///////////// private static class SavedState extends BaseSavedState { boolean checked; SavedState(final Parcelable superState) { super(superState); } private SavedState(final Parcel in) { super(in); checked = (Boolean) in.readValue(null); } @Override public void writeToParcel(final Parcel out, final int flags) { super.writeToParcel(out, flags); out.writeValue(checked); } @Override public String toString() { return TAG + ".SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " checked=" + checked + "}"; } @SuppressWarnings("unused") public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { @Override public SavedState createFromParcel(final Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(final int size) { return new SavedState[size]; } }; } } 

EDIT: исправление заключалось в том, чтобы добавить следующие строки для макета, который я сделал:

 @SuppressLint("ClickableViewAccessibility") @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public boolean onTouchEvent(final MotionEvent e) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && // e.getActionMasked() == MotionEvent.ACTION_DOWN && // mForeground != null) mForeground.setHotspot(e.getX(), e.getY()); return super.onTouchEvent(e); } 

Solutions Collecting From Web of "Как справиться с эффектом Ripple на 9-patch и CardView и управлять состоянием селектора?"

RippleDrawable расширяет LayerDrawable . Прикосновение обратной связи может содержать несколько дочерних слоев, включая специальный слой маски, который не нарисован на экране. Один слой может быть установлен как маска, указав его значение android:id качестве mask . Второй слой может быть StateListDrawable .

Например, вот наш ресурс StateListDrawable с именем item_selectable.xml :

 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="..." android:state_selected="true"/> <item android:drawable="..." android:state_activated="true"/> <item android:drawable="..." android:state_focused="true" android:state_pressed="true"/> <item android:drawable="..." android:state_pressed="true"/> <item android:drawable="..."/> </selector> 

Чтобы добиться эффекта пульсации вместе с селекторами, мы можем установить вышеприведенный уровень как слой RippleDrawable с именем list_selector_ripple.xml :

 <?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/colorControlHighlight"> <item android:id="@android:id/mask"> <color android:color="@android:color/white"/> </item> <item android:drawable="@drawable/item_selectable"/> </ripple> 

UPD:

1) Чтобы использовать эту возможность для CardView с помощью CardView просто установите ее как android:foreground , например:

 <android.support.v7.widget.CardView ... android:foreground="@drawable/list_selector_ripple" /> 

2) Чтобы эффект рябь работал в рамках 9-патча, мы должны установить этот 9-патч, который можно вытащить в качестве маски для list_selector_ripple_nine_patch.xml ( list_selector_ripple_nine_patch.xml ):

 <?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/colorControlHighlight"> <item android:id="@android:id/mask" android:drawable="@drawable/your_nine_patch" /> <item android:drawable="@drawable/your_nine_patch" /> </ripple> 

Затем установите фон вида:

 <LinearLayout ... android:background="@drawable/list_selector_ripple_nine_patch" /> 

Простой способ создать рябь сделайте xml в папке drawable-v21 и используйте этот код для xml.

 android:backgroung="@drawable/ripple_xyz" 

И если, через java / динамически использовать.

 View.setBackgroundResource(R.drawable.ripple_xyz); 

Вот ripple_xyz.xml.

 <?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="#228B22" > // ^ THIS IS THE COLOR FOR RIPPLE <item> <shape android:shape="rectangle" android:useLevel="false" > <solid android:color="#CCFFFFFF" /> // ^ THIS IS THE COLOR FOR BACK GROUND </shape> </item> 

Вы должны установить слой маски в рябь.

Что-то вроде этого:

 <?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <item android:id="@id/mask"> <color android:color="@color/myColor" /> </item> </ripple> 

Проверьте этот учебник. Эффект пульсации реализуется и работает отлично. Эффект пульсации на RecyclerView

Вы можете обрабатывать 9 патч-изображений с помощью кода ниже:

  <?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight" > <item android:id="@android:id/mask" android:drawable="@drawable/comment_background"> </item> </ripple> 

Где comment_background – это 9 патч-изображений