android - 如何在android中设计这样的 View 转换

标签 android android-recyclerview android-animation android-view android-transitions

我正在开发一个项目,我想添加一个 View 过渡,如下所示。我不知道从哪里开始,有人可以帮助我吗?

最佳答案

有几种方法可以实现这种类型的效果,不知道词汇应用程序如何处理它,但是获得非常相似的效果的一个非常简单的方法是使用多个 RecyclerView.ViewHolder类型,然后让 DefaultItemAnimator照顾动画工作。这是解决此问题的一种方法:

型号

我们的模型将包含我们显示的数据以及类型来通知我们的 RecyclerView.Adapter其中 ReclerView.ViewHolder 进行充气。所以,它可能看起来像这样 (AutoValue) :

@AutoValue
public abstract class ExpandableModel {

    public static final int TYPE_STATIC = 0;
    public static final int TYPE_EXPANDED = 1;
    public static final int TYPE_COLLAPSED = 2;

    @Nullable public abstract List<ExpandableModel> data();
    public abstract String title();
    public abstract int progress();
    public abstract int max();
    public abstract int type();

    public static ExpandableModel createExpanded(List<ExpandableModel> data,
                                                 String title, int progress, int max) {
        return new AutoValue_ExpandableModel(data, title, progress, max, TYPE_EXPANDED);
    }

    public static ExpandableModel createCollapsed(List<ExpandableModel> data,
                                                  String title, int progress, int max) {
        return new AutoValue_ExpandableModel(data, title, progress, max, TYPE_COLLAPSED);
    }

    public static ExpandableModel createExpanded(ExpandableModel model) {
        return new AutoValue_ExpandableModel(
                model.data(), model.title(), model.progress(), model.max(), TYPE_EXPANDED);
    }

    public static ExpandableModel createCollapsed(ExpandableModel model) {
        return new AutoValue_ExpandableModel(
                model.data(), model.title(), model.progress(), model.max(), TYPE_COLLAPSED);
    }

    public static ExpandableModel createStatic(String title, int progress, int max) {
        return new AutoValue_ExpandableModel(null, title, progress, max, TYPE_STATIC);
    }

}

ViewHolder

我们可以定义一个基本的ReclerView.ViewHolder,它将绑定(bind)一些ExpandableModel数据,并为我们提供一个很好的OnClickListener回调。

public abstract class ExpandableViewHolder extends RecyclerView.ViewHolder {

    public ExpandableViewHolder(ViewGroup parent, int layout) {
        super(LayoutInflater.from(parent.getContext()).inflate(layout, parent, false));
    }

    public void setItemClickListener(OnItemClickListener clickListener) {
        itemView.setOnClickListener(v -> {
            final int adapterPosition = getAdapterPosition();
            if (adapterPosition != RecyclerView.NO_POSITION) {
                clickListener.onItemClick(itemView, adapterPosition);
            }
        });
    }

    public abstract void bind(ExpandableModel model);

    public interface OnItemClickListener {
        void onItemClick(View itemView, int position);
    }

}

扩展 View 持有者

public class ExpandedViewHolder extends ExpandableViewHolder {

    private final TextView title;
    private final TextView completion;
    private final ProgressBar progress;
    private final RecyclerView recycler;

    public ExpandedViewHolder(ViewGroup parent) {
        super(parent, R.layout.adapter_view_expanded);
        title = itemView.findViewById(R.id.expanded_category);
        completion = itemView.findViewById(R.id.expanded_completion);
        progress = itemView.findViewById(R.id.expanded_progress);
        recycler = itemView.findViewById(R.id.expanded_recycler);
        recycler.addItemDecoration(new SpaceItemDecoration(10));
    }

    @Override
    public void bind(ExpandableModel model) {
        title.setText(model.title());
        completion.setText(model.progress() + "/" + model.max());
        progress.setMax(model.max());
        progress.setProgress(model.progress());
        recycler.setAdapter(new ExpandableAdapter(model.data()));
    }

}

ExpandedViewHolder 布局

<LinearLayout 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="350dp"
    android:background="#ffFFC857"
    android:orientation="vertical">

    <TextView
        android:id="@+id/expanded_category"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:gravity="center"
        android:textColor="#ffffffff"
        android:textIsSelectable="false"
        android:textSize="28sp"
        tools:text="Basic Words" />

    <TextView
        android:id="@+id/expanded_completion"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:gravity="center"
        android:textColor="#ffffffff"
        android:textIsSelectable="false"
        android:textSize="18sp"
        tools:text="174/174 mastered" />

    <ProgressBar
        android:id="@+id/expanded_progress"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        tools:progress="100" />

    <android.support.v4.widget.Space
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/expanded_recycler"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginBottom="4dp"
        android:orientation="horizontal"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager" />

</LinearLayout>

CollapsedViewHolder

public class CollapsedViewHolder extends ExpandableViewHolder {

    private final TextView title;
    private final TextView completion;
    private final ProgressBar progress;

    public CollapsedViewHolder(ViewGroup parent) {
        super(parent, R.layout.adapter_view_collapsed);
        title = itemView.findViewById(R.id.collapsed_category);
        completion = itemView.findViewById(R.id.collapsed_completion);
        progress = itemView.findViewById(R.id.collapsed_progress);
    }

    @Override
    public void bind(ExpandableModel model) {
        title.setText(model.title());
        completion.setText(model.progress() + "/" + model.max());
        progress.setMax(model.max());
        progress.setProgress(model.progress());
    }

}

CollapsedViewHolder 布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="165dp"
    android:layout_margin="4dp"
    android:background="#ffffffff"
    android:orientation="vertical">

    <TextView
        android:id="@+id/collapsed_category"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:gravity="center"
        android:textColor="#ff066FA5"
        android:textIsSelectable="false"
        android:textSize="28sp"
        tools:text="Basic Words" />

    <TextView
        android:id="@+id/collapsed_completion"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:gravity="center"
        android:textColor="#ffAEB8C3"
        android:textIsSelectable="false"
        android:textSize="18sp"
        tools:text="174/174 mastered" />

    <ProgressBar
        android:id="@+id/collapsed_progress"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        tools:progress="100" />

</LinearLayout>

适配器

现在我们可以创建我们的RecyclerView.Adapter。基本上,每当单击某个项目时,我们都会将其替换为 TYPE_EXPANDEDTYPE_COLLAPSED ExpandableModel and because DefaultItemAnimator is already applied to RecyclerView ,调用RecyclerView.Adapter.notifyItemChanged将在两种类型的 RecyclerView.ViewHolder 之间很好地进行动画处理。

public class ExpandableAdapter extends RecyclerView.Adapter<ExpandableViewHolder> {

    private final List<ExpandableModel> data = new ArrayList<>(0);

    private int expandedPosition;

    public ExpandableAdapter(Collection<ExpandableModel> data) {
        this.data.addAll(data);
    }

    @Override
    public ExpandableViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case TYPE_EXPANDED:
                final ExpandedViewHolder expandedHolder = new ExpandedViewHolder(parent);
                expandedHolder.setItemClickListener((itemView, position) -> collapse(position));
                return expandedHolder;
            case TYPE_COLLAPSED:
                final CollapsedViewHolder collapsedHolder = new CollapsedViewHolder(parent);
                collapsedHolder.setItemClickListener((itemView, position) -> {
                    collapseCurrent();
                    expand(position);
                });
                return collapsedHolder;
            case TYPE_STATIC:
                final CollapsedViewHolder staticHolder = new CollapsedViewHolder(parent);
                staticHolder.setItemClickListener((itemView, position) -> {
                    final ExpandableModel model = data.get(position);
                    Snackbar.make(itemView, model.title(), Snackbar.LENGTH_SHORT).show();
                });
                return staticHolder;
            default:
                throw new IllegalArgumentException("unknown type");
        }
    }

    @Override
    public void onBindViewHolder(ExpandableViewHolder holder, int position) {
        holder.bind(data.get(holder.getAdapterPosition()));
    }

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

    @Override
    public int getItemViewType(int position) {
        return data.get(position).type();
    }

    private void collapseCurrent() {
        final ExpandableModel curr = data.get(expandedPosition);
        data.set(expandedPosition, ExpandableModel.createCollapsed(curr));
        notifyItemChanged(expandedPosition);
    }

    private void collapse(int position) {
        final ExpandableModel curr = data.get(position);
        data.set(position, ExpandableModel.createCollapsed(curr));
        notifyItemChanged(position);
    }

    private void expand(int position) {
        final ExpandableModel curr = data.get(position);
        data.set(position, ExpandableModel.createExpanded(curr));
        notifyItemChanged(position);
        expandedPosition = position;
    }

}

虚拟数据

    final Random ran = new SecureRandom();

    final List<ExpandableModel> basic = new ArrayList<>(0);
    for (int i = 0; i < 10; i++) {
        final int max = 10;
        final int progress = ran.nextInt(max + 1);
        final String title = ("Basic Words: " + (i + 1));
        basic.add(ExpandableModel.createStatic(title, progress, max));
    }

    final List<ExpandableModel> intermediate = new ArrayList<>(0);
    for (int i = 0; i < 10; i++) {
        final int max = 10;
        final int progress = ran.nextInt(max + 1);
        final String title = ("Intermediate Words: " + (i + 1));
        intermediate.add(ExpandableModel.createStatic(title, progress, max));
    }

    final List<ExpandableModel> advanced = new ArrayList<>(0);
    for (int i = 0; i < 10; i++) {
        final int max = 10;
        final int progress = ran.nextInt(max + 1);
        final String title = ("Advanced Words: " + (i + 1));
        advanced.add(ExpandableModel.createStatic(title, progress, max));
    }

    final List<ExpandableModel> data = new ArrayList<>(0);
    data.add(ExpandableModel.createCollapsed(basic, "Basic Words", 7, 10));
    data.add(ExpandableModel.createCollapsed(intermediate, "Intermediate Words", 5, 10));
    data.add(ExpandableModel.createCollapsed(advanced, "Advanced Words", 3, 10));

    final RecyclerView recycler = findViewById(android.R.id.list);
    recycler.setAdapter(new ExpandableAdapter(data));

额外

public class SpaceItemDecoration extends RecyclerView.ItemDecoration {

    private final int space;

    public SpaceItemDecoration(int space) {
        this.space = space;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view,
                               RecyclerView parent, RecyclerView.State state) {
        final int childPosition = parent.getChildLayoutPosition(view);
        if (childPosition == RecyclerView.NO_POSITION) {
            return;
        }
        if (childPosition < 1 || childPosition >= 1) {
            outRect.left = space;
        }
        if (childPosition == getTotalItemCount(parent) - 1) {
            outRect.right = space;
        }
    }

    private static int getTotalItemCount(RecyclerView parent) {
        return parent.getAdapter().getItemCount();
    }

}

结果 ( video )

关于android - 如何在android中设计这样的 View 转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46336420/

相关文章:

android 将媒体数据传递给另一个 Activity

android - 我的RecyclerView没有在通知方法上更新

java - Android:RecyclerView.SetAdapter 导致应用程序崩溃,但没有明显错误

java - 制作一个翻转的Android动画

Android View 展开动画

android - 在 Android 中使用 Intent 时将变量传递给 An Activity

java - 为 Android 构建电话间隙应用程序失败

android - 相对于父 View 定位 subview

java - RecyclerView 中的 Admob 原生广告

java - 异步任务 : invalidating view does not take effect