标签:
res/layout/fragment_items_list.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ListView android:id="@+id/lvItems" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" > </ListView> </RelativeLayout>
public class ItemsListFragment extends Fragment { private ArrayAdapter<Item> adapterItems; //Item是自定义的一类数据 private ListView lvItems; private OnListItemSelectedListener listener; public interface OnListItemSelectedListener { public void onItemSelected(Item item); } //自定义接口,用于回调实现该接口的Activity相关方法 @Override public void onAttach(Activity activity) { super.onAttach(activity); if (activity instanceof OnListItemSelectedListener) { listener = (OnListItemSelectedListener) activity; } else { throw new ClassCastException( activity.toString() + " must implement ItemsListFragment.OnListItemSelectedListener"); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ArrayList<Item> items = Item.getItems(); adapterItems = new ArrayAdapter<Item>(getActivity(), android.R.layout.simple_list_item_activated_1, items); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_items_list, container, false); lvItems = (ListView) view.findViewById(R.id.lvItems); lvItems.setAdapter(adapterItems); lvItems.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View item, int position, long rowId) { Item item = adapterItems.getItem(position); listener.onItemSelected(item); } }); return view; } public void setActivateOnItemClick(boolean activateOnItemClick) { lvItems.setChoiceMode( activateOnItemClick ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE); } }
res/layout/fragment_item_detail.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/tvTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="Item Title" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+id/tvBody" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/tvTitle" android:layout_centerHorizontal="true" android:layout_marginTop="19dp" android:text="Item Body" /> </RelativeLayout>
public class ItemDetailFragment extends Fragment { private Item item; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); item = (Item) getArguments().getSerializable("item"); //之前通过调用Fragment的setArguments方法传进来的bundle数据 } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_item_detail, container, false); TextView tvTitle = (TextView) view.findViewById(R.id.tvTitle); TextView tvBody = (TextView) view.findViewById(R.id.tvBody); tvTitle.setText(item.getTitle()); tvBody.setText(item.getBody()); return view; } public static ItemDetailFragment newInstance(Item item) { ItemDetailFragment fragmentDemo = new ItemDetailFragment(); Bundle args = new Bundle(); args.putSerializable("item", item); fragmentDemo.setArguments(args); //getArguments().getSerializable("item")获取该数据 return fragmentDemo; } }
res/layout/activity_items_list.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".ItemsListActivity" > <fragment android:id="@+id/fragmentItemsList" android:name="com.codepath.example.masterdetaildemo.ItemsListFragment" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:layout_alignParentTop="true" tools:layout="@layout/fragment_items_list" /> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:showDividers="middle" android:baselineAligned="false" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" > <fragment android:id="@+id/fragmentItemsList" android:name="com.codepath.example.masterdetaildemo.ItemsListFragment" android:layout_height="wrap_content" android:layout_width="0dp" android:layout_weight="1" tools:layout="@layout/fragment_items_list" /> <View android:background="#000000" android:layout_width="1dp" android:layout_height="wrap_content" /> <FrameLayout android:id="@+id/flDetailContainer" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" /> </LinearLayout>
public class ItemsListActivity extends AppCompatActivity implements OnItemSelectedListener { private boolean isTwoPane = false; //平板标志位 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_items_list); determinePaneLayout(); //判断当前使用的是哪个Layout文件 } private void determinePaneLayout() { FrameLayout fragmentItemDetail = (FrameLayout) findViewById(R.id.flDetailContainer); //能够获取到FrameLayout表明使用的平板布局 if (fragmentItemDetail != null) { isTwoPane = true; ItemsListFragment fragmentItemsList = (ItemsListFragment) getSupportFragmentManager() .findFragmentById(R.id.fragmentItemsList); //因为ItemsListFragment实在平板布局文件中写死的因此可以直接获取到该Fragment,而不需要使用事务(FragmentTransaction) fragmentItemsList.setActivateOnItemClick(true); } } @Override public void onItemSelected(Item item) { if (isTwoPane) { ItemDetailFragment fragmentItem = ItemDetailFragment.newInstance(item); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.replace(R.id.flDetailContainer, fragmentItem); ft.commit(); } else { Intent i = new Intent(this, ItemDetailActivity.class); i.putExtra("item", item); startActivity(i); } } }
public class ItemDetailActivity extends AppCompatActivity { ItemDetailFragment fragmentItemDetail; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_item_detail); Item item = (Item) getIntent().getSerializableExtra("item"); if (savedInstanceState == null) { fragmentItemDetail = ItemDetailFragment.newInstance(item); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.replace(R.id.flDetailContainer, fragmentItemDetail); ft.commit(); } } }
res/layout/activity_item_detail.xml <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/flDetailContainer" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".ItemDetailActivity" > </FrameLayout>
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
public FragmentManager getSupportFragmentManager() { return mFragments.getSupportFragmentManager(); }
protected void onCreate(@Nullable Bundle savedInstanceState) { mFragments.attachHost(null /*parent*/); //该方法其最终目的实则是为了将FragmentController中的HostCallback对象引用传给FragmentManager,具体分析看FragmentController super.onCreate(savedInstanceState); NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { mFragments.restoreLoaderNonConfig(nc.loaders); } if (savedInstanceState != null) { Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); mFragments.restoreAllState(p, nc != null ? nc.fragments : null); } mFragments.dispatchCreate(); //该方法最底层会调用FragmentManager的同名方法,对具体的Fragment进行操作 }
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Parcelable p = mFragments.saveAllState(); if (p != null) { outState.putParcelable(FRAGMENTS_TAG, p); //存储当前Fragment保存的FragmentController的状态 } }
public static final FragmentController createController(FragmentHostCallback<?> callbacks) { return new FragmentController(callbacks); } private FragmentController(FragmentHostCallback<?> callbacks) { mHost = callbacks; }
public FragmentManager getSupportFragmentManager() { return mHost.getFragmentManagerImpl(); } public void attachHost(Fragment parent) { mHost.mFragmentManager.attachController( mHost, mHost /*container*/, parent); } public void dispatchCreate() { mHost.mFragmentManager.dispatchCreate(); }
public HostCallbacks() { super(FragmentActivity.this /*fragmentActivity*/); }
private final Activity mActivity; final Context mContext; private final Handler mHandler; final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
FragmentHostCallback(FragmentActivity activity) { this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/); } FragmentHostCallback(Activity activity, Context context, Handler handler, int windowAnimations) { mActivity = activity; mContext = context; mHandler = handler; mWindowAnimations = windowAnimations; }
FragmentManagerImpl getFragmentManagerImpl() { return mFragmentManager; }
static final Object USE_DEFAULT_TRANSITION = new Object(); static final int INITIALIZING = 0; // Not yet created. static final int CREATED = 1; // Created. static final int ACTIVITY_CREATED = 2; // The activity has finished its creation. static final int STOPPED = 3; // Fully created, not started. static final int STARTED = 4; // Created and started, not resumed. static final int RESUMED = 5; // Created started and resumed. int mState = INITIALIZING; int mContainerId; //当前Fragment所属的ViewGroup对应的id号 ViewGroup mContainer; View mView; View mInnerView; String mTag; //当前Fragment的标签 Fragment mParentFragment; FragmentManagerImpl mChildFragmentManager; FragmentManagerImpl mFragmentManager; boolean mFromLayout; //当前Fragment来自于一个Layout boolean mInLayout; //view has actually been inflated in its layout boolean mResumed; boolean mDetached; //当前Fragment是否和context已经绑定 boolean mAdded; //当前Fragment是否被存储在FragmentManager的mAdded集合集合中 boolean mRemoving; //当前Fragment已经存在FragmentManager中则该值为false void initState() { mIndex = -1; mWho = null; mAdded = false; mRemoving = false; mResumed = false; mFromLayout = false; mInLayout = false; mRestored = false; mBackStackNesting = 0; mFragmentManager = null; mChildFragmentManager = null; mHost = null; mFragmentId = 0; mContainerId = 0; mTag = null; mHidden = false; mDetached = false; mRetaining = false; mLoaderManager = null; mLoadersStarted = false; mCheckedForLoaderManager = false; }
final FragmentManagerImpl mManager; Op mHead; Op mTail; int mNumOp; static final class Op { Op next; Op prev; int cmd; Fragment fragment; ...... ArrayList<Fragment> removed; }
public BackStackRecord(FragmentManagerImpl manager) { mManager = manager; }
public FragmentTransaction add(int containerViewId, Fragment fragment) { doAddOp(containerViewId, fragment, null, OP_ADD); return this; } private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { fragment.mFragmentManager = mManager; if (tag != null) { if (fragment.mTag != null && !tag.equals(fragment.mTag)) { throw new IllegalStateException("Can't change tag of fragment " + fragment + ": was " + fragment.mTag + " now " + tag); } fragment.mTag = tag; } //不能改变标签 if (containerViewId != 0) { if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) { throw new IllegalStateException("Can't change container ID of fragment " + fragment + ": was " + fragment.mFragmentId + " now " + containerViewId); } fragment.mContainerId = fragment.mFragmentId = containerViewId; } //不能改变Fragment所属ViewGroup Op op = new Op(); op.cmd = opcmd; op.fragment = fragment; addOp(op); } void addOp(Op op) { if (mHead == null) { mHead = mTail = op; } else { op.prev = mTail; mTail.next = op; mTail = op; } ..... mNumOp++; }
public int commit() { return commitInternal(false); } int commitInternal(boolean allowStateLoss) { if (mCommitted) throw new IllegalStateException("commit already called"); mCommitted = true; if (mAddToBackStack) { mIndex = mManager.allocBackStackIndex(this);} //默认是false else { mIndex = -1; } mManager.enqueueAction(this, allowStateLoss); //note1 return mIndex; }
public void run() { ... TransitionState state = null; SparseArray<Fragment> firstOutFragments = null; SparseArray<Fragment> lastInFragments = null; if (SUPPORTS_TRANSITIONS) { //当前版本大于21,Build.VERSION.SDK_INT >= 21则执行下面代码 firstOutFragments = new SparseArray<Fragment>(); lastInFragments = new SparseArray<Fragment>(); calculateFragments(firstOutFragments, lastInFragments); //Finds the first removed fragment and last added fragments when going forward state = beginTransition(firstOutFragments, lastInFragments, false); } int transitionStyle = state != null ? 0 : mTransitionStyle; int transition = state != null ? 0 : mTransition; Op op = mHead; //note1 while (op != null) { ...... switch (op.cmd) { case OP_ADD: { //note2 Fragment f = op.fragment; ...... mManager.addFragment(f, false); } break; case OP_REPLACE: { Fragment f = op.fragment; int containerId = f.mContainerId; if (mManager.mAdded != null) { for (int i=0; i<mManager.mAdded.size(); i++) { Fragment old = mManager.mAdded.get(i); if (old.mContainerId == containerId) { if (old == f) { op.fragment = f = null; } else { if (op.removed == null) { op.removed = new ArrayList<Fragment>(); } op.removed.add(old); //存入op的链表中 old.mNextAnim = exitAnim; ...... mManager.removeFragment(old, transition, transitionStyle); } //end of else } //end of if (old.mContainerId == containerId) } end of for } end of if (mManager.mAdded != null) if (f != null) {//证明当前的Fragment在FragmentManager的mAdded数组中不存在,需要添加到该数组中 ...... mManager.addFragment(f, false); } } break; case OP_REMOVE: { Fragment f = op.fragment; ...... mManager.removeFragment(f, transition, transitionStyle); //后面的两个参数在分析的时候为简单起见,全部看成为0 } break; case OP_HIDE: { Fragment f = op.fragment; ...... mManager.hideFragment(f, transition, transitionStyle); } break; case OP_SHOW: { Fragment f = op.fragment; ...... mManager.showFragment(f, transition, transitionStyle); } break; case OP_DETACH: { Fragment f = op.fragment; ...... mManager.detachFragment(f, transition, transitionStyle); } break; case OP_ATTACH: { Fragment f = op.fragment; ...... mManager.attachFragment(f, transition, transitionStyle); } break; default: { throw new IllegalArgumentException("Unknown cmd: " + op.cmd); } }// end of switch op = op.next; } //end of while mManager.moveToState(mManager.mCurState, transition, transitionStyle, true); ..... }
ArrayList<Fragment> mActive; ArrayList<Fragment> mAdded; ArrayList<BackStackRecord> mBackStack; boolean mDestroyed; FragmentHostCallback mHost; //提供主线程的Handler,FragmentManagerImpl所属的Activity,在FragmentActivity的onCreate方法中会对其进行初始化 ArrayList<Runnable> mPendingActions; //需要执行的异步事务 boolean mNeedMenuInvalidate; //当前Fragment需要菜单栏 int mCurState = Fragment.INITIALIZING;//当前FragmentManager下的所有Fragment应该到达的状态
public void attachController(FragmentHostCallback host, FragmentContainer container, Fragment parent) { if (mHost != null) throw new IllegalStateException("Already attached"); mHost = host; mContainer = container; mParent = parent; }
public FragmentTransaction beginTransaction() { return new BackStackRecord(this); }
public void enqueueAction(Runnable action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } synchronized (this) { if (mDestroyed || mHost == null) { throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList<Runnable>(); } mPendingActions.add(action); if (mPendingActions.size() == 1) { mHost.getHandler().removeCallbacks(mExecCommit); mHost.getHandler().post(mExecCommit); } } } Runnable mExecCommit = new Runnable() { @Override public void run() { execPendingActions(); } //该方法内部就会将mPendingActions中的action顺序调用run方法执行 };
public void addFragment(Fragment fragment, boolean moveToStateNow) { //第二个参数默认是false if (mAdded == null) { mAdded = new ArrayList<Fragment>();} makeActive(fragment); //添加到mActive集合中 if (!fragment.mDetached) { if (mAdded.contains(fragment)) {throw new IllegalStateException("Fragment already added: " + fragment); } mAdded.add(fragment); //添加到mAdded集合中 fragment.mAdded = true; fragment.mRemoving = false; if (fragment.mHasMenu && fragment.mMenuVisible) {mNeedMenuInvalidate = true; } if (moveToStateNow) {moveToState(fragment);} //note1 } }
void moveToState(int newState, boolean always) { moveToState(newState, 0, 0, always); } void moveToState(int newState, int transit, int transitStyle, boolean always) { ...... mCurState = newState; //note1 if (mActive != null) { boolean loadersRunning = false; for (int i=0; i<mActive.size(); i++) { Fragment f = mActive.get(i); //note2 if (f != null) { moveToState(f, newState, transit, transitStyle, false); ....... } } ...... if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) { mHost.onSupportInvalidateOptionsMenu(); mNeedMenuInvalidate = false; } } }
void moveToState(Fragment f) { moveToState(f, mCurState, 0, 0, false); } void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) { //参数值如:mManager.mCurState,0, 0, true if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) { newState = Fragment.CREATED; } if (f.mRemoving && newState > f.mState) { newState = f.mState; } if (f.mDeferStart && f.mState < Fragment.STARTED && newState > Fragment.STOPPED) { newState = Fragment.STOPPED; } if (f.mState < newState) { ...... switch (f.mState) { case Fragment.INITIALIZING: ....... f.mHost = mHost; f.mParentFragment = mParent; f.mFragmentManager = mParent != null ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl(); f.mCalled = false; f.onAttach(mHost.getContext());//note-1 if (!f.mCalled) { throw new SuperNotCalledException("Fragment " + f + " did not call through to super.onAttach()"); } if (f.mParentFragment == null) { mHost.onAttachFragment(f); } if (!f.mRetaining) { f.performCreate(f.mSavedFragmentState); } //note0 f.mRetaining = false; if (f.mFromLayout) { //Fragment来自于布局文件 f.mView = f.performCreateView(f.getLayoutInflater( f.mSavedFragmentState), null, f.mSavedFragmentState); //note1 if (f.mView != null) { f.mInnerView = f.mView; if (Build.VERSION.SDK_INT >= 11) { ViewCompat.setSaveFromParentEnabled(f.mView, false); } else { f.mView = NoSaveStateFrameLayout.wrap(f.mView); } if (f.mHidden) f.mView.setVisibility(View.GONE); f.onViewCreated(f.mView, f.mSavedFragmentState); //note2 } else { f.mInnerView = null; } } //注意这里没有break!因此往下继续执行 case Fragment.CREATED: if (newState > Fragment.CREATED) { if (!f.mFromLayout) { ViewGroup container = null; if (f.mContainerId != 0) { container = (ViewGroup)mContainer.onFindViewById(f.mContainerId); //note3 if (container == null && !f.mRestored) { throwException(new IllegalArgumentException( "No view found for id 0x....."); } } f.mContainer = container; f.mView = f.performCreateView(f.getLayoutInflater( f.mSavedFragmentState), container, f.mSavedFragmentState);//note4 if (f.mView != null) { f.mInnerView = f.mView; if (Build.VERSION.SDK_INT >= 11) {ViewCompat.setSaveFromParentEnabled(f.mView, false);} else {f.mView = NoSaveStateFrameLayout.wrap(f.mView); } if (container != null) { Animation anim = loadAnimation(f, transit, true,transitionStyle);//note5 if (anim != null) { setHWLayerAnimListenerIfAlpha(f.mView, anim); f.mView.startAnimation(anim); } container.addView(f.mView);//note6 } if (f.mHidden) f.mView.setVisibility(View.GONE); f.onViewCreated(f.mView, f.mSavedFragmentState);//note6.5 } else { f.mInnerView = null;} } f.performActivityCreated(f.mSavedFragmentState);//note7 if (f.mView != null) {f.restoreViewState(f.mSavedFragmentState);} f.mSavedFragmentState = null; } //注意这里没有break!因此往下继续执行 case Fragment.ACTIVITY_CREATED: case Fragment.STOPPED: if (newState > Fragment.STOPPED) { f.performStart();} //note8 case Fragment.STARTED: if (newState > Fragment.STARTED) { f.mResumed = true; f.performResume(); //note9 f.mSavedFragmentState = null; f.mSavedViewState = null; } }//end of swith } else if (f.mState > newState) { switch (f.mState) { case Fragment.RESUMED: if (newState < Fragment.RESUMED) { f.performPause(); //note10 f.mResumed = false; } case Fragment.STARTED: if (newState < Fragment.STARTED) { f.performStop();} //note11 case Fragment.STOPPED: if (newState < Fragment.STOPPED) {f.performReallyStop();} //note12 case Fragment.ACTIVITY_CREATED: if (newState < Fragment.ACTIVITY_CREATED) { if (f.mView != null) { if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) { saveFragmentViewState(f);} } f.performDestroyView(); //note13 if (f.mView != null && f.mContainer != null) { Animation anim = null; if (mCurState > Fragment.INITIALIZING && !mDestroyed) { anim = loadAnimation(f, transit, false,transitionStyle); } if (anim != null) { final Fragment fragment = f; f.mAnimatingAway = f.mView; f.mStateAfterAnimating = newState; final View viewToAnimate = f.mView; anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(viewToAnimate, anim) { @Override public void onAnimationEnd(Animation animation) { super.onAnimationEnd(animation); if (fragment.mAnimatingAway != null) { fragment.mAnimatingAway = null; moveToState(fragment, fragment.mStateAfterAnimating, 0, 0, false); } } }); f.mView.startAnimation(anim); } f.mContainer.removeView(f.mView); //note14 } f.mContainer = null; f.mView = null; f.mInnerView = null; } case Fragment.CREATED: if (newState < Fragment.CREATED) { if (mDestroyed) { if (f.mAnimatingAway != null) { View v = f.mAnimatingAway; f.mAnimatingAway = null; v.clearAnimation(); } } if (f.mAnimatingAway != null) { f.mStateAfterAnimating = newState; newState = Fragment.CREATED; } else { if (!f.mRetaining) { f.performDestroy();} //note15 f.mCalled = false; f.onDetach(); //note16 if (!f.mCalled) { throw new SuperNotCalledException("Fragment " + f+ " did not call through to super.onDetach()"); } if (!keepActive) { if (!f.mRetaining) {makeInactive(f); } else { f.mHost = null; f.mParentFragment = null; f.mFragmentManager = null; f.mChildFragmentManager = null; } }end of if (!keepActive) }//end of else }//end of if (newState < Fragment.CREATED) }//end of switch }//end of else if (f.mState > newState) f.mState = newState; //17 }//end of function
标签:
原文地址:http://blog.csdn.net/evan_man/article/details/51329320