Home | History | Annotate | Download | only in com.example.android.adaptertransition
      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 
     75     public static AdapterTransitionFragment newInstance() {
     76         return new AdapterTransitionFragment();
     77     }
     78 
     79     public AdapterTransitionFragment() {
     80     }
     81 
     82     @Override
     83     public void onCreate(Bundle savedInstanceState) {
     84         super.onCreate(savedInstanceState);
     85         setHasOptionsMenu(true);
     86     }
     87 
     88     @Override
     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
    104     public void onSaveInstanceState(Bundle outState) {
    105         super.onSaveInstanceState(outState);
    106         outState.putBoolean(STATE_IS_LISTVIEW, mAbsListView instanceof ListView);
    107     }
    108 
    109     @Override
    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
    119     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    120         inflater.inflate(R.menu.fragment_adapter_transition, menu);
    121     }
    122 
    123     @Override
    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
    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
    150     public void onTransitionStart(Transition transition) {
    151     }
    152 
    153     // BEGIN_INCLUDE(on_transition_end)
    154     @Override
    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
    163     public void onTransitionCancel(Transition transition) {
    164     }
    165 
    166     @Override
    167     public void onTransitionPause(Transition transition) {
    168     }
    169 
    170     @Override
    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      */
    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      */
    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      */
    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      */
    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