What I want is that when the user clicks a list item in a ListView, it converts to a whole activity (as you can see in the following example), but I was not able to find a tutorial explaining this and, actually, I do not know how this movement is called.
我想要的是,当用户单击ListView中的列表项时,它将转换为整个活动(如下例所示),但我无法找到解释此内容的教程,实际上,我做了不知道这个运动是怎么称呼的。
In other words, what I want to achieve is:
换句话说,我想要实现的目标是:
-
Increase List Item elevation when it is clicked (as you can see in the right gif)
单击时增加列表项高程(如右图所示)
-
Expand and transform list item to the next fragment/activity layout that contains detailed information about the clicked item
展开列表项并将其转换为下一个片段/活动布局,其中包含有关所单击项的详细信息
I have tried a lot of transitions but with no luck. Can anyone help me out to accomplish this?
我尝试了很多过渡但没有运气。任何人都可以帮我完成这个吗?
4 个解决方案
#1
17
I build a small sample application that transitions between two activities with the desired effect:
我构建了一个小样本应用程序,它在两个活动之间转换,具有所需的效果
However the transitions in the provided gifs are slightly different. The transition in the gif on the left side transitions the list element into the content area of the second activity (Toolbar stays in place). In the gif on the right side the transition transforms the list element into the complete screen of the second activity. The following code provides the effect in the left gif. However it should be possible to adapt the solution with minor modifications to achieve the transition in the right gif.
然而,提供的GIF的过渡略有不同。左侧的gif中的转换将列表元素转换为第二个活动的内容区域(工具栏保持原位)。在右侧的gif中,转换将列表元素转换为第二个活动的完整屏幕。以下代码在左侧gif中提供了效果。但是,应该可以通过微小的修改来调整解决方案,以实现正确的gif过渡。
Note this only works on Lollipop. However it is possible to mock a different effect on older devices. Furthermore the sole purpose of the provided code is to show how it could be done. Don't use this directly in your app.
请注意,这仅适用于棒棒糖。但是,可以在旧设备上模拟不同的效果。此外,所提供代码的唯一目的是展示如何完成。请勿在您的应用中直接使用此功能。
MainActivity:
public class MainActivity extends AppCompatActivity {
MyAdapter myAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
ListView listView = (ListView) findViewById(R.id.list_view);
myAdapter = new MyAdapter(this, 0, DataSet.get());
listView.setAdapter(myAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, final View view, final int position, long id) {
startTransition(view, myAdapter.getItem(position));
}
});
}
private void startTransition(View view, Element element) {
Intent i = new Intent(MainActivity.this, DetailActivity.class);
i.putExtra("ITEM_ID", element.getId());
Pair<View, String>[] transitionPairs = new Pair[4];
transitionPairs[0] = Pair.create(findViewById(R.id.toolbar), "toolbar"); // Transition the Toolbar
transitionPairs[1] = Pair.create(view, "content_area"); // Transition the content_area (This will be the content area on the detail screen)
// We also want to transition the status and navigation bar barckground. Otherwise they will flicker
transitionPairs[2] = Pair.create(findViewById(android.R.id.statusBarBackground), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME);
transitionPairs[3] = Pair.create(findViewById(android.R.id.navigationBarBackground), Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
Bundle b = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, transitionPairs).toBundle();
ActivityCompat.startActivity(MainActivity.this, i, b);
}
}
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:transitionName="toolbar" />
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
DetailActivity:
public class DetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
long elementId = getIntent().getLongExtra("ITEM_ID", -1);
Element element = DataSet.find(elementId);
((TextView) findViewById(R.id.title)).setText(element.getTitle());
((TextView) findViewById(R.id.description)).setText(element.getDescription());
// if we transition the status and navigation bar we have to wait till everything is available
TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar(this);
// set a custom shared element enter transition
TransitionHelper.setSharedElementEnterTransition(this, R.transition.detail_activity_shared_element_enter_transition);
}
}
activity_detail.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:transitionName="toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#abc"
android:orientation="vertical"
android:paddingBottom="200dp"
android:transitionName="content_area"
android:elevation="10dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
detail_activity_shared_element_enter_transition.xml (/res/transition/):
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="together">
<changeBounds/>
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
<transition class="my.application.transitions.ElevationTransition"/>
</transitionSet>
my.application.transitions.ElevationTransition:
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class ElevationTransition extends Transition {
private static final String PROPNAME_ELEVATION = "my.elevation:transition:elevation";
public ElevationTransition() {
}
public ElevationTransition(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void captureStartValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
private void captureValues(TransitionValues transitionValues) {
Float elevation = transitionValues.view.getElevation();
transitionValues.values.put(PROPNAME_ELEVATION, elevation);
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
Float startVal = (Float) startValues.values.get(PROPNAME_ELEVATION);
Float endVal = (Float) endValues.values.get(PROPNAME_ELEVATION);
if (startVal == null || endVal == null || startVal.floatValue() == endVal.floatValue()) {
return null;
}
final View view = endValues.view;
ValueAnimator a = ValueAnimator.ofFloat(startVal, endVal);
a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
view.setElevation((float)animation.getAnimatedValue());
}
});
return a;
}
}
TransitionHelper:
public class TransitionHelper {
public static void fixSharedElementTransitionForStatusAndNavigationBar(final Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return;
final View decor = activity.getWindow().getDecorView();
if (decor == null)
return;
activity.postponeEnterTransition();
decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onPreDraw() {
decor.getViewTreeObserver().removeOnPreDrawListener(this);
activity.startPostponedEnterTransition();
return true;
}
});
}
public static void setSharedElementEnterTransition(final Activity activity, int transition) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return;
activity.getWindow().setSharedElementEnterTransition(TransitionInflater.from(activity).inflateTransition(transition));
}
}
So what are the different parts here: We have two activities. During the transition four views are transitioned between the activities.
那么这里的不同部分是什么:我们有两个活动。在过渡期间,四个视图在活动之间转换。
-
Toolbar: like in the left gif the toolbar doesn't move with the rest of the content.
工具栏:就像在左边的gif中一样,工具栏不会与其他内容一起移动。
-
ListView element View -> becomes the content view of the DetailActivity
ListView元素视图 - >成为DetailActivity的内容视图
-
StatusBar and NavigationBar Background: If we don't add these views to the set of transitioned views they will fade out and back in during the transition. This however requires to delay the enter transition (see:
TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar
)StatusBar和NavigationBar背景:如果我们不将这些视图添加到转换视图集,它们将在转换期间淡出并重新进入。但是这需要延迟输入转换(请参阅:TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar)
In the MainActivity
the transitioned views are added to the Bundle that is used to start the DetailActivity
. Furthermore the transitioned views need to be named (transitionName
) in both activities. This can be done in the layout xml as well as programatically.
在MainActivity中,转换的视图将添加到用于启动DetailActivity的Bundle中。此外,转换后的视图需要在两个活动中命名(transitionName)。这可以在布局xml中以及以编程方式完成。
The default set of transitions, that is used during the shared element transition, affects different aspects of the view(for example: view bounds - see 2). However differences in the elevation of a view are not animated. This is why the presented solution utilizes the custom ElevationTransition.
在共享元素转换期间使用的默认转换集会影响视图的不同方面(例如:视图边界 - 请参阅2)。但是,视图高程的差异不是动画的。这就是为什么所提出的解决方案使用自定义ElevationTransition。
#2
3
try this.. Material-Animations
试试这个..材料 - 动画
blueIconImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, SharedElementActivity.class);
View sharedView = blueIconImageView;
String transitionName = getString(R.string.blue_name);
ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);
startActivity(i, transitionActivityOptions.toBundle());
}
});
#3
0
The Animation you need is called Activity Transitions between shared elements. By Research I found that you should:
您需要的动画称为共享元素之间的活动转换。通过研究我发现你应该:
- Put your ListView view in a relativeLayout
- OnClick, inflate a copy of your renderer
- Find the global coordinates for where the renderer sits in relationship to the parent of the ListView
- Add the copied renderer to the RelativeLayout (parent of ListView)
- Animate the listView away
- On the end of that animate, animate your new renderer
-
Profit!
public class MainActivity extends Activity { private RelativeLayout layout; private ListView listView; private MyRenderer selectedRenderer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); layout = new RelativeLayout(this); setContentView(layout); listView = new ListView(this); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); layout.addView(listView, rlp); listView.setAdapter(new MyAdapter()); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // find out where the clicked view sits in relationship to the // parent container int t = view.getTop() + listView.getTop(); int l = view.getLeft() + listView.getLeft(); // create a copy of the listview and add it to the parent // container // at the same location it was in the listview selectedRenderer = new MyRenderer(view.getContext()); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(view.getWidth(), view .getHeight()); rlp.topMargin = t; rlp.leftMargin = l; selectedRenderer.textView.setText(((MyRenderer) view).textView.getText()); layout.addView(selectedRenderer, rlp); view.setVisibility(View.INVISIBLE); // animate out the listView Animation outAni = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, -1f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f); outAni.setDuration(1000); outAni.setFillAfter(true); outAni.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { ScaleAnimation scaleAni = new ScaleAnimation(1f, 1f, 1f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); scaleAni.setDuration(400); scaleAni.setFillAfter(true); selectedRenderer.startAnimation(scaleAni); } }); listView.startAnimation(outAni); } }); } public class MyAdapter extends BaseAdapter { @Override public int getCount() { return 10; } @Override public String getItem(int position) { return "Hello World " + position; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { MyRenderer renderer; if (convertView != null) renderer = (MyRenderer) convertView; else renderer = new MyRenderer(MainActivity.this); renderer.textView.setText(getItem(position)); return renderer; } } public class MyRenderer extends RelativeLayout { public TextView textView; public MyRenderer(Context context) { super(context); setPadding(20, 20, 20, 20); setBackgroundColor(0xFFFF0000); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); rlp.addRule(CENTER_IN_PARENT); textView = new TextView(context); addView(textView, rlp); } } }
将ListView视图放在relativeLayout中
OnClick,为渲染器的副本充气
查找渲染器与ListView的父级相关的全局坐标
将复制的渲染器添加到RelativeLayout(ListView的父级)
为listView设置动画
在该动画的末尾,为新渲染器设置动画
利润!公共类MainActivity扩展Activity { 私有RelativeLayout布局; 私有ListView listView; 私人MyRenderer selectedRenderer; @覆盖 protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); layout = new RelativeLayout(this); 的setContentView(布局); listView = new ListView(this); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT,RelativeLayout.LayoutParams.MATCH_PARENT); layout.addView(listView,rlp); listView.setAdapter(new MyAdapter()); listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){ @覆盖 public void onItemClick(AdapterView parent,View视图,int position,long id){ //找出被点击的视图与...的关系 //父容器 int t = view.getTop()+ listView.getTop(); int l = view.getLeft()+ listView.getLeft(); //创建listview的副本并将其添加到父级 // 容器 //在listview中的同一位置 selectedRenderer = new MyRenderer(view.getContext()); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(view.getWidth(),view .getHeight()); rlp.topMargin = t; rlp.leftMargin = l; selectedRenderer.textView.setText(((MyRenderer)view).textView.getText()); layout.addView(selectedRenderer,rlp); view.setVisibility(View.INVISIBLE); //为listView设置动画 动画outAni = new TranslateAnimation(Animation.RELATIVE_TO_SELF,0f, Animation.RELATIVE_TO_SELF,-1f,Animation.RELATIVE_TO_SELF,0f, Animation.RELATIVE_TO_SELF,0f); outAni.setDuration(1000); outAni.setFillAfter(真); outAni.setAnimationListener(new Animation.AnimationListener(){ @覆盖 public void onAnimationStart(动画动画){ } @覆盖 public void onAnimationRepeat(动画动画){ } @覆盖 public void onAnimationEnd(动画动画){ ScaleAnimation scaleAni = new ScaleAnimation(1f, 1f,1f,2f, Animation.RELATIVE_TO_SELF,0.5f, Animation.RELATIVE_TO_SELF,0.5f); scaleAni.setDuration(400); scaleAni.setFillAfter(真); selectedRenderer.startAnimation(scaleAni); } }); listView.startAnimation(outAni); } }); } 公共类MyAdapter扩展BaseAdapter { @覆盖 public int getCount(){ 返回10; } @覆盖 public String getItem(int position){ 返回“Hello World”+位置; } @覆盖 public long getItemId(int position){ 返回位置; } @覆盖 public View getView(int position,View convertView,ViewGroup parent){ MyRenderer渲染器; if(convertView!= null) renderer =(MyRenderer)convertView; 其他 renderer = new MyRenderer(MainActivity.this); renderer.textView.setText(的getItem(位置)); 返回渲染器; } } 公共类MyRenderer扩展RelativeLayout { public TextView textView; public MyRenderer(Context context){ 超级(上下文); setPadding(20,20,20,20); setBackgroundColor(0xFFFF0000地址); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT); rlp.addRule(CENTER_IN_PARENT); textView = new TextView(context); addView(textView,rlp); } }}
#4
0
Try this spectacular webpage @ Getting Started with Activity & Fragment Transitions (part 1). Here they talked about Activity and Fragment Transitions. I have not tried it. My view is that Fragment Transitions is better and less computer intensive, so it's a good start. And you may not need to change Toolbars, you can show/hide them.
试试这个壮观的网页@活动和碎片过渡入门(第1部分)。在这里,他们谈到了活动和片段转换。我没试过。我认为Fragment Transitions更好,计算机密集度更低,所以这是一个好的开始。您可能不需要更改工具栏,您可以显示/隐藏它们。
Another good SO link is @ Animate the transition between fragments, look at the best answer. In that post, they talked about objectAnimator.
另一个好的SO链接是@Animate片段之间的过渡,看看最好的答案。在那篇文章中,他们谈到了objectAnimator。
Another opinion is about the sample animation you posted, it does not show a smooth animation from one art to another. It is less impressive when the animation is not smooth.
另一种观点是关于你发布的样本动画,它没有显示从一个艺术到另一个艺术的平滑动画。当动画不流畅时,它不那么令人印象深刻。
Good luck, have fun, keep us all posted.
祝你好运,玩得开心,让我们全部张贴。
#1
17
I build a small sample application that transitions between two activities with the desired effect:
我构建了一个小样本应用程序,它在两个活动之间转换,具有所需的效果
However the transitions in the provided gifs are slightly different. The transition in the gif on the left side transitions the list element into the content area of the second activity (Toolbar stays in place). In the gif on the right side the transition transforms the list element into the complete screen of the second activity. The following code provides the effect in the left gif. However it should be possible to adapt the solution with minor modifications to achieve the transition in the right gif.
然而,提供的GIF的过渡略有不同。左侧的gif中的转换将列表元素转换为第二个活动的内容区域(工具栏保持原位)。在右侧的gif中,转换将列表元素转换为第二个活动的完整屏幕。以下代码在左侧gif中提供了效果。但是,应该可以通过微小的修改来调整解决方案,以实现正确的gif过渡。
Note this only works on Lollipop. However it is possible to mock a different effect on older devices. Furthermore the sole purpose of the provided code is to show how it could be done. Don't use this directly in your app.
请注意,这仅适用于棒棒糖。但是,可以在旧设备上模拟不同的效果。此外,所提供代码的唯一目的是展示如何完成。请勿在您的应用中直接使用此功能。
MainActivity:
public class MainActivity extends AppCompatActivity {
MyAdapter myAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
ListView listView = (ListView) findViewById(R.id.list_view);
myAdapter = new MyAdapter(this, 0, DataSet.get());
listView.setAdapter(myAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, final View view, final int position, long id) {
startTransition(view, myAdapter.getItem(position));
}
});
}
private void startTransition(View view, Element element) {
Intent i = new Intent(MainActivity.this, DetailActivity.class);
i.putExtra("ITEM_ID", element.getId());
Pair<View, String>[] transitionPairs = new Pair[4];
transitionPairs[0] = Pair.create(findViewById(R.id.toolbar), "toolbar"); // Transition the Toolbar
transitionPairs[1] = Pair.create(view, "content_area"); // Transition the content_area (This will be the content area on the detail screen)
// We also want to transition the status and navigation bar barckground. Otherwise they will flicker
transitionPairs[2] = Pair.create(findViewById(android.R.id.statusBarBackground), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME);
transitionPairs[3] = Pair.create(findViewById(android.R.id.navigationBarBackground), Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME);
Bundle b = ActivityOptionsCompat.makeSceneTransitionAnimation(MainActivity.this, transitionPairs).toBundle();
ActivityCompat.startActivity(MainActivity.this, i, b);
}
}
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:transitionName="toolbar" />
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
DetailActivity:
public class DetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
long elementId = getIntent().getLongExtra("ITEM_ID", -1);
Element element = DataSet.find(elementId);
((TextView) findViewById(R.id.title)).setText(element.getTitle());
((TextView) findViewById(R.id.description)).setText(element.getDescription());
// if we transition the status and navigation bar we have to wait till everything is available
TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar(this);
// set a custom shared element enter transition
TransitionHelper.setSharedElementEnterTransition(this, R.transition.detail_activity_shared_element_enter_transition);
}
}
activity_detail.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:transitionName="toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#abc"
android:orientation="vertical"
android:paddingBottom="200dp"
android:transitionName="content_area"
android:elevation="10dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
detail_activity_shared_element_enter_transition.xml (/res/transition/):
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="together">
<changeBounds/>
<changeTransform/>
<changeClipBounds/>
<changeImageTransform/>
<transition class="my.application.transitions.ElevationTransition"/>
</transitionSet>
my.application.transitions.ElevationTransition:
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class ElevationTransition extends Transition {
private static final String PROPNAME_ELEVATION = "my.elevation:transition:elevation";
public ElevationTransition() {
}
public ElevationTransition(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void captureStartValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
private void captureValues(TransitionValues transitionValues) {
Float elevation = transitionValues.view.getElevation();
transitionValues.values.put(PROPNAME_ELEVATION, elevation);
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
Float startVal = (Float) startValues.values.get(PROPNAME_ELEVATION);
Float endVal = (Float) endValues.values.get(PROPNAME_ELEVATION);
if (startVal == null || endVal == null || startVal.floatValue() == endVal.floatValue()) {
return null;
}
final View view = endValues.view;
ValueAnimator a = ValueAnimator.ofFloat(startVal, endVal);
a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
view.setElevation((float)animation.getAnimatedValue());
}
});
return a;
}
}
TransitionHelper:
public class TransitionHelper {
public static void fixSharedElementTransitionForStatusAndNavigationBar(final Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return;
final View decor = activity.getWindow().getDecorView();
if (decor == null)
return;
activity.postponeEnterTransition();
decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onPreDraw() {
decor.getViewTreeObserver().removeOnPreDrawListener(this);
activity.startPostponedEnterTransition();
return true;
}
});
}
public static void setSharedElementEnterTransition(final Activity activity, int transition) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return;
activity.getWindow().setSharedElementEnterTransition(TransitionInflater.from(activity).inflateTransition(transition));
}
}
So what are the different parts here: We have two activities. During the transition four views are transitioned between the activities.
那么这里的不同部分是什么:我们有两个活动。在过渡期间,四个视图在活动之间转换。
-
Toolbar: like in the left gif the toolbar doesn't move with the rest of the content.
工具栏:就像在左边的gif中一样,工具栏不会与其他内容一起移动。
-
ListView element View -> becomes the content view of the DetailActivity
ListView元素视图 - >成为DetailActivity的内容视图
-
StatusBar and NavigationBar Background: If we don't add these views to the set of transitioned views they will fade out and back in during the transition. This however requires to delay the enter transition (see:
TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar
)StatusBar和NavigationBar背景:如果我们不将这些视图添加到转换视图集,它们将在转换期间淡出并重新进入。但是这需要延迟输入转换(请参阅:TransitionHelper.fixSharedElementTransitionForStatusAndNavigationBar)
In the MainActivity
the transitioned views are added to the Bundle that is used to start the DetailActivity
. Furthermore the transitioned views need to be named (transitionName
) in both activities. This can be done in the layout xml as well as programatically.
在MainActivity中,转换的视图将添加到用于启动DetailActivity的Bundle中。此外,转换后的视图需要在两个活动中命名(transitionName)。这可以在布局xml中以及以编程方式完成。
The default set of transitions, that is used during the shared element transition, affects different aspects of the view(for example: view bounds - see 2). However differences in the elevation of a view are not animated. This is why the presented solution utilizes the custom ElevationTransition.
在共享元素转换期间使用的默认转换集会影响视图的不同方面(例如:视图边界 - 请参阅2)。但是,视图高程的差异不是动画的。这就是为什么所提出的解决方案使用自定义ElevationTransition。
#2
3
try this.. Material-Animations
试试这个..材料 - 动画
blueIconImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, SharedElementActivity.class);
View sharedView = blueIconImageView;
String transitionName = getString(R.string.blue_name);
ActivityOptions transitionActivityOptions = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, sharedView, transitionName);
startActivity(i, transitionActivityOptions.toBundle());
}
});
#3
0
The Animation you need is called Activity Transitions between shared elements. By Research I found that you should:
您需要的动画称为共享元素之间的活动转换。通过研究我发现你应该:
- Put your ListView view in a relativeLayout
- OnClick, inflate a copy of your renderer
- Find the global coordinates for where the renderer sits in relationship to the parent of the ListView
- Add the copied renderer to the RelativeLayout (parent of ListView)
- Animate the listView away
- On the end of that animate, animate your new renderer
-
Profit!
public class MainActivity extends Activity { private RelativeLayout layout; private ListView listView; private MyRenderer selectedRenderer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); layout = new RelativeLayout(this); setContentView(layout); listView = new ListView(this); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); layout.addView(listView, rlp); listView.setAdapter(new MyAdapter()); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // find out where the clicked view sits in relationship to the // parent container int t = view.getTop() + listView.getTop(); int l = view.getLeft() + listView.getLeft(); // create a copy of the listview and add it to the parent // container // at the same location it was in the listview selectedRenderer = new MyRenderer(view.getContext()); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(view.getWidth(), view .getHeight()); rlp.topMargin = t; rlp.leftMargin = l; selectedRenderer.textView.setText(((MyRenderer) view).textView.getText()); layout.addView(selectedRenderer, rlp); view.setVisibility(View.INVISIBLE); // animate out the listView Animation outAni = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, -1f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f); outAni.setDuration(1000); outAni.setFillAfter(true); outAni.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { ScaleAnimation scaleAni = new ScaleAnimation(1f, 1f, 1f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); scaleAni.setDuration(400); scaleAni.setFillAfter(true); selectedRenderer.startAnimation(scaleAni); } }); listView.startAnimation(outAni); } }); } public class MyAdapter extends BaseAdapter { @Override public int getCount() { return 10; } @Override public String getItem(int position) { return "Hello World " + position; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { MyRenderer renderer; if (convertView != null) renderer = (MyRenderer) convertView; else renderer = new MyRenderer(MainActivity.this); renderer.textView.setText(getItem(position)); return renderer; } } public class MyRenderer extends RelativeLayout { public TextView textView; public MyRenderer(Context context) { super(context); setPadding(20, 20, 20, 20); setBackgroundColor(0xFFFF0000); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); rlp.addRule(CENTER_IN_PARENT); textView = new TextView(context); addView(textView, rlp); } } }
将ListView视图放在relativeLayout中
OnClick,为渲染器的副本充气
查找渲染器与ListView的父级相关的全局坐标
将复制的渲染器添加到RelativeLayout(ListView的父级)
为listView设置动画
在该动画的末尾,为新渲染器设置动画
利润!公共类MainActivity扩展Activity { 私有RelativeLayout布局; 私有ListView listView; 私人MyRenderer selectedRenderer; @覆盖 protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); layout = new RelativeLayout(this); 的setContentView(布局); listView = new ListView(this); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.MATCH_PARENT,RelativeLayout.LayoutParams.MATCH_PARENT); layout.addView(listView,rlp); listView.setAdapter(new MyAdapter()); listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){ @覆盖 public void onItemClick(AdapterView parent,View视图,int position,long id){ //找出被点击的视图与...的关系 //父容器 int t = view.getTop()+ listView.getTop(); int l = view.getLeft()+ listView.getLeft(); //创建listview的副本并将其添加到父级 // 容器 //在listview中的同一位置 selectedRenderer = new MyRenderer(view.getContext()); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(view.getWidth(),view .getHeight()); rlp.topMargin = t; rlp.leftMargin = l; selectedRenderer.textView.setText(((MyRenderer)view).textView.getText()); layout.addView(selectedRenderer,rlp); view.setVisibility(View.INVISIBLE); //为listView设置动画 动画outAni = new TranslateAnimation(Animation.RELATIVE_TO_SELF,0f, Animation.RELATIVE_TO_SELF,-1f,Animation.RELATIVE_TO_SELF,0f, Animation.RELATIVE_TO_SELF,0f); outAni.setDuration(1000); outAni.setFillAfter(真); outAni.setAnimationListener(new Animation.AnimationListener(){ @覆盖 public void onAnimationStart(动画动画){ } @覆盖 public void onAnimationRepeat(动画动画){ } @覆盖 public void onAnimationEnd(动画动画){ ScaleAnimation scaleAni = new ScaleAnimation(1f, 1f,1f,2f, Animation.RELATIVE_TO_SELF,0.5f, Animation.RELATIVE_TO_SELF,0.5f); scaleAni.setDuration(400); scaleAni.setFillAfter(真); selectedRenderer.startAnimation(scaleAni); } }); listView.startAnimation(outAni); } }); } 公共类MyAdapter扩展BaseAdapter { @覆盖 public int getCount(){ 返回10; } @覆盖 public String getItem(int position){ 返回“Hello World”+位置; } @覆盖 public long getItemId(int position){ 返回位置; } @覆盖 public View getView(int position,View convertView,ViewGroup parent){ MyRenderer渲染器; if(convertView!= null) renderer =(MyRenderer)convertView; 其他 renderer = new MyRenderer(MainActivity.this); renderer.textView.setText(的getItem(位置)); 返回渲染器; } } 公共类MyRenderer扩展RelativeLayout { public TextView textView; public MyRenderer(Context context){ 超级(上下文); setPadding(20,20,20,20); setBackgroundColor(0xFFFF0000地址); RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT); rlp.addRule(CENTER_IN_PARENT); textView = new TextView(context); addView(textView,rlp); } }}
#4
0
Try this spectacular webpage @ Getting Started with Activity & Fragment Transitions (part 1). Here they talked about Activity and Fragment Transitions. I have not tried it. My view is that Fragment Transitions is better and less computer intensive, so it's a good start. And you may not need to change Toolbars, you can show/hide them.
试试这个壮观的网页@活动和碎片过渡入门(第1部分)。在这里,他们谈到了活动和片段转换。我没试过。我认为Fragment Transitions更好,计算机密集度更低,所以这是一个好的开始。您可能不需要更改工具栏,您可以显示/隐藏它们。
Another good SO link is @ Animate the transition between fragments, look at the best answer. In that post, they talked about objectAnimator.
另一个好的SO链接是@Animate片段之间的过渡,看看最好的答案。在那篇文章中,他们谈到了objectAnimator。
Another opinion is about the sample animation you posted, it does not show a smooth animation from one art to another. It is less impressive when the animation is not smooth.
另一种观点是关于你发布的样本动画,它没有显示从一个艺术到另一个艺术的平滑动画。当动画不流畅时,它不那么令人印象深刻。
Good luck, have fun, keep us all posted.
祝你好运,玩得开心,让我们全部张贴。