Подтвердить что ты не робот

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

Фон

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

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

Проблема

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

enter image description here

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

Код

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

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

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

Помимо простого вышеприведенного кода, я также попытался установить свойство selector на элемент фона вместо использования "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.

Например, здесь 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>

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

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

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

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


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

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);
}
4b9b3361

Ответ 1

RippleDrawable extends 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, просто установите его как android:foreground, например:

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

2) Чтобы эффект пульсации работал в границах 9-патча, мы должны установить этот 9-патч, который можно вытащить как маску масштабирования пульсации (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"
    />

Ответ 2

Простой способ создать рябь сделайте 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>

Ответ 3

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

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

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

Ответ 5

Вы можете обрабатывать 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 патч-изображений