android android-listview android-5.0-lollipop rippledrawable

android - ¿Cómo manejar el efecto Ripple en 9-patch y CardView, y tener control sobre los estados del selector?



android-listview android-5.0-lollipop (5)

Mira este tutorial efecto dominó es implementar y funciona bien. Efecto Ripple en RecyclerView

Fondo

Deseo agregar un efecto de onda simple para los artículos listView de Android Lollipop y superiores.

Primero me gustaría configurarlo para filas simples, y luego para filas de 9 parches e incluso CardView.

El problema

Estaba seguro de que este sería muy fácil, ya que ni siquiera necesito que defina el selector normal. No lo hice incluso para filas simples. Por alguna razón, el efecto dominó va más allá de los límites de la fila:

No solo eso, sino que en algunos casos, el fondo del elemento se atasca en el color que he configurado.

El código

Esto es lo que he intentado:

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 (tengo un selector normal para otras versiones de Android)

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

Lo que he intentado

Aparte del simple código anterior, también he intentado configurar el selector por propiedad de fondo del elemento, en lugar de usar "listSelector" en el ListView, pero no ayudó.

Otra cosa que he intentado es establecer el primer plano de los elementos, pero también tuvo el mismo resultado.

La pregunta

¿Cómo soluciono este problema? ¿Por qué ocurre? ¿Qué hice mal?

¿Cómo voy más allá, para soportar 9 parches e incluso CardView?

Además, ¿cómo puedo establecer un estado para el nuevo fondo, como ser marcado / seleccionado?

Actualización: el dibujo fuera de la vista se arregla usando algo como esto:

<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>

Aún así, tiene el problema de que el fondo está atascado, y no puedo encontrar cómo manejar el resto de las características que faltan (9-patch, cardView, ...).

Creo que el color-estar-atascado tiene algo que ver con usarlo como primer plano de las vistas.

EDITAR: veo que algunas personas no entienden de qué se trata la pregunta aquí.

Se trata de manejar el nuevo efecto dominó, sin dejar de tener el selector anterior / CardView.

Por ejemplo, aquí hay un selector-drawble:

<?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>

Esto se puede usar como un selector de lista o un fondo de una sola vista.

Sin embargo, no puedo encontrar cómo usarlo junto con la ondulación dibujable.

Sé que la onda ya se ocupa de algunos estados, pero para algunos no es así. Además, no puedo encontrar la manera de hacerlo funcionar con 9 parches y CardView.

Espero que ahora sea más fácil entender el problema que tengo.

Sobre el tema del color de la onda se "atascó", creo que es por cómo hice el diseño. Quería un diseño que pudiera verificarse (cuando lo decidiera) y también tener el efecto de hacer clic, así que esto es lo que hice (basado en este sitio web y otro que no puedo encontrar):

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: la solución fue agregar las siguientes líneas para el diseño que hice:

@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); }


Puede manejar 9 imágenes de parche por algo como el siguiente código:

<?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>

donde comment_background es una imagen de 9 parches


Tienes que establecer una capa de máscara en la onda.

Algo como esto:

<?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>


Una forma simple de crear una onda forma un xml en la carpeta drawable-v21 y usa este código para xml.

android:backgroung="@drawable/ripple_xyz"

Y si, a través de java / uso dinámico.

View.setBackgroundResource(R.drawable.ripple_xyz);

Aquí está el 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>


RippleDrawable extiende LayerDrawable . La información táctil extraíble puede contener varias capas secundarias, incluida una capa de máscara especial que no se dibuja en la pantalla. Se puede establecer una sola capa como máscara especificando su valor android:id como mask . La segunda capa puede ser StateListDrawable .

Por ejemplo, aquí está nuestro recurso StateListDrawable con el nombre 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>

Para lograr el efecto dominó junto con los selectores, podemos configurar dibujar arriba como una capa de RippleDrawable con el nombre 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) Para usar este CardView con CardView simplemente CardView como android:foreground , así:

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

2) Para hacer que el efecto dominó funcione dentro de los límites del parche 9, deberíamos establecer este parche de 9 paréntesis como máscara de ondulación 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>

Luego establece el fondo de la vista:

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