Alexander Lucas | 97842ff | 2014-03-07 14:56:55 -0800 | [diff] [blame^] | 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 | |
| 37 | /** |
| 38 | * Main screen for AdapterTransition sample. |
| 39 | */ |
| 40 | public class AdapterTransitionFragment extends Fragment implements Transition.TransitionListener { |
| 41 | |
| 42 | /** |
| 43 | * Since the transition framework requires all relevant views in a view hierarchy to be marked |
| 44 | * with IDs, we use this ID to mark the root view. |
| 45 | */ |
| 46 | private static final int ROOT_ID = 1; |
| 47 | |
| 48 | /** |
| 49 | * This is where we place our AdapterView (ListView / GridView). |
| 50 | */ |
| 51 | private FrameLayout mContent; |
| 52 | |
| 53 | /** |
| 54 | * This is where we carry out the transition. |
| 55 | */ |
| 56 | private FrameLayout mCover; |
| 57 | |
| 58 | /** |
| 59 | * This list shows our contents. It can be ListView or GridView, and we toggle between them |
| 60 | * using the transition framework. |
| 61 | */ |
| 62 | private AbsListView mAbsListView; |
| 63 | |
| 64 | /** |
| 65 | * This is our contents. |
| 66 | */ |
| 67 | private MeatAdapter mAdapter; |
| 68 | |
| 69 | public static AdapterTransitionFragment newInstance() { |
| 70 | return new AdapterTransitionFragment(); |
| 71 | } |
| 72 | |
| 73 | public AdapterTransitionFragment() { |
| 74 | } |
| 75 | |
| 76 | @Override |
| 77 | public void onCreate(Bundle savedInstanceState) { |
| 78 | super.onCreate(savedInstanceState); |
| 79 | setHasOptionsMenu(true); |
| 80 | } |
| 81 | |
| 82 | @Override |
| 83 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { |
| 84 | // We use a ListView at first |
| 85 | mAbsListView = (AbsListView) inflater.inflate(R.layout.fragment_meat_list, container, false); |
| 86 | mAdapter = new MeatAdapter(inflater, R.layout.item_meat_list); |
| 87 | return inflater.inflate(R.layout.fragment_adapter_transition, container, false); |
| 88 | } |
| 89 | |
| 90 | @Override |
| 91 | public void onViewCreated(View view, Bundle savedInstanceState) { |
| 92 | // Retaining references for FrameLayouts that we use later. |
| 93 | mContent = (FrameLayout) view.findViewById(R.id.content); |
| 94 | mCover = (FrameLayout) view.findViewById(R.id.cover); |
| 95 | // We are attaching the list to the screen here. |
| 96 | mAbsListView.setAdapter(mAdapter); |
| 97 | mContent.addView(mAbsListView); |
| 98 | } |
| 99 | |
| 100 | @Override |
| 101 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { |
| 102 | inflater.inflate(R.menu.fragment_adapter_transition, menu); |
| 103 | } |
| 104 | |
| 105 | @Override |
| 106 | public void onPrepareOptionsMenu(Menu menu) { |
| 107 | // We change the look of the icon every time the user toggles between list and grid. |
| 108 | MenuItem item = menu.findItem(R.id.action_toggle); |
| 109 | if (null != item) { |
| 110 | if (mAbsListView instanceof ListView) { |
| 111 | item.setIcon(R.drawable.ic_action_grid); |
| 112 | } else { |
| 113 | item.setIcon(R.drawable.ic_action_list); |
| 114 | } |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | @Override |
| 119 | public boolean onOptionsItemSelected(MenuItem item) { |
| 120 | switch (item.getItemId()) { |
| 121 | case R.id.action_toggle: { |
| 122 | toggle(); |
| 123 | return true; |
| 124 | } |
| 125 | } |
| 126 | return false; |
| 127 | } |
| 128 | |
| 129 | @Override |
| 130 | public void onTransitionStart(Transition transition) { |
| 131 | } |
| 132 | |
| 133 | // BEGIN_INCLUDE(on_transition_end) |
| 134 | @Override |
| 135 | public void onTransitionEnd(Transition transition) { |
| 136 | // When the transition ends, we remove all the views from the overlay and hide it. |
| 137 | mCover.removeAllViews(); |
| 138 | mCover.setVisibility(View.INVISIBLE); |
| 139 | } |
| 140 | // END_INCLUDE(on_transition_end) |
| 141 | |
| 142 | @Override |
| 143 | public void onTransitionCancel(Transition transition) { |
| 144 | } |
| 145 | |
| 146 | @Override |
| 147 | public void onTransitionPause(Transition transition) { |
| 148 | } |
| 149 | |
| 150 | @Override |
| 151 | public void onTransitionResume(Transition transition) { |
| 152 | } |
| 153 | |
| 154 | /** |
| 155 | * Toggle the UI between ListView and GridView. |
| 156 | */ |
| 157 | private void toggle() { |
| 158 | // We use mCover as the overlay on which we carry out the transition. |
| 159 | mCover.setVisibility(View.VISIBLE); |
| 160 | // This FrameLayout holds all the visible views in the current list or grid. We use this as |
| 161 | // the starting Scene of the Transition later. |
| 162 | FrameLayout before = copyVisibleViews(); |
| 163 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( |
| 164 | FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); |
| 165 | mCover.addView(before, params); |
| 166 | // Swap the actual list. |
| 167 | swapAbsListView(); |
| 168 | // We also swap the icon for the toggle button. |
| 169 | ActivityCompat.invalidateOptionsMenu(getActivity()); |
| 170 | // It is now ready to start the transition. |
| 171 | mAbsListView.post(new Runnable() { |
| 172 | @Override |
| 173 | public void run() { |
| 174 | // BEGIN_INCLUDE(transition_with_listener) |
| 175 | Scene scene = new Scene(mCover, copyVisibleViews()); |
| 176 | Transition transition = new AutoTransition(); |
| 177 | transition.addListener(AdapterTransitionFragment.this); |
| 178 | TransitionManager.go(scene, transition); |
| 179 | // END_INCLUDE(transition_with_listener) |
| 180 | } |
| 181 | }); |
| 182 | } |
| 183 | |
| 184 | /** |
| 185 | * Swap ListView with GridView, or GridView with ListView. |
| 186 | */ |
| 187 | private void swapAbsListView() { |
| 188 | // We save the current scrolling position before removing the current list. |
| 189 | int first = mAbsListView.getFirstVisiblePosition(); |
| 190 | // If the current list is a GridView, we replace it with a ListView. If it is a ListView, |
| 191 | // a GridView. |
| 192 | LayoutInflater inflater = LayoutInflater.from(getActivity()); |
| 193 | if (mAbsListView instanceof GridView) { |
| 194 | mAbsListView = (AbsListView) inflater.inflate( |
| 195 | R.layout.fragment_meat_list, (ViewGroup) mAbsListView.getParent(), false); |
| 196 | mAdapter = new MeatAdapter(inflater, R.layout.item_meat_list); |
| 197 | } else { |
| 198 | mAbsListView = (AbsListView) inflater.inflate( |
| 199 | R.layout.fragment_meat_grid, (ViewGroup) mAbsListView.getParent(), false); |
| 200 | mAdapter = new MeatAdapter(inflater, R.layout.item_meat_grid); |
| 201 | } |
| 202 | mAbsListView.setAdapter(mAdapter); |
| 203 | // We restore the scrolling position here. |
| 204 | mAbsListView.setSelection(first); |
| 205 | // The new list is ready, and we replace the existing one with it. |
| 206 | mContent.removeAllViews(); |
| 207 | mContent.addView(mAbsListView); |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Copy all the visible views in the mAbsListView into a new FrameLayout and return it. |
| 212 | * |
| 213 | * @return a FrameLayout with all the visible views inside. |
| 214 | */ |
| 215 | private FrameLayout copyVisibleViews() { |
| 216 | // This is the FrameLayout we return afterwards. |
| 217 | FrameLayout layout = new FrameLayout(getActivity()); |
| 218 | // The transition framework requires to set ID for all views to be animated. |
| 219 | layout.setId(ROOT_ID); |
| 220 | // We only copy visible views. |
| 221 | int first = mAbsListView.getFirstVisiblePosition(); |
| 222 | int index = 0; |
| 223 | while (true) { |
| 224 | // This is one of the views that we copy. Note that the argument for getChildAt is a |
| 225 | // zero-oriented index, and it doesn't usually match with its position in the list. |
| 226 | View source = mAbsListView.getChildAt(index); |
| 227 | if (null == source) { |
| 228 | break; |
| 229 | } |
| 230 | // This is the copy of the original view. |
| 231 | View destination = mAdapter.getView(first + index, null, layout); |
| 232 | assert destination != null; |
| 233 | destination.setId(ROOT_ID + first + index); |
| 234 | FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( |
| 235 | source.getWidth(), source.getHeight()); |
| 236 | params.leftMargin = (int) source.getX(); |
| 237 | params.topMargin = (int) source.getY(); |
| 238 | layout.addView(destination, params); |
| 239 | ++index; |
| 240 | } |
| 241 | return layout; |
| 242 | } |
| 243 | |
| 244 | } |