Невозможно установить прослушиватель щелчка для кнопки, которая сначала находится за пределами экрана (а затем анимируется внутри) - PullRequest
0 голосов
/ 08 мая 2018

Я пытаюсь воссоздать поведение редактирования списка iOS в Android с помощью RecyclerView. Вот результат, который я хочу получить (без кнопки удаления):

iOS edit mode

Мне не нужна кнопка редактирования в этом контексте, и я предполагаю, что маленький красный кружок слева всегда есть. Когда вы нажимаете красную круглую кнопку, кнопка удаления прямоугольника должна сдвинуться вправо, сдвинув строку влево.

Я пытался сделать горизонтальный LinearLayout с двумя детьми. Одним из них является ConstraintLayout, который имеет основной макет каждой строки, а его ширина установлена ​​на match_parent, так что он занимает всю ширину экрана. Другой дочерний элемент - кнопка удаления шириной 130dp, которая по умолчанию будет вне экрана.

Вот код для файла макета:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clipChildren="false">

    <LinearLayout
        android:id="@+id/root_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:clipChildren="false">

        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="9dp"
            android:layout_marginTop="9dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <ImageButton
                android:id="@+id/remove_circle_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:visibility="visible"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/ic_remove_circle"
                android:background="@null"/>

            <TextView
                android:id="@+id/textView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:textSize="20sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toEndOf="@+id/remove_circle_button"
                app:layout_constraintTop_toTopOf="parent"
                tools:text="some text" />

        </android.support.constraint.ConstraintLayout>

        <Button
            android:id="@+id/remove_main_button"
            style="?android:borderlessButtonStyle"
            android:layout_width="130dp"
            android:layout_height="match_parent"
            android:background="@color/red"
            android:stateListAnimator="@null"
            android:text="Delete"
            android:textColor="@color/white" />

    </LinearLayout>

</FrameLayout>

Моя логика заключается в том, что когда пользователь нажимает круглую красную кнопку, линейный макет будет анимирован 130dp влево, делая красную кнопку видимой, в то время как остальная часть строки будет сдвинута влево. Визуально это выглядит достаточно хорошо для наших требований.

Вот код для адаптера RecyclerView, который я написал:

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {

    private Context mContext;
    private List<String> mStrings;
    private SparseBooleanArray mHasDeleteButtonPressed;

    public RecyclerViewAdapter(Context context, List<String> strings) {
        this.mContext = context;
        this.mStrings = strings;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View row = LayoutInflater.from(mContext).inflate(R.layout.rv_item, parent, false);
        return new ViewHolder(row);
    }

    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
        String value = mStrings.get(position);

        holder.textView.setText(value);
        holder.smallDeleteButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                holder.rootLayout.animate().translationX(
                        -1 * holder.mainDeleteButton.getWidth())
                        .setDuration(300)
                        .start();
                mHasDeleteButtonPressed.put(holder.getAdapterPosition(), false);
            }
        });

        holder.mainDeleteButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(mContext, "clicked", Toast.LENGTH_LONG).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return mStrings.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        LinearLayout rootLayout;
        TextView textView;
        ImageButton smallDeleteButton;
        Button mainDeleteButton;

        ViewHolder(View itemView) {
            super(itemView);
            rootLayout = (LinearLayout) itemView.findViewById(R.id.root_layout);
            textView = (TextView) itemView.findViewById(R.id.time_textView);
            smallDeleteButton = (ImageButton) itemView.findViewById(R.id.remove_circle_button);
            mainDeleteButton = (Button) itemView.findViewById(R.id.remove_main_button);
        }
    }
}

Логический массив должен знать, какая кнопка каждой строки была нажата, чтобы мы могли анимировать кнопку удаления, если пользователь решил, что не хочет удалять эту строку.

Теоретически, с этим кодом следует нажать кнопку удаления и показать тост, указывающий, что кнопка была нажата. Однако, похоже, что OnClickListener не устанавливается вообще и не вызывается код внутри метода onClick. Я подозреваю, что, поскольку кнопка поначалу находится за пределами экрана, и мы заставляем представление не обрезать ее (используя android:clipChildren="false" в файле XML), метод setOnClickListener не работает. Потому что, когда я переставляю макет так, чтобы кнопка «Удалить» находилась внутри экрана, приведенный выше код адаптера будет работать без каких-либо изменений.

Так как я могу исправить мою проблему? Я не хочу использовать какие-либо внешние библиотеки, и я хочу сделать это в RecyclerView.

Заранее спасибо

1 Ответ

0 голосов
/ 09 мая 2018

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

Мое решение состоит в том, чтобы сначала упростить расположение элементов RecyclerView следующим образом:

activity_main.xml

Я изменил несколько вещей, таких как цвет кнопки и т. Д., Для собственного удобства использования, но они не имеют отношения к решению.

<android.support.constraint.ConstraintLayout 
    android:id="@+id/root_layout"
    android:layout_width="match_parent"
    android:layout_height="70dp"
    android:layout_marginBottom="9dp"
    android:layout_marginTop="9dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <ImageButton
        android:id="@+id/remove_circle_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:background="@null"
        android:visibility="visible"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/circle_button" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="some text"
        android:textColor="@color/colorPrimary"
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@+id/remove_circle_button"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/remove_main_button"
        style="?android:borderlessButtonStyle"
        android:layout_width="130dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_blue_light"
        android:stateListAnimator="@null"
        android:text="Delete"
        android:textColor="@android:color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

Этот макет покажет кнопку на экране. В упражнении макету будет присвоена указанная ширина, равная ширине экрана плюс ширина кнопки. Это изменение размера макета переместит кнопку за пределы экрана. Затем код может выполнить перевод, чтобы переместить его обратно на экран.

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private View rootLayout;
    private View mainDeleteButton;
    private View circleButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        rootLayout = findViewById(R.id.root_layout);
        mainDeleteButton = findViewById(R.id.remove_main_button);
        circleButton = findViewById(R.id.remove_circle_button);
        rootLayout.post(new Runnable() {
            @Override
            public void run() {
                rootLayout.getLayoutParams().width = rootLayout.getWidth() + mainDeleteButton.getWidth();
                rootLayout.requestLayout();
            }
        });
        circleButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                rootLayout.animate().translationX(
                    -1 * mainDeleteButton.getWidth())
                    .setDuration(300)
                    .start();
            }
        });

        mainDeleteButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "clicked", Toast.LENGTH_SHORT).show();
            }
        });
    }
} 

Вот эффект:

enter image description here

Хотя этот пример не включает RecyclerView, метод можно применить к RecyclerView.

...