Je travaille sur un projet où je veux ajouter une transition View
comme ci-dessous. Je n'ai aucune idée par où commencer, quelqu'un peut-il m'aider?Comment concevoir une transition de vue comme ça dans android
Répondre
Il y a plusieurs façons dont vous pouvez réaliser ce type d'effet, aucune idée de comment cette application de vocabulaire est la manipulation, mais un joli moyen facile d'obtenir un effet similaire est d'utiliser plusieurs types RecyclerView.ViewHolder
puis laissez DefaultItemAnimator
prendre soin du travail d'animation. Voici une façon d'aller à ce sujet:
Modèle
Notre modèle va contenir les données que nous sommes d'affichage ainsi qu'un type
pour informer notre RecyclerView.Adapter
qui ReclerView.ViewHolder
à gonfler. Ainsi, il pourrait ressembler à ceci (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
Nous pouvons définir une ReclerView.ViewHolder
de base qui lie certains ExpandableModel
données ainsi que nous fournir une belle rappel 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);
}
}
ExpandedViewHolder
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()));
}
}
mise en page de 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 mise en page
<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>
Adaptateur
Maintenant, nous pouvons créer notre RecyclerView.Adapter
. Fondamentalement, chaque fois qu'un élément est cliqué, nous allons le remplacer par un TYPE_EXPANDED
ou TYPE_COLLAPSED
ExpandableModel
and because DefaultItemAnimator
is already applied to RecyclerView
, appelant RecyclerView.Adapter.notifyItemChanged
va bien animer entre les deux types de 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;
}
}
données fictives
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));
supplémentaires
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();
}
}
Résultats (video)
vous êtes un épargnant de vie ..... –
Qu'est-ce que vous avez essayé jusqu'à présent? –
Je viens de créer les trois premières vues personnalisées .... comme je l'ai dit, je n'ai aucune idée de comment puis-je passer de la première mise en page à la deuxième mise en page avec cette transition de vue lisse. –
Essayez aussi ces - https://github.com/wasabeef/awesome-android-ui – SanVed