[在底部编辑]
我正在尝试手动编码这些类型的动画:
如果仔细查看这些视图,它们属于List或RecyclerView,但它们是父边界内的动画(大小动画,翻译动画)。
如果我尝试这样做,结果就是我的观点属于我父母的界限。
https://drive.google.com/file/d/0B-V0KHNRjbE_bkJEekExNGNLbDA/view?usp=sharing
这是一个框架,小心地停止只是为了看到子视图是从父视图中获取的,并且正在开始扩展到整个视图:
这是几乎100%扩展的地方:
我只想以另一种方式重新指出这一点。这是与Activity Transitions有关的东西吗?因为如果是这样,我不知道该怎么做。
答案 0 :(得分:1)
我可以想到两种方法来实现这种效果:
一种方法是使用共享元素活动转换。它将需要使用2个活动:一个具有回收视图,第二个具有全屏视图。动画将在活动一和活动二之间切换时自动应用。此解决方案将起作用并且不需要太多代码,但是您将遇到使两个活动保持同步的问题(例如RecyclerView的确切位置)。定制并非不可能,但由于您严重依赖框架,因此可能很难。
第二种方式是,保持在同一活动中并使用对象动画师在您的回收者视图项和全屏视图之间进行转换。诀窍不是为位于RecyclerView内部的视图设置动画,而是从位于RecyclerView内部的视图边界设置全屏视图的动画。这样,您就不会受到父母边界的限制。我继续实施第二个解决方案,因为它是高度可定制的,让您可以完全控制所有动画。
此示例应用程序包括翻译和缩放动画师。它将从屏幕左侧的小方块位置视图进行动画处理。这种行为很容易改变。
演示:https://dl.dropboxusercontent.com/u/87080012/device-2016-03-25-160611.mp4
链接到项目仓库:https://dkarmazi@bitbucket.org/dkarmazi/androidrecyclerviewanimation.git
活动
public class MainActivity extends AppCompatActivity implements Adapter.ItemClickListener, CustomView.CloseButtonClickListener {
public static final int ANIMATION_SPEED = 3000;
private RecyclerView recyclerView;
private CustomView customView;
private RelativeLayout rootView;
private Rect lastClickedRecyclerViewItemRect;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rootView = (RelativeLayout) findViewById(R.id.root_view);
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
customView = (CustomView) findViewById(R.id.custom_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
recyclerView.setAdapter(new Adapter(getApplicationContext(), this, getSampleData()));
}
@Override
public void onItemClicked(View clickedView, int position, String title) {
lastClickedRecyclerViewItemRect = new Rect();
clickedView.getGlobalVisibleRect(lastClickedRecyclerViewItemRect);
Rect targetViewRect = new Rect();
rootView.getGlobalVisibleRect(targetViewRect);
AnimatorSet animatorSet = getViewToViewScalingAnimator(rootView, customView, lastClickedRecyclerViewItemRect, targetViewRect, ANIMATION_SPEED, 0);
customView.setData(position, title, this);
customView.setVisibility(View.VISIBLE);
animatorSet.start();
}
@Override
public void onCloseButtonClicked(int position) {
Rect clickedViewRect = new Rect();
customView.getGlobalVisibleRect(clickedViewRect);
AnimatorSet animatorSet = getViewToViewScalingAnimator(rootView, customView, clickedViewRect, lastClickedRecyclerViewItemRect, ANIMATION_SPEED, 0);
animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
// no op
}
@Override
public void onAnimationEnd(Animator animation) {
customView.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animation) {
// no op
}
@Override
public void onAnimationRepeat(Animator animation) {
// no op
}
});
animatorSet.start();
}
public static AnimatorSet getViewToViewScalingAnimator(final RelativeLayout parentView,
final View viewToAnimate,
final Rect fromViewRect,
final Rect toViewRect,
final long duration,
final long startDelay) {
// get all coordinates at once
final Rect parentViewRect = new Rect(), viewToAnimateRect = new Rect();
parentView.getGlobalVisibleRect(parentViewRect);
viewToAnimate.getGlobalVisibleRect(viewToAnimateRect);
viewToAnimate.setScaleX(1f);
viewToAnimate.setScaleY(1f);
// rescaling of the object on X-axis
final ValueAnimator valueAnimatorWidth = ValueAnimator.ofInt(fromViewRect.width(), toViewRect.width());
valueAnimatorWidth.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// Get animated width value update
int newWidth = (int) valueAnimatorWidth.getAnimatedValue();
// Get and update LayoutParams of the animated view
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) viewToAnimate.getLayoutParams();
lp.width = newWidth;
viewToAnimate.setLayoutParams(lp);
}
});
// rescaling of the object on Y-axis
final ValueAnimator valueAnimatorHeight = ValueAnimator.ofInt(fromViewRect.height(), toViewRect.height());
valueAnimatorHeight.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// Get animated width value update
int newHeight = (int) valueAnimatorHeight.getAnimatedValue();
// Get and update LayoutParams of the animated view
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) viewToAnimate.getLayoutParams();
lp.height = newHeight;
viewToAnimate.setLayoutParams(lp);
}
});
// moving of the object on X-axis
ObjectAnimator translateAnimatorX = ObjectAnimator.ofFloat(viewToAnimate, "X", fromViewRect.left - parentViewRect.left, toViewRect.left - parentViewRect.left);
// moving of the object on Y-axis
ObjectAnimator translateAnimatorY = ObjectAnimator.ofFloat(viewToAnimate, "Y", fromViewRect.top - parentViewRect.top, toViewRect.top - parentViewRect.top);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setInterpolator(new DecelerateInterpolator(1f));
animatorSet.setDuration(duration); // can be decoupled for each animator separately
animatorSet.setStartDelay(startDelay); // can be decoupled for each animator separately
animatorSet.playTogether(valueAnimatorWidth, valueAnimatorHeight, translateAnimatorX, translateAnimatorY);
return animatorSet;
}
private static List<String> getSampleData() {
List<String> dataList = new ArrayList<>();
dataList.add("zero");
dataList.add("one");
dataList.add("two");
dataList.add("three");
dataList.add("four");
dataList.add("five");
dataList.add("six");
dataList.add("seven");
dataList.add("eight");
dataList.add("nine");
dataList.add("ten");
dataList.add("eleven");
dataList.add("twelve");
dataList.add("thirteen");
dataList.add("fourteen");
dataList.add("fifteen");
dataList.add("sixteen");
dataList.add("seventeen");
dataList.add("eighteen");
dataList.add("nineteen");
dataList.add("twenty");
return dataList;
}
}
活动布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"/>
<com.dkarmazi.android.myapplication.CustomView
android:id="@+id/custom_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
</RelativeLayout>
将全屏显示的自定义视图
public class CustomView extends FrameLayout {
public interface CloseButtonClickListener {
void onCloseButtonClicked(int position);
}
private TextView positionView;
private TextView titleView;
private View closeView;
private CloseButtonClickListener closeButtonClickListener;
private int position;
public CustomView(Context context) {
super(context);
init();
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
inflate(getContext(), R.layout.custom_view, this);
positionView = (TextView) findViewById(R.id.custom_view_position);
titleView = (TextView) findViewById(R.id.custom_view_title);
closeView = findViewById(R.id.custom_view_close_button);
closeView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(closeButtonClickListener != null) {
closeButtonClickListener.onCloseButtonClicked(position);
}
}
});
}
public void setData(int position, String title, CloseButtonClickListener closeButtonClickListener) {
this.position = position;
this.positionView.setText("" + position);
this.titleView.setText(title);
this.closeButtonClickListener = closeButtonClickListener;
}
}
自定义视图的布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_red_dark">
<ImageView
android:id="@+id/custom_view_close_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_menu_close_clear_cancel"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:layout_marginTop="50dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="20sp"
android:gravity="center"
android:layout_gravity="top"
android:text="Position:" />
<TextView
android:id="@+id/custom_view_position"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="25sp"
android:gravity="center"
android:layout_gravity="top"
android:paddingBottom="100dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="20sp"
android:gravity="center"
android:layout_gravity="top"
android:text="Title:" />
<TextView
android:id="@+id/custom_view_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:gravity="center"
android:textSize="25sp"
android:layout_gravity="center"/>
</LinearLayout>
</RelativeLayout>
RecyclerView适配器
public class Adapter extends RecyclerView.Adapter {
public interface ItemClickListener {
void onItemClicked(View v, int position, String title);
}
private Context context;
private ItemClickListener itemClickListener;
private List<String> dataList;
public Adapter(Context context, ItemClickListener itemClickListener, List<String> dataList) {
this.context = context;
this.itemClickListener = itemClickListener;
this.dataList = dataList;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.recycler_view_item, null, false);
return new MyViewHolder(view, new OnRecyclerItemClickListener());
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((MyViewHolder) holder).onRecyclerItemClickListener.updatePosition(position);
((MyViewHolder) holder).position.setText("" + position);
((MyViewHolder) holder).title.setText(dataList.get(position));
}
@Override
public int getItemCount() {
return dataList.size();
}
private class MyViewHolder extends RecyclerView.ViewHolder {
private OnRecyclerItemClickListener onRecyclerItemClickListener;
private TextView position;
private TextView title;
public MyViewHolder(View itemView, OnRecyclerItemClickListener onRecyclerItemClickListener) {
super(itemView);
itemView.setOnClickListener(onRecyclerItemClickListener);
this.onRecyclerItemClickListener = onRecyclerItemClickListener;
this.position = (TextView) itemView.findViewById(R.id.position);
this.title = (TextView) itemView.findViewById(R.id.title);
}
}
private class OnRecyclerItemClickListener implements View.OnClickListener {
private int position = -1;
public void updatePosition(int position) {
this.position = position;
}
@Override
public void onClick(View v) {
if(itemClickListener != null) {
itemClickListener.onItemClicked(v.findViewById(R.id.position), position, dataList.get(position));
}
}
}
}
Recycler视图项目布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp">
<TextView
android:id="@+id/position"
android:layout_width="30dp"
android:layout_height="50dp"
android:textColor="@android:color/white"
android:gravity="center"
android:background="@android:color/holo_green_light"
android:layout_alignParentLeft="true"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textColor="@android:color/white"
android:gravity="center"
android:background="@android:color/holo_green_dark"
android:layout_toRightOf="@id/position"
android:layout_alignParentRight="true"/>
</RelativeLayout>