Merge "Improvements to ListFragment."
This commit is contained in:
committed by
Android (Google) Code Review
commit
f1b0eff0da
@@ -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"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
56
core/res/res/layout/list_content_rich.xml
Normal file
56
core/res/res/layout/list_content_rich.xml
Normal 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>
|
||||
@@ -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. -->
|
||||
|
||||
Reference in New Issue
Block a user