Added shared element transitions to Sunshine.
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
package com.example.android.sunshine.app;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
@@ -37,6 +38,7 @@ public class DetailActivity extends AppCompatActivity {
|
||||
|
||||
Bundle arguments = new Bundle();
|
||||
arguments.putParcelable(DetailFragment.DETAIL_URI, getIntent().getData());
|
||||
arguments.putBoolean(DetailFragment.DETAIL_TRANSITION_ANIMATION, true);
|
||||
|
||||
DetailFragment fragment = new DetailFragment();
|
||||
fragment.setArguments(arguments);
|
||||
@@ -44,6 +46,9 @@ public class DetailActivity extends AppCompatActivity {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.add(R.id.weather_detail_container, fragment)
|
||||
.commit();
|
||||
|
||||
// Being here means we are in animation mode
|
||||
supportPostponeEnterTransition();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,11 +48,13 @@ public class DetailFragment extends Fragment implements LoaderManager.LoaderCall
|
||||
|
||||
private static final String LOG_TAG = DetailFragment.class.getSimpleName();
|
||||
static final String DETAIL_URI = "URI";
|
||||
static final String DETAIL_TRANSITION_ANIMATION = "DTA";
|
||||
|
||||
private static final String FORECAST_SHARE_HASHTAG = " #SunshineApp";
|
||||
|
||||
private String mForecast;
|
||||
private Uri mUri;
|
||||
private boolean mTransitionAnimation;
|
||||
|
||||
private static final int DETAIL_LOADER = 0;
|
||||
|
||||
@@ -108,6 +110,7 @@ public class DetailFragment extends Fragment implements LoaderManager.LoaderCall
|
||||
Bundle arguments = getArguments();
|
||||
if (arguments != null) {
|
||||
mUri = arguments.getParcelable(DetailFragment.DETAIL_URI);
|
||||
mTransitionAnimation = arguments.getBoolean(DetailFragment.DETAIL_TRANSITION_ANIMATION, false);
|
||||
}
|
||||
|
||||
View rootView = inflater.inflate(R.layout.fragment_detail_start, container, false);
|
||||
@@ -265,7 +268,7 @@ public class DetailFragment extends Fragment implements LoaderManager.LoaderCall
|
||||
Toolbar toolbarView = (Toolbar) getView().findViewById(R.id.toolbar);
|
||||
|
||||
// We need to start the enter transition after the data has loaded
|
||||
if (activity instanceof DetailActivity) {
|
||||
if ( mTransitionAnimation ) {
|
||||
activity.supportStartPostponedEnterTransition();
|
||||
|
||||
if ( null != toolbarView ) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -145,6 +146,10 @@ public class ForecastAdapter extends RecyclerView.Adapter<ForecastAdapter.Foreca
|
||||
.into(forecastAdapterViewHolder.mIconView);
|
||||
}
|
||||
|
||||
// this enables better animations. even if we lose state due to a device rotation,
|
||||
// the animator can use this to re-find the original view
|
||||
ViewCompat.setTransitionName(forecastAdapterViewHolder.mIconView, "iconView" + position);
|
||||
|
||||
// Read date from cursor
|
||||
long dateInMillis = mCursor.getLong(ForecastFragment.COL_WEATHER_DATE);
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
|
||||
private int mPosition = RecyclerView.NO_POSITION;
|
||||
private boolean mUseTodayLayout, mAutoSelectView;
|
||||
private int mChoiceMode;
|
||||
private boolean mHoldForTransition;
|
||||
|
||||
private static final String SELECTED_KEY = "selected_position";
|
||||
|
||||
@@ -102,7 +103,7 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
|
||||
/**
|
||||
* DetailFragmentCallback for when an item has been selected.
|
||||
*/
|
||||
public void onItemSelected(Uri dateUri);
|
||||
public void onItemSelected(Uri dateUri, ForecastAdapter.ForecastAdapterViewHolder vh);
|
||||
}
|
||||
|
||||
public ForecastFragment() {
|
||||
@@ -159,6 +160,7 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
|
||||
0, 0);
|
||||
mChoiceMode = a.getInt(R.styleable.ForecastFragment_android_choiceMode, AbsListView.CHOICE_MODE_NONE);
|
||||
mAutoSelectView = a.getBoolean(R.styleable.ForecastFragment_autoSelectView, false);
|
||||
mHoldForTransition = a.getBoolean(R.styleable.ForecastFragment_sharedElementTransitions, false);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
@@ -188,7 +190,8 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
|
||||
String locationSetting = Utility.getPreferredLocation(getActivity());
|
||||
((Callback) getActivity())
|
||||
.onItemSelected(WeatherContract.WeatherEntry.buildWeatherLocationWithDate(
|
||||
locationSetting, date)
|
||||
locationSetting, date),
|
||||
vh
|
||||
);
|
||||
mPosition = vh.getAdapterPosition();
|
||||
}
|
||||
@@ -237,6 +240,12 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
// We hold for transition here just in-case the activity
|
||||
// needs to be re-created. In a standard return transition,
|
||||
// this doesn't actually make a difference.
|
||||
if ( mHoldForTransition ) {
|
||||
getActivity().supportPostponeEnterTransition();
|
||||
}
|
||||
getLoaderManager().initLoader(FORECAST_LOADER, null, this);
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
}
|
||||
@@ -316,7 +325,9 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
|
||||
mRecyclerView.smoothScrollToPosition(mPosition);
|
||||
}
|
||||
updateEmptyView();
|
||||
if ( data.getCount() > 0 ) {
|
||||
if ( data.getCount() == 0 ) {
|
||||
getActivity().supportStartPostponedEnterTransition();
|
||||
} else {
|
||||
mRecyclerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
@@ -330,6 +341,9 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
|
||||
if ( null != vh && mAutoSelectView ) {
|
||||
mForecastAdapter.selectView( vh );
|
||||
}
|
||||
if ( mHoldForTransition ) {
|
||||
getActivity().supportStartPostponedEnterTransition();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -339,6 +353,8 @@ public class ForecastFragment extends Fragment implements LoaderManager.LoaderCa
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
@@ -26,11 +26,13 @@ import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import com.example.android.sunshine.app.sync.SunshineSyncAdapter;
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
@@ -161,7 +163,7 @@ public class MainActivity extends AppCompatActivity implements ForecastFragment.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemSelected(Uri contentUri) {
|
||||
public void onItemSelected(Uri contentUri, ForecastAdapter.ForecastAdapterViewHolder vh) {
|
||||
if (mTwoPane) {
|
||||
// In two-pane mode, show the detail view in this activity by
|
||||
// adding or replacing the detail fragment using a
|
||||
@@ -180,7 +182,8 @@ public class MainActivity extends AppCompatActivity implements ForecastFragment.
|
||||
.setData(contentUri);
|
||||
|
||||
ActivityOptionsCompat activityOptions =
|
||||
ActivityOptionsCompat.makeSceneTransitionAnimation(this);
|
||||
ActivityOptionsCompat.makeSceneTransitionAnimation(this,
|
||||
new Pair<View, String>(vh.mIconView, getString(R.string.detail_icon_transition_name)));
|
||||
ActivityCompat.startActivity(this, intent, activityOptions.toBundle());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
-->
|
||||
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/fragment_forecast"
|
||||
android:name="com.example.android.sunshine.app.ForecastFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:layout="@android:layout/list_content"
|
||||
app:sharedElementTransitions="true"
|
||||
/>
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
android:maxWidth="@dimen/today_icon"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill_horizontal"
|
||||
tools:src="@drawable/art_clouds" />
|
||||
tools:src="@drawable/art_clouds"
|
||||
android:transitionName="@string/detail_icon_transition_name"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detail_high_textview"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
-->
|
||||
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:transitionOrdering="together"
|
||||
android:duration="500">
|
||||
android:duration="250">
|
||||
<fade xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<targets>
|
||||
<target android:excludeId="@android:id/statusBarBackground"/>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
-->
|
||||
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:transitionOrdering="together"
|
||||
android:duration="500">
|
||||
android:duration="250">
|
||||
<fade>
|
||||
<targets>
|
||||
<target android:excludeId="@android:id/statusBarBackground" />
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
-->
|
||||
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:transitionOrdering="together"
|
||||
android:duration="500">
|
||||
android:duration="250">
|
||||
<fade xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<targets>
|
||||
<target android:excludeId="@android:id/statusBarBackground"/>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
-->
|
||||
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:transitionOrdering="together"
|
||||
android:duration="500">
|
||||
android:duration="250">
|
||||
<fade>
|
||||
<targets>
|
||||
<target android:excludeId="@android:id/statusBarBackground" />
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
<declare-styleable name="ForecastFragment">
|
||||
<attr name="android:choiceMode" />
|
||||
<attr name="autoSelectView" format="boolean"/>
|
||||
<attr name="sharedElementTransitions" format="boolean"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
||||
@@ -211,4 +211,8 @@
|
||||
<string name="condition_962">Hurricane</string>
|
||||
|
||||
<string name="condition_unknown">Unknown (<xliff:g id="low">%1$s</xliff:g>)</string>
|
||||
|
||||
<!-- For Shared Element Transitions -->
|
||||
<string name="detail_icon_transition_name" translatable="false">TN_DetailIcon</string>
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user