• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.adaptertransition;
18 
19 import android.os.Bundle;
20 import android.support.v4.app.ActivityCompat;
21 import android.support.v4.app.Fragment;
22 import android.transition.AutoTransition;
23 import android.transition.Scene;
24 import android.transition.Transition;
25 import android.transition.TransitionManager;
26 import android.view.LayoutInflater;
27 import android.view.Menu;
28 import android.view.MenuInflater;
29 import android.view.MenuItem;
30 import android.view.View;
31 import android.view.ViewGroup;
32 import android.widget.AbsListView;
33 import android.widget.FrameLayout;
34 import android.widget.GridView;
35 import android.widget.ListView;
36 import android.widget.Toast;
37 
38 /**
39  * Main screen for AdapterTransition sample.
40  */
41 public class AdapterTransitionFragment extends Fragment implements Transition.TransitionListener {
42 
43     /**
44      * Since the transition framework requires all relevant views in a view hierarchy to be marked
45      * with IDs, we use this ID to mark the root view.
46      */
47     private static final int ROOT_ID = 1;
48 
49     /**
50      * A tag for saving state whether the mAbsListView is ListView or GridView.
51      */
52     private static final String STATE_IS_LISTVIEW = "is_listview";
53 
54     /**
55      * This is where we place our AdapterView (ListView / GridView).
56      */
57     private FrameLayout mContent;
58 
59     /**
60      * This is where we carry out the transition.
61      */
62     private FrameLayout mCover;
63 
64     /**
65      * This list shows our contents. It can be ListView or GridView, and we toggle between them
66      * using the transition framework.
67      */
68     private AbsListView mAbsListView;
69 
70     /**
71      * This is our contents.
72      */
73     private MeatAdapter mAdapter;
74 
newInstance()75     public static AdapterTransitionFragment newInstance() {
76         return new AdapterTransitionFragment();
77     }
78 
AdapterTransitionFragment()79     public AdapterTransitionFragment() {
80     }
81 
82     @Override
onCreate(Bundle savedInstanceState)83     public void onCreate(Bundle savedInstanceState) {
84         super.onCreate(savedInstanceState);
85         setHasOptionsMenu(true);
86     }
87 
88     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)89     public View onCreateView(LayoutInflater inflater, ViewGroup container,
90                              Bundle savedInstanceState) {
91         // If savedInstanceState is available, we restore the state whether the list is a ListView
92         // or a GridView.
93         boolean isListView;
94         if (null == savedInstanceState) {
95             isListView = true;
96         } else {
97             isListView = savedInstanceState.getBoolean(STATE_IS_LISTVIEW, true);
98         }
99         inflateAbsList(inflater, container, isListView);
100         return inflater.inflate(R.layout.fragment_adapter_transition, container, false);
101     }
102 
103     @Override
onSaveInstanceState(Bundle outState)104     public void onSaveInstanceState(Bundle outState) {
105         super.onSaveInstanceState(outState);
106         outState.putBoolean(STATE_IS_LISTVIEW, mAbsListView instanceof ListView);
107     }
108 
109     @Override
onViewCreated(View view, Bundle savedInstanceState)110     public void onViewCreated(View view, Bundle savedInstanceState) {
111         // Retaining references for FrameLayouts that we use later.
112         mContent = (FrameLayout) view.findViewById(R.id.content);
113         mCover = (FrameLayout) view.findViewById(R.id.cover);
114         // We are attaching the list to the screen here.
115         mContent.addView(mAbsListView);
116     }
117 
118     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)119     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
120         inflater.inflate(R.menu.fragment_adapter_transition, menu);
121     }
122 
123     @Override
onPrepareOptionsMenu(Menu menu)124     public void onPrepareOptionsMenu(Menu menu) {
125         // We change the look of the icon every time the user toggles between list and grid.
126         MenuItem item = menu.findItem(R.id.action_toggle);
127         if (null != item) {
128             if (mAbsListView instanceof ListView) {
129                 item.setIcon(R.drawable.ic_action_grid);
130                 item.setTitle(R.string.show_as_grid);
131             } else {
132                 item.setIcon(R.drawable.ic_action_list);
133                 item.setTitle(R.string.show_as_list);
134             }
135         }
136     }
137 
138     @Override
onOptionsItemSelected(MenuItem item)139     public boolean onOptionsItemSelected(MenuItem item) {
140         switch (item.getItemId()) {
141             case R.id.action_toggle: {
142                 toggle();
143                 return true;
144             }
145         }
146         return false;
147     }
148 
149     @Override
onTransitionStart(Transition transition)150     public void onTransitionStart(Transition transition) {
151     }
152 
153     // BEGIN_INCLUDE(on_transition_end)
154     @Override
onTransitionEnd(Transition transition)155     public void onTransitionEnd(Transition transition) {
156         // When the transition ends, we remove all the views from the overlay and hide it.
157         mCover.removeAllViews();
158         mCover.setVisibility(View.INVISIBLE);
159     }
160     // END_INCLUDE(on_transition_end)
161 
162     @Override
onTransitionCancel(Transition transition)163     public void onTransitionCancel(Transition transition) {
164     }
165 
166     @Override
onTransitionPause(Transition transition)167     public void onTransitionPause(Transition transition) {
168     }
169 
170     @Override
onTransitionResume(Transition transition)171     public void onTransitionResume(Transition transition) {
172     }
173 
174     /**
175      * Inflate a ListView or a GridView with a corresponding ListAdapter.
176      *
177      * @param inflater The LayoutInflater.
178      * @param container The ViewGroup that contains this AbsListView. The AbsListView won't be
179      *                  attached to it.
180      * @param inflateListView Pass true to inflate a ListView, or false to inflate a GridView.
181      */
inflateAbsList(LayoutInflater inflater, ViewGroup container, boolean inflateListView)182     private void inflateAbsList(LayoutInflater inflater, ViewGroup container,
183                                 boolean inflateListView) {
184         if (inflateListView) {
185             mAbsListView = (AbsListView) inflater.inflate(R.layout.fragment_meat_list,
186                     container, false);
187             mAdapter = new MeatAdapter(inflater, R.layout.item_meat_list);
188         } else {
189             mAbsListView = (AbsListView) inflater.inflate(R.layout.fragment_meat_grid,
190                     container, false);
191             mAdapter = new MeatAdapter(inflater, R.layout.item_meat_grid);
192         }
193         mAbsListView.setAdapter(mAdapter);
194         mAbsListView.setOnItemClickListener(mAdapter);
195     }
196 
197     /**
198      * Toggle the UI between ListView and GridView.
199      */
toggle()200     private void toggle() {
201         // We use mCover as the overlay on which we carry out the transition.
202         mCover.setVisibility(View.VISIBLE);
203         // This FrameLayout holds all the visible views in the current list or grid. We use this as
204         // the starting Scene of the Transition later.
205         FrameLayout before = copyVisibleViews();
206         FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
207                 FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
208         mCover.addView(before, params);
209         // Swap the actual list.
210         swapAbsListView();
211         // We also swap the icon for the toggle button.
212         ActivityCompat.invalidateOptionsMenu(getActivity());
213         // It is now ready to start the transition.
214         mAbsListView.post(new Runnable() {
215             @Override
216             public void run() {
217                 // BEGIN_INCLUDE(transition_with_listener)
218                 Scene scene = new Scene(mCover, copyVisibleViews());
219                 Transition transition = new AutoTransition();
220                 transition.addListener(AdapterTransitionFragment.this);
221                 TransitionManager.go(scene, transition);
222                 // END_INCLUDE(transition_with_listener)
223             }
224         });
225     }
226 
227     /**
228      * Swap ListView with GridView, or GridView with ListView.
229      */
swapAbsListView()230     private void swapAbsListView() {
231         // We save the current scrolling position before removing the current list.
232         int first = mAbsListView.getFirstVisiblePosition();
233         // If the current list is a GridView, we replace it with a ListView. If it is a ListView,
234         // a GridView.
235         LayoutInflater inflater = LayoutInflater.from(getActivity());
236         inflateAbsList(inflater, (ViewGroup) mAbsListView.getParent(),
237                 mAbsListView instanceof GridView);
238         mAbsListView.setAdapter(mAdapter);
239         // We restore the scrolling position here.
240         mAbsListView.setSelection(first);
241         // The new list is ready, and we replace the existing one with it.
242         mContent.removeAllViews();
243         mContent.addView(mAbsListView);
244     }
245 
246     /**
247      * Copy all the visible views in the mAbsListView into a new FrameLayout and return it.
248      *
249      * @return a FrameLayout with all the visible views inside.
250      */
copyVisibleViews()251     private FrameLayout copyVisibleViews() {
252         // This is the FrameLayout we return afterwards.
253         FrameLayout layout = new FrameLayout(getActivity());
254         // The transition framework requires to set ID for all views to be animated.
255         layout.setId(ROOT_ID);
256         // We only copy visible views.
257         int first = mAbsListView.getFirstVisiblePosition();
258         int index = 0;
259         while (true) {
260             // This is one of the views that we copy. Note that the argument for getChildAt is a
261             // zero-oriented index, and it doesn't usually match with its position in the list.
262             View source = mAbsListView.getChildAt(index);
263             if (null == source) {
264                 break;
265             }
266             // This is the copy of the original view.
267             View destination = mAdapter.getView(first + index, null, layout);
268             assert destination != null;
269             destination.setId(ROOT_ID + first + index);
270             FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
271                     source.getWidth(), source.getHeight());
272             params.leftMargin = (int) source.getX();
273             params.topMargin = (int) source.getY();
274             layout.addView(destination, params);
275             ++index;
276         }
277         return layout;
278     }
279 
280 }
281