Merge "Improvements to ListFragment."

This commit is contained in:
Dianne Hackborn
2010-06-28 15:26:00 -07:00
committed by Android (Google) Code Review
8 changed files with 288 additions and 19 deletions

View File

@@ -28030,6 +28030,19 @@
<parameter name="id" type="long">
</parameter>
</method>
<method name="setEmptyText"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="text" type="java.lang.CharSequence">
</parameter>
</method>
<method name="setListAdapter"
return="void"
abstract="false"
@@ -28043,6 +28056,21 @@
<parameter name="adapter" type="android.widget.ListAdapter">
</parameter>
</method>
<method name="setListShown"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="shown" type="boolean">
</parameter>
<parameter name="animate" type="boolean">
</parameter>
</method>
<method name="setSelection"
return="void"
abstract="false"
@@ -28152,7 +28180,7 @@
static="false"
final="false"
deprecated="not deprecated"
visibility="protected"
visibility="public"
>
<parameter name="id" type="int">
</parameter>
@@ -223896,7 +223924,7 @@
deprecated="not deprecated"
visibility="public"
>
<parameter name="t" type="T">
<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
@@ -247337,7 +247365,7 @@
<method name="availableProcessors"
return="int"
abstract="false"
native="false"
native="true"
synchronized="false"
static="false"
final="false"

View File

@@ -4036,6 +4036,7 @@ public class Activity extends ContextThemeWrapper
final void performStart() {
mCalled = false;
mFragments.execPendingActions();
mInstrumentation.callActivityOnStart(this);
if (!mCalled) {
throw new SuperNotCalledException(
@@ -4074,6 +4075,8 @@ public class Activity extends ContextThemeWrapper
final void performResume() {
performRestart();
mFragments.execPendingActions();
mLastNonConfigurationInstances = null;
// First call onResume() -before- setting mResumed, so we don't
@@ -4091,6 +4094,7 @@ public class Activity extends ContextThemeWrapper
mCalled = false;
mFragments.dispatchResume();
mFragments.execPendingActions();
onPostResume();
if (!mCalled) {

View File

@@ -18,6 +18,7 @@ package android.app;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import java.util.ArrayList;
@@ -28,14 +29,30 @@ final class BackStackState implements Parcelable {
final String mName;
public BackStackState(FragmentManager fm, BackStackEntry bse) {
mOps = new int[bse.mNumOp*4];
int numRemoved = 0;
BackStackEntry.Op op = bse.mHead;
while (op != null) {
if (op.removed != null) numRemoved += op.removed.size();
op = op.next;
}
mOps = new int[bse.mNumOp*5 + numRemoved];
op = bse.mHead;
int pos = 0;
while (op != null) {
mOps[pos++] = op.cmd;
mOps[pos++] = op.fragment.mIndex;
mOps[pos++] = op.enterAnim;
mOps[pos++] = op.exitAnim;
if (op.removed != null) {
final int N = op.removed.size();
mOps[pos++] = N;
for (int i=0; i<N; i++) {
mOps[pos++] = op.removed.get(i).mIndex;
}
} else {
mOps[pos++] = 0;
}
op = op.next;
}
mTransition = bse.mTransition;
@@ -61,6 +78,13 @@ final class BackStackState implements Parcelable {
op.fragment = f;
op.enterAnim = mOps[pos++];
op.exitAnim = mOps[pos++];
final int N = mOps[pos++];
if (N > 0) {
op.removed = new ArrayList<Fragment>(N);
for (int i=0; i<N; i++) {
op.removed.add(fm.mActive.get(mOps[pos++]));
}
}
bse.addOp(op);
}
bse.mTransition = mTransition;
@@ -96,6 +120,8 @@ final class BackStackState implements Parcelable {
* @hide Entry of an operation on the fragment back stack.
*/
final class BackStackEntry implements FragmentTransaction, Runnable {
static final String TAG = "BackStackEntry";
final FragmentManager mManager;
static final int OP_NULL = 0;
@@ -265,11 +291,14 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
public void commit() {
if (mCommitted) throw new IllegalStateException("commit already called");
if (FragmentManager.DEBUG) Log.v(TAG, "Commit: " + this);
mCommitted = true;
mManager.mActivity.mHandler.post(this);
mManager.enqueueAction(this);
}
public void run() {
if (FragmentManager.DEBUG) Log.v(TAG, "Run: " + this);
Op op = mHead;
while (op != null) {
switch (op.cmd) {
@@ -286,6 +315,8 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
if (mManager.mAdded != null) {
for (int i=0; i<mManager.mAdded.size(); i++) {
Fragment old = mManager.mAdded.get(i);
if (FragmentManager.DEBUG) Log.v(TAG,
"OP_REPLACE: adding=" + f + " old=" + old);
if (old.mContainerId == f.mContainerId) {
if (op.removed == null) {
op.removed = new ArrayList<Fragment>();
@@ -350,6 +381,8 @@ final class BackStackEntry implements FragmentTransaction, Runnable {
}
public void popFromBackStack() {
if (FragmentManager.DEBUG) Log.v(TAG, "popFromBackStack: " + this);
Op op = mTail;
while (op != null) {
switch (op.cmd) {

View File

@@ -77,6 +77,10 @@ public class FragmentManager {
static final boolean DEBUG = true;
static final String TAG = "FragmentManager";
ArrayList<Runnable> mPendingActions;
Runnable[] mTmpActions;
boolean mExecutingActions;
ArrayList<Fragment> mActive;
ArrayList<Fragment> mAdded;
ArrayList<Integer> mAvailIndices;
@@ -91,6 +95,13 @@ public class FragmentManager {
Bundle mStateBundle = null;
SparseArray<Parcelable> mStateArray = null;
Runnable mExecCommit = new Runnable() {
@Override
public void run() {
execPendingActions();
}
};
Animation loadAnimation(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
Animation animObj = fragment.onCreateAnimation(transitionStyle, enter,
@@ -486,6 +497,52 @@ public class FragmentManager {
return null;
}
public void enqueueAction(Runnable action) {
synchronized (this) {
if (mPendingActions == null) {
mPendingActions = new ArrayList<Runnable>();
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
mActivity.mHandler.removeCallbacks(mExecCommit);
mActivity.mHandler.post(mExecCommit);
}
}
}
/**
* Only call from main thread!
*/
public void execPendingActions() {
if (mExecutingActions) {
throw new IllegalStateException("Recursive entry to execPendingActions");
}
while (true) {
int numActions;
synchronized (this) {
if (mPendingActions == null || mPendingActions.size() == 0) {
return;
}
numActions = mPendingActions.size();
if (mTmpActions == null || mTmpActions.length < numActions) {
mTmpActions = new Runnable[numActions];
}
mPendingActions.toArray(mTmpActions);
mPendingActions.clear();
mActivity.mHandler.removeCallbacks(mExecCommit);
}
mExecutingActions = true;
for (int i=0; i<numActions; i++) {
mTmpActions[i].run();
}
mExecutingActions = false;
}
}
public void addBackStackState(BackStackEntry state) {
if (mBackStack == null) {
mBackStack = new ArrayList<BackStackEntry>();
@@ -503,8 +560,9 @@ public class FragmentManager {
return false;
}
final BackStackEntry bss = mBackStack.remove(last);
handler.post(new Runnable() {
enqueueAction(new Runnable() {
public void run() {
if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss);
bss.popFromBackStack();
moveToState(mCurState, reverseTransit(bss.getTransition()),
bss.getTransitionStyle(), true);
@@ -526,9 +584,10 @@ public class FragmentManager {
for (int i=mBackStack.size()-1; i>index; i--) {
states.add(mBackStack.remove(i));
}
handler.post(new Runnable() {
enqueueAction(new Runnable() {
public void run() {
for (int i=0; i<states.size(); i++) {
if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
states.get(i).popFromBackStack();
}
moveToState(mCurState, true);

View File

@@ -21,9 +21,11 @@ import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
/**
* An fragment that displays a list of items by binding to a data source such as
@@ -154,17 +156,28 @@ public class ListFragment extends Fragment {
ListAdapter mAdapter;
ListView mList;
View mEmptyView;
TextView mStandardEmptyView;
View mProgressContainer;
View mListContainer;
boolean mSetEmptyView;
boolean mListShown;
public ListFragment() {
}
/**
* Provide default implementation to return a simple list view.
* Provide default implementation to return a simple list view. Subclasses
* can override to replace with their own layout. If doing so, the
* returned view hierarchy <em>must</em> have a ListView whose id
* is {@link android.R.id.list android.R.id.list} and can optionally
* have a sibling view id {@link android.R.id.empty android.R.id.empty}
* that is to be shown when the list is empty.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(com.android.internal.R.layout.list_content,
return inflater.inflate(com.android.internal.R.layout.list_content_rich,
container, false);
}
@@ -246,6 +259,62 @@ public class ListFragment extends Fragment {
return mList;
}
/**
* The default content for a ListFragment has a TextView that can
* be shown when the list is empty. If you would like to have it
* shown, call this method to supply the text it should use.
*/
public void setEmptyText(CharSequence text) {
ensureList();
if (mStandardEmptyView == null) {
throw new IllegalStateException("Can't be used with a custom content view");
}
if (!mSetEmptyView) {
mSetEmptyView = true;
mList.setEmptyView(mStandardEmptyView);
}
}
/**
* Control whether the list is being displayed. You can make it not
* displayed if you are waiting for the initial data to show in it. During
* this time an indeterminant progress indicator will be shown instead.
*
* @param shown If true, the list view is shown; if false, the progress
* indicator. The initial value is true.
* @param animate If true, an animation will be used to transition to the
* new state.
*/
public void setListShown(boolean shown, boolean animate) {
ensureList();
if (mProgressContainer == null) {
throw new IllegalStateException("Can't be used with a custom content view");
}
if (mListShown == shown) {
return;
}
mListShown = shown;
if (shown) {
if (animate) {
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
getActivity(), android.R.anim.fade_out));
mListContainer.startAnimation(AnimationUtils.loadAnimation(
getActivity(), android.R.anim.fade_in));
}
mProgressContainer.setVisibility(View.GONE);
mListContainer.setVisibility(View.VISIBLE);
} else {
if (animate) {
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
getActivity(), android.R.anim.fade_in));
mListContainer.startAnimation(AnimationUtils.loadAnimation(
getActivity(), android.R.anim.fade_out));
}
mProgressContainer.setVisibility(View.VISIBLE);
mListContainer.setVisibility(View.GONE);
}
}
/**
* Get the ListAdapter associated with this activity's ListView.
*/
@@ -261,16 +330,33 @@ public class ListFragment extends Fragment {
if (root == null) {
throw new IllegalStateException("Content view not yet created");
}
View emptyView = root.findViewById(com.android.internal.R.id.empty);
mList = (ListView)root.findViewById(com.android.internal.R.id.list);
if (mList == null) {
throw new RuntimeException(
"Your content must have a ListView whose id attribute is " +
"'android.R.id.list'");
}
if (emptyView != null) {
mList.setEmptyView(emptyView);
if (root instanceof ListView) {
mList = (ListView)root;
} else {
mStandardEmptyView = (TextView)root.findViewById(
com.android.internal.R.id.internalEmpty);
if (mStandardEmptyView == null) {
mEmptyView = root.findViewById(android.R.id.empty);
}
mProgressContainer = root.findViewById(com.android.internal.R.id.progressContainer);
mListContainer = root.findViewById(com.android.internal.R.id.listContainer);
View rawListView = root.findViewById(android.R.id.list);
if (!(rawListView instanceof ListView)) {
throw new RuntimeException(
"Content has view with id attribute 'android.R.id.list' "
+ "that is not a ListView class");
}
mList = (ListView)rawListView;
if (mList == null) {
throw new RuntimeException(
"Your content must have a ListView whose id attribute is " +
"'android.R.id.list'");
}
if (mEmptyView != null) {
mList.setEmptyView(mEmptyView);
}
}
mListShown = true;
mList.setOnItemClickListener(mOnClickListener);
if (mAdapter != null) {
setListAdapter(mAdapter);

View File

@@ -44,7 +44,7 @@ public abstract class LoaderManagingFragment<D> extends Fragment
* when the new loader completes it's work. The callback will be delivered before the old loader
* is destroyed.
*/
protected Loader<D> startLoading(int id, Bundle args) {
public Loader<D> startLoading(int id, Bundle args) {
LoaderInfo<D> info = mLoaders.get(id);
if (info != null) {
// Keep track of the previous instance of this loader so we can destroy

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/* Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout android:id="@+id/progressContainer"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:gravity="center">
<ProgressBar style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="@string/loading"
android:paddingTop="4dip"
android:singleLine="true" />
</LinearLayout>
<FrameLayout android:id="@+id/listContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawSelectorOnTop="false" />
<TextView android:id="@+android:id/internalEmpty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceLarge" />
</FrameLayout>
</FrameLayout>

View File

@@ -1919,6 +1919,9 @@
combined with setIcon(android.R.drawable.ic_dialog_alert) -->
<string name="dialog_alert_title">Attention</string>
<!-- Text shown by list fragment when waiting for data to display. -->
<string name="loading">Loading...</string>
<!-- Default text for a button that can be toggled on and off. -->
<string name="capital_on">ON</string>
<!-- Default text for a button that can be toggled on and off. -->