summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/ViewGroup.java147
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java168
-rw-r--r--docs/html/tools/data-binding/guide.jd732
-rw-r--r--packages/DocumentsUI/AndroidManifest.xml3
-rw-r--r--packages/DocumentsUI/res/drawable/ic_files_app.xml5
-rw-r--r--packages/DocumentsUI/res/drawable/icon256.pngbin0 -> 14014 bytes
-rw-r--r--packages/DocumentsUI/res/values/strings.xml3
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java16
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java52
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/animation/AppearAnimationUtils.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java4
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java47
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java11
15 files changed, 838 insertions, 440 deletions
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6dca26b18f26..a6639d5be78b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -151,15 +151,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
Transformation mInvalidationTransformation;
- // View currently under an ongoing drag
+ // View currently under an ongoing drag. Can be null, a child or this window.
private View mCurrentDragView;
// Metadata about the ongoing drag
- private DragEvent mCurrentDrag;
- private HashSet<View> mDragNotifiedChildren;
-
- // Does this group have a child that can accept the current drag payload?
- private boolean mChildAcceptsDrag;
+ private DragEvent mCurrentDragStartEvent;
+ private boolean mIsInterestedInDrag;
+ private HashSet<View> mChildrenInterestedInDrag;
// Used during drag dispatch
private PointF mLocalPoint;
@@ -1276,9 +1274,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
// in all cases, for drags
- if (mCurrentDrag != null) {
- if (newVisibility == VISIBLE) {
- notifyChildOfDrag(child);
+ if (newVisibility == VISIBLE && mCurrentDragStartEvent != null) {
+ if (!mChildrenInterestedInDrag.contains(child)) {
+ notifyChildOfDragStart(child);
}
}
}
@@ -1386,61 +1384,66 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
mCurrentDragView = null;
// Set up our tracking of drag-started notifications
- mCurrentDrag = DragEvent.obtain(event);
- if (mDragNotifiedChildren == null) {
- mDragNotifiedChildren = new HashSet<View>();
+ mCurrentDragStartEvent = DragEvent.obtain(event);
+ if (mChildrenInterestedInDrag == null) {
+ mChildrenInterestedInDrag = new HashSet<View>();
} else {
- mDragNotifiedChildren.clear();
+ mChildrenInterestedInDrag.clear();
}
// Now dispatch down to our children, caching the responses
- mChildAcceptsDrag = false;
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
child.mPrivateFlags2 &= ~View.DRAG_MASK;
if (child.getVisibility() == VISIBLE) {
- final boolean handled = notifyChildOfDrag(children[i]);
- if (handled) {
- mChildAcceptsDrag = true;
+ if (notifyChildOfDragStart(children[i])) {
+ retval = true;
}
}
}
- // Return HANDLED if one of our children can accept the drag
- if (mChildAcceptsDrag) {
+ // Notify itself of the drag start.
+ mIsInterestedInDrag = super.dispatchDragEvent(event);
+ if (mIsInterestedInDrag) {
retval = true;
}
} break;
case DragEvent.ACTION_DRAG_ENDED: {
// Release the bookkeeping now that the drag lifecycle has ended
- if (mDragNotifiedChildren != null) {
- for (View child : mDragNotifiedChildren) {
- // If a child was notified about an ongoing drag, it's told that it's over
- child.dispatchDragEvent(event);
+ if (mChildrenInterestedInDrag != null) {
+ for (View child : mChildrenInterestedInDrag) {
+ // If a child was interested in the ongoing drag, it's told that it's over
+ if (child.dispatchDragEvent(event)) {
+ retval = true;
+ }
child.mPrivateFlags2 &= ~View.DRAG_MASK;
child.refreshDrawableState();
}
- mDragNotifiedChildren.clear();
- if (mCurrentDrag != null) {
- mCurrentDrag.recycle();
- mCurrentDrag = null;
+ mChildrenInterestedInDrag.clear();
+ if (mCurrentDragStartEvent != null) {
+ mCurrentDragStartEvent.recycle();
+ mCurrentDragStartEvent = null;
}
}
- // We consider drag-ended to have been handled if one of our children
- // had offered to handle the drag.
- if (mChildAcceptsDrag) {
- retval = true;
+ if (mIsInterestedInDrag) {
+ if (super.dispatchDragEvent(event)) {
+ retval = true;
+ }
+ mIsInterestedInDrag = false;
}
} break;
case DragEvent.ACTION_DRAG_LOCATION: {
// Find the [possibly new] drag target
- final View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
+ View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
+ if (target == null && mIsInterestedInDrag) {
+ target = this;
+ }
// If we've changed apparent drag target, tell the view root which view
// we're over now [for purposes of the eventual drag-recipient-changed
@@ -1452,35 +1455,49 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
root.setDragFocus(target);
final int action = event.mAction;
- // If we've dragged off of a child view, send it the EXITED message
+ // If we've dragged off of a child view or this window, send it the EXITED message
if (mCurrentDragView != null) {
final View view = mCurrentDragView;
event.mAction = DragEvent.ACTION_DRAG_EXITED;
- view.dispatchDragEvent(event);
- view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
- view.refreshDrawableState();
+ if (view != this) {
+ view.dispatchDragEvent(event);
+ view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
+ view.refreshDrawableState();
+ } else {
+ super.dispatchDragEvent(event);
+ }
}
+
mCurrentDragView = target;
- // If we've dragged over a new child view, send it the ENTERED message
+ // If we've dragged over a new child view, send it the ENTERED message, otherwise
+ // send it to this window.
if (target != null) {
event.mAction = DragEvent.ACTION_DRAG_ENTERED;
- target.dispatchDragEvent(event);
- target.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
- target.refreshDrawableState();
+ if (target != this) {
+ target.dispatchDragEvent(event);
+ target.mPrivateFlags2 |= View.PFLAG2_DRAG_HOVERED;
+ target.refreshDrawableState();
+ } else {
+ super.dispatchDragEvent(event);
+ }
}
event.mAction = action; // restore the event's original state
}
// Dispatch the actual drag location notice, localized into its coordinates
if (target != null) {
- event.mX = localPoint.x;
- event.mY = localPoint.y;
+ if (target != this) {
+ event.mX = localPoint.x;
+ event.mY = localPoint.y;
- retval = target.dispatchDragEvent(event);
+ retval = target.dispatchDragEvent(event);
- event.mX = tx;
- event.mY = ty;
+ event.mX = tx;
+ event.mY = ty;
+ } else {
+ retval = super.dispatchDragEvent(event);
+ }
}
} break;
@@ -1490,6 +1507,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* that we're about to get the corresponding LOCATION event, which we will use to
* determine which of our children is the new target; at that point we will
* push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
+ * If no suitable child is detected, dispatch to this window.
*
* DRAG_EXITED *is* dispatched all the way down immediately: once we know the
* drag has left this ViewGroup, we know by definition that every contained subview
@@ -1499,9 +1517,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
case DragEvent.ACTION_DRAG_EXITED: {
if (mCurrentDragView != null) {
final View view = mCurrentDragView;
- view.dispatchDragEvent(event);
- view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
- view.refreshDrawableState();
+ if (view != this) {
+ view.dispatchDragEvent(event);
+ view.mPrivateFlags2 &= ~View.PFLAG2_DRAG_HOVERED;
+ view.refreshDrawableState();
+ } else {
+ super.dispatchDragEvent(event);
+ }
mCurrentDragView = null;
}
@@ -1517,6 +1539,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
retval = target.dispatchDragEvent(event);
event.mX = tx;
event.mY = ty;
+ } else if (mIsInterestedInDrag) {
+ retval = super.dispatchDragEvent(event);
} else {
if (ViewDebug.DEBUG_DRAG) {
Log.d(View.VIEW_LOG_TAG, " not dropped on an accepting view");
@@ -1525,11 +1549,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
} break;
}
- // If none of our children could handle the event, try here
- if (!retval) {
- // Call up to the View implementation that dispatches to installed listeners
- retval = super.dispatchDragEvent(event);
- }
return retval;
}
@@ -1551,16 +1570,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return null;
}
- boolean notifyChildOfDrag(View child) {
+ boolean notifyChildOfDragStart(View child) {
+ // The caller guarantees that the child is not in mChildrenInterestedInDrag yet.
+
if (ViewDebug.DEBUG_DRAG) {
Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
}
- boolean canAccept = false;
- if (! mDragNotifiedChildren.contains(child)) {
- mDragNotifiedChildren.add(child);
- canAccept = child.dispatchDragEvent(mCurrentDrag);
- if (canAccept && !child.canAcceptDrag()) {
+ final boolean canAccept = child.dispatchDragEvent(mCurrentDragStartEvent);
+ if (canAccept) {
+ mChildrenInterestedInDrag.add(child);
+ if (!child.canAcceptDrag()) {
child.mPrivateFlags2 |= View.PFLAG2_DRAG_CAN_ACCEPT;
child.refreshDrawableState();
}
@@ -3005,10 +3025,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
mLayoutCalledWhileSuppressed = false;
// Tear down our drag tracking
- mDragNotifiedChildren = null;
- if (mCurrentDrag != null) {
- mCurrentDrag.recycle();
- mCurrentDrag = null;
+ mChildrenInterestedInDrag = null;
+ mIsInterestedInDrag = false;
+ if (mCurrentDragStartEvent != null) {
+ mCurrentDragStartEvent.recycle();
+ mCurrentDragStartEvent = null;
}
final int count = mChildrenCount;
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 444f878caac0..a709bb8e1b07 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -23,10 +23,10 @@ import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Debug;
@@ -38,8 +38,10 @@ import android.provider.Settings;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.Log;
+import android.view.DisplayListCanvas;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
+import android.view.RenderNodeAnimator;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -200,10 +202,16 @@ public class LockPatternView extends View {
}
public static class CellState {
- public float scale = 1.0f;
- public float translateY = 0.0f;
- public float alpha = 1.0f;
- public float size;
+ int row;
+ int col;
+ boolean hwAnimating;
+ CanvasProperty<Float> hwRadius;
+ CanvasProperty<Float> hwCenterX;
+ CanvasProperty<Float> hwCenterY;
+ CanvasProperty<Paint> hwPaint;
+ float radius;
+ float translationY;
+ float alpha = 1f;
public float lineEndX = Float.MIN_VALUE;
public float lineEndY = Float.MIN_VALUE;
public ValueAnimator lineAnimator;
@@ -313,7 +321,9 @@ public class LockPatternView extends View {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
mCellStates[i][j] = new CellState();
- mCellStates[i][j].size = mDotSize;
+ mCellStates[i][j].radius = mDotSize/2;
+ mCellStates[i][j].row = i;
+ mCellStates[i][j].col = j;
}
}
@@ -412,6 +422,112 @@ public class LockPatternView extends View {
invalidate();
}
+ public void startCellStateAnimation(CellState cellState, float startAlpha, float endAlpha,
+ float startTranslationY, float endTranslationY, float startScale, float endScale,
+ long delay, long duration,
+ Interpolator interpolator, Runnable finishRunnable) {
+ if (isHardwareAccelerated()) {
+ startCellStateAnimationHw(cellState, startAlpha, endAlpha, startTranslationY,
+ endTranslationY, startScale, endScale, delay, duration, interpolator,
+ finishRunnable);
+ } else {
+ startCellStateAnimationSw(cellState, startAlpha, endAlpha, startTranslationY,
+ endTranslationY, startScale, endScale, delay, duration, interpolator,
+ finishRunnable);
+ }
+ }
+
+ private void startCellStateAnimationSw(final CellState cellState,
+ final float startAlpha, final float endAlpha,
+ final float startTranslationY, final float endTranslationY,
+ final float startScale, final float endScale,
+ long delay, long duration, Interpolator interpolator, final Runnable finishRunnable) {
+ cellState.alpha = startAlpha;
+ cellState.translationY = startTranslationY;
+ cellState.radius = mDotSize/2 * startScale;
+ ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+ animator.setDuration(duration);
+ animator.setStartDelay(delay);
+ animator.setInterpolator(interpolator);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = (float) animation.getAnimatedValue();
+ cellState.alpha = (1 - t) * startAlpha + t * endAlpha;
+ cellState.translationY = (1 - t) * startTranslationY + t * endTranslationY;
+ cellState.radius = mDotSize/2 * ((1 - t) * startScale + t * endScale);
+ invalidate();
+ }
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (finishRunnable != null) {
+ finishRunnable.run();
+ }
+ }
+ });
+ animator.start();
+ }
+
+ private void startCellStateAnimationHw(final CellState cellState,
+ float startAlpha, float endAlpha,
+ float startTranslationY, float endTranslationY,
+ float startScale, float endScale,
+ long delay, long duration, Interpolator interpolator, final Runnable finishRunnable) {
+ cellState.alpha = endAlpha;
+ cellState.translationY = endTranslationY;
+ cellState.radius = mDotSize/2 * endScale;
+ cellState.hwAnimating = true;
+ cellState.hwCenterY = CanvasProperty.createFloat(
+ getCenterYForRow(cellState.row) + startTranslationY);
+ cellState.hwCenterX = CanvasProperty.createFloat(getCenterXForColumn(cellState.col));
+ cellState.hwRadius = CanvasProperty.createFloat(mDotSize/2 * startScale);
+ mPaint.setColor(getCurrentColor(false));
+ mPaint.setAlpha((int) (startAlpha * 255));
+ cellState.hwPaint = CanvasProperty.createPaint(new Paint(mPaint));
+
+ startRtFloatAnimation(cellState.hwCenterY,
+ getCenterYForRow(cellState.row) + endTranslationY, delay, duration, interpolator);
+ startRtFloatAnimation(cellState.hwRadius, mDotSize/2 * endScale, delay, duration,
+ interpolator);
+ startRtAlphaAnimation(cellState, endAlpha, delay, duration, interpolator,
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ cellState.hwAnimating = false;
+ if (finishRunnable != null) {
+ finishRunnable.run();
+ }
+ }
+ });
+
+ invalidate();
+ }
+
+ private void startRtAlphaAnimation(CellState cellState, float endAlpha,
+ long delay, long duration, Interpolator interpolator,
+ Animator.AnimatorListener listener) {
+ RenderNodeAnimator animator = new RenderNodeAnimator(cellState.hwPaint,
+ RenderNodeAnimator.PAINT_ALPHA, (int) (endAlpha * 255));
+ animator.setDuration(duration);
+ animator.setStartDelay(delay);
+ animator.setInterpolator(interpolator);
+ animator.setTarget(this);
+ animator.addListener(listener);
+ animator.start();
+ }
+
+ private void startRtFloatAnimation(CanvasProperty<Float> property, float endValue,
+ long delay, long duration, Interpolator interpolator) {
+ RenderNodeAnimator animator = new RenderNodeAnimator(property, endValue);
+ animator.setDuration(duration);
+ animator.setStartDelay(delay);
+ animator.setInterpolator(interpolator);
+ animator.setTarget(this);
+ animator.start();
+ }
+
private void notifyCellAdded() {
// sendAccessEvent(R.string.lockscreen_access_pattern_cell_added);
if (mOnPatternListener != null) {
@@ -603,14 +719,15 @@ public class LockPatternView extends View {
private void startCellActivatedAnimation(Cell cell) {
final CellState cellState = mCellStates[cell.row][cell.column];
- startSizeAnimation(mDotSize, mDotSizeActivated, 96, mLinearOutSlowInInterpolator,
+ startRadiusAnimation(mDotSize/2, mDotSizeActivated/2, 96, mLinearOutSlowInInterpolator,
cellState, new Runnable() {
- @Override
- public void run() {
- startSizeAnimation(mDotSizeActivated, mDotSize, 192, mFastOutSlowInInterpolator,
- cellState, null);
- }
- });
+ @Override
+ public void run() {
+ startRadiusAnimation(mDotSizeActivated/2, mDotSize/2, 192,
+ mFastOutSlowInInterpolator,
+ cellState, null);
+ }
+ });
startLineEndAnimation(cellState, mInProgressX, mInProgressY,
getCenterXForColumn(cell.column), getCenterYForRow(cell.row));
}
@@ -639,13 +756,13 @@ public class LockPatternView extends View {
state.lineAnimator = valueAnimator;
}
- private void startSizeAnimation(float start, float end, long duration, Interpolator interpolator,
- final CellState state, final Runnable endRunnable) {
+ private void startRadiusAnimation(float start, float end, long duration,
+ Interpolator interpolator, final CellState state, final Runnable endRunnable) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, end);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- state.size = (float) animation.getAnimatedValue();
+ state.radius = (float) animation.getAnimatedValue();
invalidate();
}
});
@@ -969,10 +1086,16 @@ public class LockPatternView extends View {
for (int j = 0; j < 3; j++) {
CellState cellState = mCellStates[i][j];
float centerX = getCenterXForColumn(j);
- float size = cellState.size * cellState.scale;
- float translationY = cellState.translateY;
- drawCircle(canvas, (int) centerX, (int) centerY + translationY,
- size, drawLookup[i][j], cellState.alpha);
+ float translationY = cellState.translationY;
+ if (isHardwareAccelerated() && cellState.hwAnimating) {
+ DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
+ displayListCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
+ cellState.hwRadius, cellState.hwPaint);
+ } else {
+ drawCircle(canvas, (int) centerX, (int) centerY + translationY,
+ cellState.radius, drawLookup[i][j], cellState.alpha);
+
+ }
}
}
@@ -1055,11 +1178,11 @@ public class LockPatternView extends View {
/**
* @param partOfPattern Whether this circle is part of the pattern.
*/
- private void drawCircle(Canvas canvas, float centerX, float centerY, float size,
+ private void drawCircle(Canvas canvas, float centerX, float centerY, float radius,
boolean partOfPattern, float alpha) {
mPaint.setColor(getCurrentColor(partOfPattern));
mPaint.setAlpha((int) (alpha * 255));
- canvas.drawCircle(centerX, centerY, size/2, mPaint);
+ canvas.drawCircle(centerX, centerY, radius, mPaint);
}
@Override
@@ -1290,7 +1413,6 @@ public class LockPatternView extends View {
float centerY = getCenterYForRow(row);
float cellheight = mSquareHeight * mHitFactor * 0.5f;
float cellwidth = mSquareWidth * mHitFactor * 0.5f;
- float translationY = cell.translateY;
bounds.left = (int) (centerX - cellwidth);
bounds.right = (int) (centerX + cellwidth);
bounds.top = (int) (centerY - cellheight);
diff --git a/docs/html/tools/data-binding/guide.jd b/docs/html/tools/data-binding/guide.jd
index 7c4c15a1be43..2de5bc209d55 100644
--- a/docs/html/tools/data-binding/guide.jd
+++ b/docs/html/tools/data-binding/guide.jd
@@ -28,6 +28,10 @@ page.tags="databinding", "layouts"
<li>
<a href="#binding_data">Binding Data</a>
</li>
+
+ <li>
+ <a href="#binding_events">Binding Events</a>
+ </li>
</ol>
</li>
@@ -141,7 +145,7 @@ page.tags="databinding", "layouts"
&mdash; it's a support library, so you can use it with all Android platform
versions back to <strong>Android 2.1</strong> (API level 7+).</p>
-<p>To use data binding, Android Plugin for Gradle <strong>1.3.0-beta1</strong>
+<p>To use data binding, Android Plugin for Gradle <strong>1.3.0-beta4</strong>
or higher is required.</p>
<h4>Beta release</h4>
@@ -187,9 +191,9 @@ or higher is required.</p>
<p>To get started with Data Binding, download the library from the Support
repository in the Android SDK manager. </p>
-<p>The Data Binding plugin requires Android Plugin for Gradle <strong>1.3.0-beta1
-or higher</strong>, so update your build dependencies (in
-<code>build.gradle</code>) as needed.</p>
+<p>The Data Binding plugin requires Android Plugin for Gradle <strong>1.3.0-beta4
+or higher</strong>, so update your build dependencies (in the top-level
+<code>build.gradle</code> file) as needed.</p>
<p>Also, make sure you are using a compatible version of Android Studio.
<strong>Android Studio 1.3</strong> adds the code-completion and layout-preview
@@ -201,18 +205,18 @@ support for data binding.</p>
<p>
To set up your application to use data binding, add data binding to the class
- path of your <code>build.gradle</code> file, right below "android".
+ path of your top-level <code>build.gradle</code> file, right below "android".
</p>
<pre>
dependencies {
- classpath <strong>"com.android.tools.build:gradle:1.3.0-beta1"
- </strong>classpath <strong>"com.android.databinding:dataBinder:</strong>1.0-rc0"
+ classpath <strong>"com.android.tools.build:gradle:1.3.0-beta4"</strong>
+ classpath <strong>"com.android.databinding:dataBinder:1.0-rc1"</strong>
}
-}
</pre>
<p>
- Then make sure jcenter is in the repositories list for your sub projects.
+ Then make sure jcenter is in the repositories list for your projects in the top-level
+ <code>build.gradle</code> file.
</p>
<pre>
@@ -228,8 +232,8 @@ allprojects {
</p>
<pre>
-apply plugin: ‘com.android.application&apos;
-apply plugin: &apos;<strong>com.android.databinding</strong>&apos;
+apply plugin: &apos;com.android.application&apos;
+apply plugin: &apos;com.android.databinding&apos;
</pre>
<p>
The data binding plugin is going to add necessary <strong>provided</strong>
@@ -252,23 +256,23 @@ apply plugin: &apos;<strong>com.android.databinding</strong>&apos;
</p>
<pre>
-<em>&lt;?</em><strong>xml version="1.0" encoding="utf-8"</strong><em>?&gt;
-</em>&lt;<strong>layout xmlns:android="http://schemas.android.com/apk/res/android"</strong>&gt;
- &lt;<strong>data</strong>&gt;
- &lt;<strong>variable name="user" type="com.example.User"</strong>/&gt;
- &lt;/<strong>data</strong>&gt;
- &lt;<strong>LinearLayout
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;layout xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;data&gt;
+ &lt;variable name="user" type="com.example.User"/&gt;
+ &lt;/data&gt;
+ &lt;LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent"</strong>&gt;
- &lt;<strong>TextView android:layout_width="wrap_content"
+ android:layout_height="match_parent"&gt;
+ &lt;TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="&commat;{user.firstName}"</strong>/&gt;
- &lt;<strong>TextView android:layout_width="wrap_content"
+ android:text="&commat;{user.firstName}"/&gt;
+ &lt;TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="&commat;{user.lastName}"</strong>/&gt;
- &lt;/<strong>LinearLayout</strong>&gt;
-&lt;/<strong>layout</strong>&gt;
+ android:text="&commat;{user.lastName}"/&gt;
+ &lt;/LinearLayout&gt;
+&lt;/layout&gt;
</pre>
<p>
The user <strong>variable</strong> within <strong>data</strong> describes a
@@ -285,9 +289,9 @@ apply plugin: &apos;<strong>com.android.databinding</strong>&apos;
</p>
<pre>
-&lt;<strong>TextView android:layout_width="wrap_content"
+&lt;TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="&commat;{user.firstName}"</strong>/&gt;
+ android:text="&commat;{user.firstName}"/&gt;
</pre>
<h3 id="data_object">
Data Object
@@ -298,12 +302,12 @@ apply plugin: &apos;<strong>com.android.databinding</strong>&apos;
</p>
<pre>
-<strong>public class </strong>User {
- <strong>public final </strong>String <strong>firstName</strong>;
- <strong>public final </strong>String <strong>lastName</strong>;
- <strong>public </strong>User(String firstName, String lastName) {
- <strong>this</strong>.<strong>firstName </strong>= firstName;
- <strong>this</strong>.<strong>lastName </strong>= lastName;
+public class User {
+ public final String firstName;
+ public final String lastName;
+ public User(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
}
}
</pre>
@@ -314,18 +318,18 @@ apply plugin: &apos;<strong>com.android.databinding</strong>&apos;
</p>
<pre>
-<strong>public class </strong>User {
- <strong>private final </strong>String <strong>firstName</strong>;
- <strong>private final </strong>String <strong>lastName</strong>;
- <strong>public </strong>User(String firstName, String lastName) {
- <strong>this</strong>.<strong>firstName </strong>= firstName;
- <strong>this</strong>.<strong>lastName </strong>= lastName;
+public class User {
+ private final String firstName;
+ private final String lastName;
+ public User(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
}
- <strong>public </strong>String getFirstName() {
- <strong>return this</strong>.<strong>firstName</strong>;
+ public String getFirstName() {
+ return this.firstName;
}
- <strong>public </strong>String getLastName() {
- <strong>return this</strong>.<strong>lastName</strong>;
+ public String getLastName() {
+ return this.lastName;
}
}
</pre>
@@ -334,7 +338,8 @@ apply plugin: &apos;<strong>com.android.databinding</strong>&apos;
expression <strong><code>&commat;{user.firstName}</code></strong> used for
the TextView’s <strong><code>android:text</code></strong> attribute will
access the <strong><code>firstName</code></strong> field in the former class
- and the <code>getFirstName()</code> method in the latter class.
+ and the <code>getFirstName()</code> method in the latter class. Alternatively, it
+ will also be resolved to <code>firstName()</code> if that method exists.
</p>
<h3 id="binding_data">
@@ -344,8 +349,8 @@ apply plugin: &apos;<strong>com.android.databinding</strong>&apos;
<p>
By default, a Binding class will be generated based on the name of the layout
file, converting it to Pascal case and suffixing “Binding” to it. The above
- layout file was <code>activity_main.xml</code> so the generate class was
- <code>ActivityMainBinding</code>. This class holds all the bindings from the
+ layout file was <code>main_activity.xml</code> so the generate class was
+ <code>MainActivityBinding</code>. This class holds all the bindings from the
layout properties (e.g. the <code>user</code> variable) to the layout’s Views
and knows how to assign values for the binding expressions.The easiest means
for creating the bindings is to do it while inflating:
@@ -353,10 +358,10 @@ apply plugin: &apos;<strong>com.android.databinding</strong>&apos;
<pre>
&commat;Override
-<strong>protected void </strong>onCreate(Bundle savedInstanceState) {
- <strong>super</strong>.onCreate(savedInstanceState);
- ActivityMainBinding binding = DataBindingUtil.<em>setContentView</em>(<strong>this</strong>, R.layout.<em><strong>main_activity</strong></em>);
- User user = <strong>new </strong>User(<strong>"Test"</strong>, <strong>"User"</strong>);
+protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
+ User user = new User("Test", "User");
binding.setUser(user);
}
</pre>
@@ -374,11 +379,55 @@ MainActivityBinding binding = MainActivityBinding.<em>inflate</em>(getLayoutInfl
</p>
<pre>
-ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup,
-false);
+ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.layout.<em><strong>list_item</strong></em>, viewGroup, <strong>false</strong>);
</pre>
+
+<h3 id="binding_events">
+ Binding Events
+</h3>
+<p>
+ Events may be bound to handler methods directly, similar to the way
+ <strong><code>android:onClick</code></strong> can be assigned to a method in the Activity.
+ Event attribute names are governed by the name of the listener method with a few exceptions.
+ For example, {@link android.view.View.OnLongClickListener} has a method {@link android.view.View.OnLongClickListener#onLongClick onLongClick()},
+ so the attribute for this event is <code>android:onLongClick</code>.
+</p>
+<p>
+ To assign an event to its handler, use a normal binding expression, with the value
+ being the method name to call. For example, if your data object has two methods:
+</p>
+<pre>public class MyHandlers {
+ public void onClickFriend(View view) { ... }
+ public void onClickEnemy(View view) { ... }
+}
+</pre>
+<p>
+ The binding expression can assign the click listener for a View:
+</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;layout xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;data&gt;
+ &lt;variable name="handlers" type="com.example.Handlers"/&gt;
+ &lt;variable name="user" type="com.example.User"/&gt;
+ &lt;/data&gt;
+ &lt;LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"&gt;
+ &lt;TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="&commat;{user.firstName}"
+ android:onClick="&commat;{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/&gt;
+ &lt;TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="&commat;{user.lastName}"
+ android:onClick="&commat;{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/&gt;
+ &lt;/LinearLayout&gt;
+&lt;/layout&gt;
+</pre>
<h2 id="layout_details">
Layout Details
</h2>
@@ -394,20 +443,20 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</p>
<pre>
-&lt;<strong>data</strong>&gt;
- &lt;<strong>import type="android.view.View"</strong>/&gt;
-&lt;/<strong>data</strong>&gt;
+&lt;data&gt;
+ &lt;import type="android.view.View"/&gt;
+&lt;/data&gt;
</pre>
<p>
Now, View may be used within your binding expression:
</p>
<pre>
-&lt;<strong>TextView
+&lt;TextView
android:text="&commat;{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:visibility="&commat;{user.isAdult ? View.VISIBLE : View.GONE}"</strong>/&gt;
+ android:visibility="&commat;{user.isAdult ? View.VISIBLE : View.GONE}"/&gt;
</pre>
<p>
When there are class name conflicts, one of the classes may be renamed to an
@@ -415,9 +464,9 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</p>
<pre>
-&lt;<strong>import type="android.view.View"</strong>/&gt;
-&lt;<strong>import type="com.example.real.estate.View"
- alias="Vista"</strong>/&gt;
+&lt;import type="android.view.View"/&gt;
+&lt;import type="com.example.real.estate.View"
+ alias="Vista"/&gt;
</pre>
<p>
Now, <strong><code>Vista</code></strong> may be used to reference the
@@ -428,12 +477,12 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</p>
<pre>
-&lt;<strong>data</strong>&gt;
- &lt;<strong>import type="com.example.User"</strong>/&gt;
- &lt;<strong>import type="java.util.List"</strong>/&gt;
- &lt;<strong>variable name="user" type="User"</strong>/&gt;
- &lt;<strong>variable name="userList" type="List&amp;lt;User&gt;"</strong>/&gt;
- &lt;/<strong>data</strong>&gt;
+&lt;data&gt;
+ &lt;import type="com.example.User"/&gt;
+ &lt;import type="java.util.List"/&gt;
+ &lt;variable name="user" type="User"/&gt;
+ &lt;variable name="userList" type="List&amp;lt;User&gt;"/&gt;
+&lt;/data&gt;
</pre>
<p class="caution">
<strong>Note</strong>: Android Studio does not yet handle imports so the
@@ -443,10 +492,10 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</p>
<pre>
-&lt;<strong>TextView
+&lt;TextView
android:text="&commat;{((User)(user.connection)).lastName}"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/&gt;
+ android:layout_height="wrap_content"/&gt;
</pre>
<p>
Imported types may also be used when referencing static fields and methods in
@@ -454,15 +503,15 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</p>
<pre>
-&lt;<strong>data</strong>&gt;
- &lt;<strong>import type="com.example.MyStringUtils"</strong>/&gt;
- &lt;<strong>variable name="user" type="com.example.User"</strong>/&gt;
-&lt;/<strong>data</strong>&gt;
+&lt;data&gt;
+ &lt;import type="com.example.MyStringUtils"/&gt;
+ &lt;variable name="user" type="com.example.User"/&gt;
+&lt;/data&gt;
-&lt;<strong>TextView
+&lt;TextView
android:text="&commat;{MyStringUtils.capitalize(user.lastName)}"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/&gt;
+ android:layout_height="wrap_content"/&gt;
</pre>
<p>
Just as in Java, <code>java.lang.*</code> is imported automatically.
@@ -481,16 +530,16 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</p>
<pre>
-&lt;<strong>data</strong>&gt;
- &lt;<strong>import type="android.graphics.drawable.Drawable"</strong>/&gt;
- &lt;<strong>variable name="user" type="com.example.User"</strong>/&gt;
- &lt;<strong>variable name="image" type="Drawable"</strong>/&gt;
- &lt;<strong>variable name="note" type="String"</strong>/&gt;
-&lt;/<strong>data</strong>&gt;
+&lt;data&gt;
+ &lt;import type="android.graphics.drawable.Drawable"/&gt;
+ &lt;variable name="user" type="com.example.User"/&gt;
+ &lt;variable name="image" type="Drawable"/&gt;
+ &lt;variable name="note" type="String"/&gt;
+&lt;/data&gt;
</pre>
<p>
The variable types are inspected at compile time, so if a variable implements
- <a href="#observable_objects">Observable</a> or is an <a href=
+ {@link android.databinding.Observable} or is an <a href=
"#observable_collections">observable collection</a>, that should be reflected
in the type. If the variable is a base class or interface that does not
implement the Observable* interface, the variables will <strong>not
@@ -533,9 +582,9 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</p>
<pre>
-&lt;<strong>data class="ContactItem"</strong>&gt;
+&lt;data class="ContactItem"&gt;
...
-&lt;/<strong>data</strong>&gt;
+&lt;/data&gt;
</pre>
<p>
This generates the binding class as <code>ContactItem</code> in the
@@ -545,9 +594,9 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</p>
<pre>
-&lt;<strong>data class=".ContactItem"</strong>&gt;
+&lt;data class=".ContactItem"&gt;
...
-&lt;/<strong>data</strong>&gt;
+&lt;/data&gt;
</pre>
<p>
In this case, <code>ContactItem</code> is generated in the module package
@@ -555,9 +604,9 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</p>
<pre>
-&lt;<strong>data class="com.example.ContactItem"</strong>&gt;
+&lt;data class="com.example.ContactItem"&gt;
...
-&lt;/<strong>data</strong>&gt;
+&lt;/data&gt;
</pre>
<h3 id="includes">
Includes
@@ -570,28 +619,46 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</p>
<pre>
-<em>&lt;?</em><strong>xml version="1.0" encoding="utf-8"</strong><em>?&gt;
-</em>&lt;<strong>layout xmlns:android="http://schemas.android.com/apk/res/android"
-</strong> <strong> xmlns:bind="http://schemas.android.com/apk/res-auto"</strong>&gt;
- &lt;<strong>data</strong>&gt;
- &lt;<strong>variable name="user" type="com.example.User"</strong>/&gt;
- &lt;/<strong>data</strong>&gt;
- &lt;<strong>LinearLayout
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bind="http://schemas.android.com/apk/res-auto"&gt;
+ &lt;data&gt;
+ &lt;variable name="user" type="com.example.User"/&gt;
+ &lt;/data&gt;
+ &lt;LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent"</strong>&gt;
- &lt;<strong>include layout="&commat;layout/name"
- bind:user="&commat;{user}"</strong>/&gt;
- &lt;<strong>include layout="&commat;layout/contact"
- bind:user="&commat;{user}"</strong>/&gt;
- &lt;/<strong>LinearLayout</strong>&gt;
-&lt;/<strong>layout</strong>&gt;
+ android:layout_height="match_parent"&gt;
+ &lt;include layout="&commat;layout/name"
+ bind:user="&commat;{user}"/&gt;
+ &lt;include layout="&commat;layout/contact"
+ bind:user="&commat;{user}"/&gt;
+ &lt;/LinearLayout&gt;
+&lt;/layout&gt;
</pre>
<p>
Here, there must be a <code>user</code> variable in both the
<code>name.xml</code> and <code>contact.xml</code> layout files.
</p>
-
+<p>
+ Data binding does not support include as a direct child of a merge element. For example,
+ <strong>the following layout is not supported:</strong>
+</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bind="http://schemas.android.com/apk/res-auto"&gt;
+ &lt;data&gt;
+ &lt;variable name="user" type="com.example.User"/&gt;
+ &lt;/data&gt;
+ &lt;merge&gt;
+ &lt;include layout="&commat;layout/name"
+ bind:user="&commat;{user}"/&gt;
+ &lt;include layout="&commat;layout/contact"
+ bind:user="&commat;{user}"/&gt;
+ &lt;/merge&gt;
+&lt;/layout&gt;
+</pre>
<h3 id="expression_language">
Expression Language
</h3>
@@ -613,10 +680,10 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</li>
<li>
- <code>L</code>ogical <strong><code>&& ||</code></strong>
+ Logical <strong><code>&& ||</code></strong>
</li>
- <li>Binary <strong><code>&</code> <code>|</code> <code>^</code></strong>
+ <li>Binary <strong><code>& | ^</code></strong>
</li>
<li>Unary <strong><code>+ - ! ~</code></strong>
@@ -659,9 +726,9 @@ ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.lay
</p>
<pre>
-<strong>android:text="&commat;{String.valueOf(index + 1)}"
+android:text="&commat;{String.valueOf(index + 1)}"
android:visibility="&commat;{age &amp;lt; 13 ? View.GONE : View.VISIBLE}"
-android:transitionName=&apos;&commat;{"image_" + id}&apos;</strong>
+android:transitionName=&apos;&commat;{"image_" + id}&apos;
</pre>
<h4 id="missing_operations">
Missing Operations
@@ -746,23 +813,23 @@ android:transitionName=&apos;&commat;{"image_" + id}&apos;</strong>
</p>
<pre>
-&lt;<strong>data</strong>&gt;
- &lt;<strong>import type="android.util.SparseArray"</strong>/&gt;
- &lt;<strong>import type="java.util.Map"</strong>/&gt;
- &lt;<strong>import type="java.util.List"</strong>/&gt;
- &lt;<strong>variable name="list" type="List&lt;String&gt;"</strong>/&gt;
- &lt;<strong>variable name="sparse" type="SparseArray&amp;lt;String&gt;"</strong>/&gt;
- &lt;<strong>variable name="map" type="Map&amp;lt;String, String&gt;"</strong>/&gt;
- &lt;<strong>variable name="index" type="int"</strong>/&gt;
- &lt;<strong>variable name="key" type="String"</strong>/&gt;
-&lt;/<strong>data</strong>&gt;
+&lt;data&gt;
+ &lt;import type="android.util.SparseArray"/&gt;
+ &lt;import type="java.util.Map"/&gt;
+ &lt;import type="java.util.List"/&gt;
+ &lt;variable name="list" type="List&amp;lt;String&gt;"/&gt;
+ &lt;variable name="sparse" type="SparseArray&amp;lt;String&gt;"/&gt;
+ &lt;variable name="map" type="Map&amp;lt;String, String&gt;"/&gt;
+ &lt;variable name="index" type="int"/&gt;
+ &lt;variable name="key" type="String"/&gt;
+&lt;/data&gt;
+…
+android:text="&commat;{list[index]}"
-<strong>android:text="&commat;{list[index]}"
-</strong>…
-<strong>android:text="&commat;{sparse[index]}"
-</strong>…
-<strong>android:text="&commat;{map[key]}"
-</strong>
+android:text="&commat;{sparse[index]}"
+…
+android:text="&commat;{map[key]}"
+
</pre>
<h4 id="string_literals">
String Literals
@@ -774,7 +841,7 @@ android:transitionName=&apos;&commat;{"image_" + id}&apos;</strong>
</p>
<pre>
-<strong>android:text=&apos;&commat;{map["firstName"]}&apos;</strong>
+android:text=&apos;&commat;{map["firstName"]}&apos;
</pre>
<p>
It is also possible to use double quotes to surround the attribute value.
@@ -783,8 +850,8 @@ android:transitionName=&apos;&commat;{"image_" + id}&apos;</strong>
</p>
<pre>
-<strong>android:text="&commat;{map[`firstName`}"
-android:text="&commat;{map[&amp;quot;firstName&amp;quot;]}"</strong>
+android:text="&commat;{map[`firstName`}"
+android:text="&commat;{map[&amp;quot;firstName&amp;quot;]}"
</pre>
<h4 id="resources">
Resources
@@ -796,15 +863,15 @@ android:text="&commat;{map[&amp;quot;firstName&amp;quot;]}"</strong>
</p>
<pre>
-<strong>android:padding="&commat;{large? &commat;dimen/largePadding : &commat;dimen/smallPadding}"</strong>
+android:padding="&commat;{large? &commat;dimen/largePadding : &commat;dimen/smallPadding}"
</pre>
<p>
Format strings and plurals may be evaluated by providing parameters:
</p>
<pre>
-<strong>android:text="&commat;{&commat;string/nameFormat(firstName, lastName)}"
-android:text="&commat;{&commat;plurals/banana(bananaCount)}"</strong>
+android:text="&commat;{&commat;string/nameFormat(firstName, lastName)}"
+android:text="&commat;{&commat;plurals/banana(bananaCount)}"
</pre>
<p>
When a plural takes multiple parameters, all parameters should be passed:
@@ -815,7 +882,7 @@ android:text="&commat;{&commat;plurals/banana(bananaCount)}"</strong>
Have an orange
Have %d oranges
-android:text="<strong>&commat;{&commat;plurals/orange(orangeCount, orangeCount)}</strong>"
+android:text="&commat;{&commat;plurals/orange(orangeCount, orangeCount)}"
</pre>
<p>
Some resources require explicit type evaluation.
@@ -836,9 +903,7 @@ android:text="<strong>&commat;{&commat;plurals/orange(orangeCount, orangeCount)}
<tr>
<td>
- <pre>
-String[]
-</pre>
+ String[]
</td>
<td>
&commat;array
@@ -901,9 +966,7 @@ String[]
color <code>int</code>
</td>
<td>
- <pre>
-&commat;color
-</pre>
+ &commat;color
</td>
<td>
&commat;color
@@ -932,8 +995,9 @@ String[]
a POJO will not cause the UI to update. The real power of data binding can be
used by giving your data objects the ability to notify when data changes.
There are three different data change notification mechanisms,
- <code>Observable</code> objects, <code>ObservableField</code>s, and
- <code>observable collections</code>.
+ <a href="#observable_objects">Observable objects</a>,
+ <a href="#observablefields">observable fields</a>, and
+ <a href="#observable_collections">observable collection</a>s.
</p>
<p>
@@ -946,49 +1010,49 @@ String[]
</h3>
<p>
- A class implementing <code>android.databinding.Observable</code> interface
+ A class implementing the {@link android.databinding.Observable} interface
will allow the binding to attach a single listener to a bound object to
listen for changes of all properties on that object.
</p>
<p>
- The <code>Observable</code> interface has a mechanism to add and remove
+ The {@link android.databinding.Observable} interface has a mechanism to add and remove
listeners, but notifying is up to the developer. To make development easier,
- a base class, <code>BaseObservable,</code> was created to implement the
+ a base class, {@link android.databinding.BaseObservable}, was created to implement the
listener registration mechanism. The data class implementer is still
responsible for notifying when the properties change. This is done by
- assigning a <code>Bindable</code> annotation to the getter and notifying in
+ assigning a {@link android.databinding.Bindable} annotation to the getter and notifying in
the setter.
</p>
<pre>
-<strong>private static class </strong>User <strong>extends </strong>BaseObservable {
- <strong>private </strong>String <strong>firstName</strong>;
- <strong>private </strong>String <strong>lastName</strong>;
+private static class User extends BaseObservable {
+ private String firstName;
+ private String lastName;
&commat;Bindable
- <strong>public </strong>String getFirstName() {
- <strong>return this</strong>.<strong>firstName</strong>;
+ public String getFirstName() {
+ return this.firstName;
}
&commat;Bindable
- <strong>public </strong>String getFirstName() {
- <strong>return this</strong>.<strong>lastName</strong>;
+ public String getLastName() {
+ return this.lastName;
}
- <strong>public void </strong>setFirstName(String firstName) {
- <strong>this</strong>.<strong>firstName </strong>= firstName;
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
- <strong>public void </strong>setLastName(String lastName) {
- <strong>this</strong>.<strong>lastName </strong>= lastName;
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}
</pre>
<p>
- The <code>Bindable</code> annotation generates an entry in the BR class file
+ The {@link android.databinding.Bindable} annotation generates an entry in the BR class file
during compilation. The BR class file will be generated in the module
- package.If the base class for data classes cannot be changed, the
- <code>Observable</code> interface may be implemented using the convenient
- <code>PropertyChangeRegistry</code> to store and notify listeners
+ package. If the base class for data classes cannot be changed, the
+ {@link android.databinding.Observable} interface may be implemented using the convenient
+ {@link android.databinding.PropertyChangeRegistry} to store and notify listeners
efficiently.
</p>
@@ -997,20 +1061,30 @@ String[]
</h3>
<p>
- A little work is involved in creating Observable classes, so developers who
- want to save time or have few properties may use ObservableFields.
- ObservableFields are self-contained observable objects that have a single
- field. There are versions for all primitive types and one for reference
- types. To use, create a public final field in the data class:
+ A little work is involved in creating {@link android.databinding.Observable} classes, so
+ developers who want to save time or have few properties may use
+ {@link android.databinding.ObservableField} and its siblings
+ {@link android.databinding.ObservableBoolean},
+ {@link android.databinding.ObservableByte},
+ {@link android.databinding.ObservableChar},
+ {@link android.databinding.ObservableShort},
+ {@link android.databinding.ObservableInt},
+ {@link android.databinding.ObservableLong},
+ {@link android.databinding.ObservableFloat},
+ {@link android.databinding.ObservableDouble}, and
+ {@link android.databinding.ObservableParcelable}.
+ <code>ObservableFields</code> are self-contained observable objects that have a single
+ field. The primitive versions avoid boxing and unboxing during access operations.
+ To use, create a public final field in the data class:
</p>
<pre>
-<strong>private static class </strong>User <strong>extends </strong>BaseObservable {
- <strong>public final </strong>ObservableField&lt;String&gt; <strong>firstName </strong>=
- <strong>new </strong>ObservableField&lt;&gt;();
- <strong>public final </strong>ObservableField&lt;String&gt; <strong>lastName </strong>=
- <strong>new </strong>ObservableField&lt;&gt;();
- <strong>public final </strong>ObservableInt <strong>age </strong>= <strong>new </strong>ObservableInt();
+private static class User {
+ public final ObservableField&lt;String&gt; firstName =
+ new ObservableField&lt;&gt;();
+ public final ObservableField&lt;String&gt; lastName =
+ new ObservableField&lt;&gt;();
+ public final ObservableInt age = new ObservableInt();
}
</pre>
<p>
@@ -1018,8 +1092,8 @@ String[]
</p>
<pre>
-user.<strong>firstName</strong>.set(<strong>"Google"</strong>);
-<strong>int </strong>age = user.<strong>age</strong>.get();
+user.firstName.set("Google");
+int age = user.age.get();
</pre>
<h3 id="observable_collections">
Observable Collections
@@ -1027,43 +1101,44 @@ user.<strong>firstName</strong>.set(<strong>"Google"</strong>);
<p>
Some applications use more dynamic structures to hold data. Observable
- collections allow keyed access to these data objects.ObservableArrayMap is
+ collections allow keyed access to these data objects.
+ {@link android.databinding.ObservableArrayMap} is
useful when the key is a reference type, such as String.
</p>
<pre>
-ObservableArrayMap&lt;String, Object&gt; user = <strong>new </strong>ObservableArrayMap&lt;&gt;();
-user.put(<strong>"firstName"</strong>, <strong>"Google"</strong>);
-user.put(<strong>"lastName"</strong>, <strong>"Inc."</strong>);
-user.put(<strong>"age"</strong>, 17);
+ObservableArrayMap&lt;String, Object&gt; user = new ObservableArrayMap&lt;&gt;();
+user.put("firstName", "Google");
+user.put("lastName", "Inc.");
+user.put("age", 17);
</pre>
<p>
In the layout, the map may be accessed through the String keys:
</p>
<pre>
-&lt;<strong>data</strong>&gt;
- &lt;<strong>import type="android.databinding.ObservableMap"</strong>/&gt;
- &lt;<strong>variable name="user" type="ObservableMap&amp;lt;String, Object&gt;"</strong>/&gt;
-&lt;/<strong>data</strong>&gt;
+&lt;data&gt;
+ &lt;import type="android.databinding.ObservableMap"/&gt;
+ &lt;variable name="user" type="ObservableMap&amp;lt;String, Object&gt;"/&gt;
+&lt;/data&gt;
-&lt;<strong>TextView
+&lt;TextView
android:text=&apos;&commat;{user["lastName"]}&apos;
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/&gt;
-&lt;<strong>TextView
+ android:layout_height="wrap_content"/&gt;
+&lt;TextView
android:text=&apos;&commat;{String.valueOf(1 + (Integer)user["age"])}&apos;
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/&gt;
+ android:layout_height="wrap_content"/&gt;
</pre>
<p>
- ObservableArrayList is useful when the key is an integer:
+ {@link android.databinding.ObservableArrayList} is useful when the key is an integer:
</p>
<pre>
-ObservableArrayList&lt;Object&gt; user = <strong>new </strong>ObservableArrayList&lt;&gt;();
-user.add(<strong>"Google"</strong>);
-user.add(<strong>"Inc."</strong>);
+ObservableArrayList&lt;Object&gt; user = new ObservableArrayList&lt;&gt;();
+user.add("Google");
+user.add("Inc.");
user.add(17);
</pre>
<p>
@@ -1071,20 +1146,20 @@ user.add(17);
</p>
<pre>
-&lt;<strong>data</strong>&gt;
- &lt;<strong>import type="android.databinding.ObservableList"</strong>/&gt;
- &lt;<strong>import type="com.example.my.app.Fields"</strong>/&gt;
- &lt;<strong>variable name="user" type="ObservableList&amp;lt;Object&gt;"</strong>/&gt;
-&lt;/<strong>data</strong>&gt;
+&lt;data&gt;
+ &lt;import type="android.databinding.ObservableList"/&gt;
+ &lt;import type="com.example.my.app.Fields"/&gt;
+ &lt;variable name="user" type="ObservableList&amp;lt;Object&gt;"/&gt;
+&lt;/data&gt;
-&lt;<strong>TextView
+&lt;TextView
android:text=&apos;&commat;{user[Fields.LAST_NAME]}&apos;
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/&gt;
-&lt;<strong>TextView
+ android:layout_height="wrap_content"/&gt;
+&lt;TextView
android:text=&apos;&commat;{String.valueOf(1 + (Integer)user[Fields.AGE])}&apos;
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/&gt;
+ android:layout_height="wrap_content"/&gt;
</pre>
<h2 id="generated_binding">
Generated Binding
@@ -1094,7 +1169,7 @@ user.add(17);
The generated binding class links the layout variables with the Views within
the layout. As discussed earlier, the name and package of the Binding may be
<a href="#custom_binding_class_names">customized</a>. The Generated binding
- classes all extend <code>android.databinding.ViewDataBinding</code>.
+ classes all extend {@link android.databinding.ViewDataBinding}.
</p>
<h3 id="creating">
@@ -1107,13 +1182,13 @@ user.add(17);
within the layout. There are a few ways to bind to a layout. The most common
is to use the static methods on the Binding class.The inflate method inflates
the View hierarchy and binds to it all it one step. There is a simpler
- version that only takes a <code>LayoutInflater</code> and one that takes a
- <code>ViewGroup</code> as well:
+ version that only takes a {@link android.view.LayoutInflater} and one that takes a
+ {@link android.view.ViewGroup} as well:
</p>
<pre>
-MyLayoutBinding binding = MyLayoutBinding.<em>inflate</em>(<strong>layoutInflater</strong>);
-MyLayoutBinding binding = MyLayoutBinding.<em>inflate</em>(LayoutInflater, viewGroup, false);
+MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
+MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);
</pre>
<p>
If the layout was inflated using a different mechanism, it may be bound
@@ -1121,17 +1196,17 @@ MyLayoutBinding binding = MyLayoutBinding.<em>inflate</em>(LayoutInflater, viewG
</p>
<pre>
-MyLayoutBinding binding = MyLayoutBinding.<em>bind</em>(viewRoot);
+MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
</pre>
<p>
Sometimes the binding cannot be known in advance. In such cases, the binding
- can be created using the DataBindingUtil class:
+ can be created using the {@link android.databinding.DataBindingUtil} class:
</p>
<pre>
-ViewDataBinding binding = DataBindingUtil.<em>inflate</em>(LayoutInflater, layoutId,
+ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
parent, attachToParent);
-ViewDataBinding binding = DataBindingUtil.<em>bindTo</em>(viewRoot, layoutId);
+ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
</pre>
<h3 id="views_with_ids">
Views With IDs
@@ -1145,32 +1220,32 @@ ViewDataBinding binding = DataBindingUtil.<em>bindTo</em>(viewRoot, layoutId);
</p>
<pre>
-&lt;<strong>layout xmlns:android="http://schemas.android.com/apk/res/android"</strong>&gt;
- &lt;<strong>data</strong>&gt;
- &lt;<strong>variable name="user" type="com.example.User"</strong>/&gt;
- &lt;/<strong>data</strong>&gt;
- &lt;<strong>LinearLayout
+&lt;layout xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;data&gt;
+ &lt;variable name="user" type="com.example.User"/&gt;
+ &lt;/data&gt;
+ &lt;LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent"</strong>&gt;
- &lt;<strong>TextView android:layout_width="wrap_content"
+ android:layout_height="match_parent"&gt;
+ &lt;TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="&commat;{user.firstName}"</strong>
- <strong>android:id="&commat;+id/firstName"</strong>/&gt;
- &lt;<strong>TextView android:layout_width="wrap_content"
+ android:text="&commat;{user.firstName}"
+ android:id="&commat;+id/firstName"/&gt;
+ &lt;TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="&commat;{user.lastName}"</strong>
- <strong>android:id="&commat;+id/lastName"</strong>/&gt;
- &lt;/<strong>LinearLayout</strong>&gt;
-&lt;/<strong>layout</strong>&gt;
+ android:text="&commat;{user.lastName}"
+ android:id="&commat;+id/lastName"/&gt;
+ &lt;/LinearLayout&gt;
+&lt;/layout&gt;
</pre>
<p>
Will generate a binding class with:
</p>
<pre>
-<strong>public final </strong>TextView <strong>firstName</strong>;
-<strong>public final </strong>TextView <strong>lastName</strong>;
+public final TextView firstName;
+public final TextView lastName;
</pre>
<p>
IDs are not nearly as necessary as without data binding, but there are still
@@ -1186,49 +1261,49 @@ ViewDataBinding binding = DataBindingUtil.<em>bindTo</em>(viewRoot, layoutId);
</p>
<pre>
-&lt;<strong>data</strong>&gt;
- &lt;<strong>import type="android.graphics.drawable.Drawable"</strong>/&gt;
- &lt;<strong>variable name="user" type="com.example.User"</strong>/&gt;
- &lt;<strong>variable name="image" type="Drawable"</strong>/&gt;
- &lt;<strong>variable name="note" type="String"</strong>/&gt;
-&lt;/<strong>data</strong>&gt;
+&lt;data&gt;
+ &lt;import type="android.graphics.drawable.Drawable"/&gt;
+ &lt;variable name="user" type="com.example.User"/&gt;
+ &lt;variable name="image" type="Drawable"/&gt;
+ &lt;variable name="note" type="String"/&gt;
+&lt;/data&gt;
</pre>
<p>
will generate setters and getters in the binding:
</p>
<pre>
-<strong>public abstract </strong>com.example.User getUser();
-<strong>public abstract void </strong>setUser(com.example.User user);
-<strong>public abstract </strong>Drawable getImage();
-<strong>public abstract void </strong>setImage(Drawable image);
-<strong>public abstract </strong>String getNote();
-<strong>public abstract void </strong>setNote(String note);
+public abstract com.example.User getUser();
+public abstract void setUser(com.example.User user);
+public abstract Drawable getImage();
+public abstract void setImage(Drawable image);
+public abstract String getNote();
+public abstract void setNote(String note);
</pre>
<h3 id="viewstubs">
ViewStubs
</h3>
<p>
- ViewStubs are a little different from normal Views. They start off invisible
+ {@link android.view.ViewStub}s are a little different from normal Views. They start off invisible
and when they either are made visible or are explicitly told to inflate, they
replace themselves in the layout by inflating another layout.
</p>
<p>
- Because the ViewStub essentially disappears from the View hierarchy, the View
+ Because the <code>ViewStub</code> essentially disappears from the View hierarchy, the View
in the binding object must also disappear to allow collection. Because the
- Views are final, a ViewStubProxy object takes the place of the ViewStub,
- giving the developer access to the ViewStub when it exists and also access to
- the inflated View hierarchy when the ViewStub has been inflated.
+ Views are final, a {@link android.databinding.ViewStubProxy} object takes the place of the
+ <code>ViewStub</code>, giving the developer access to the ViewStub when it exists and also
+ access to the inflated View hierarchy when the <code>ViewStub</code> has been inflated.
</p>
<p>
When inflating another layout, a binding must be established for the new
- layout. Therefore, the ViewStubProxy must listen to the ViewStub&apos;s
- OnInflateListener and establish the binding at that time. Since only one can
- exist, the ViewStubProxy allows the developer to set an OnInflateListener on
- it that it will call after establishing the binding.
+ layout. Therefore, the <code>ViewStubProxy</code> must listen to the <code>ViewStub</code>&apos;s
+ {@link android.view.ViewStub.OnInflateListener} and establish the binding at that time. Since
+ only one can exist, the <code>ViewStubProxy</code> allows the developer to set an
+ <code>OnInflateListener</code> on it that it will call after establishing the binding.
</p>
<h3 id="advanced_binding">
@@ -1241,20 +1316,20 @@ ViewDataBinding binding = DataBindingUtil.<em>bindTo</em>(viewRoot, layoutId);
<p>
At times, the specific binding class won&apos;t be known. For example, a
- RecyclerView Adapter operating against arbitrary layouts won&apos;t know the
- specific binding class. It still must assign the binding value during the
- onBindViewHolder.
+ {@link android.support.v7.widget.RecyclerView.Adapter} operating against arbitrary layouts
+ won&apos;t know the specific binding class. It still must assign the binding value during the
+ {@link android.support.v7.widget.RecyclerView.Adapter#onBindViewHolder}.
</p>
<p>
In this example, all layouts that the RecyclerView binds to have an "item"
- variable. The BindingHolder has a getBinding method returning the
- <code>ViewDataBinding</code> base.
+ variable. The <code>BindingHolder</code> has a <code>getBinding</code> method returning the
+ {@link android.databinding.ViewDataBinding} base.
</p>
<pre>
-<strong>public void </strong>onBindViewHolder(BindingHolder holder, <strong>int </strong>position) {
- <strong>final </strong>T item = <strong>mItems</strong>.get(position);
+public void onBindViewHolder(BindingHolder holder, int position) {
+ final T item = mItems.get(position);
holder.getBinding().setVariable(BR.item, item);
holder.getBinding().executePendingBindings();
}
@@ -1267,7 +1342,7 @@ ViewDataBinding binding = DataBindingUtil.<em>bindTo</em>(viewRoot, layoutId);
When a variable or observable changes, the binding will be scheduled to
change before the next frame. There are times, however, when binding must be
executed immediately. To force execution, use the
- <code>executePendingBindings()</code> method.
+ {@link android.databinding.ViewDataBinding#executePendingBindings()} method.
</p>
<h4>
@@ -1321,17 +1396,18 @@ namespace for the attribute does not matter, only the attribute name itself.
<p>
Some attributes have setters that don&apos;t match by name. For these
methods, an attribute may be associated with the setter through
- BindingMethods annotation. This must be associated with a class and contains
- BindingMethod annotations, one for each renamed method. For example, the
- <strong><code>android:tint</code></strong> attribute is really associated
- with setImageTintList, not setTint.
+ {@link android.databinding.BindingMethods} annotation. This must be associated with
+ a class and contains {@link android.databinding.BindingMethod} annotations, one for
+ each renamed method. For example, the <strong><code>android:tint</code></strong> attribute
+ is really associated with {@link android.widget.ImageView#setImageTintList}, not
+ <code>setTint</code>.
</p>
<pre>
&commat;BindingMethods({
- &commat;BindingMethod(type = <strong>"android.widget.ImageView"</strong>,
- attribute = <strong>"android:tint"</strong>,
- method = <strong>"setImageTintList"</strong>),
+ &commat;BindingMethod(type = "android.widget.ImageView",
+ attribute = "android:tint",
+ method = "setImageTintList"),
})
</pre>
<p>
@@ -1347,7 +1423,7 @@ namespace for the attribute does not matter, only the attribute name itself.
Some attributes need custom binding logic. For example, there is no
associated setter for the <strong><code>android:paddingLeft</code></strong>
attribute. Instead, <code>setPadding(left, top, right, bottom)</code> exists.
- A static binding adapter method with the <code>BindingAdapter</code>
+ A static binding adapter method with the {@link android.databinding.BindingAdapter}
annotation allows the developer to customize how a setter for an attribute is
called.
</p>
@@ -1358,9 +1434,8 @@ namespace for the attribute does not matter, only the attribute name itself.
</p>
<pre>
-
-&commat;BindingAdapter(<strong>"android:paddingLeft"</strong>)
-<strong>public static void </strong>setPaddingLeft(View view, <strong>int </strong>padding) {
+&commat;BindingAdapter("android:paddingLeft")
+public static void setPaddingLeft(View view, int padding) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
@@ -1382,9 +1457,9 @@ namespace for the attribute does not matter, only the attribute name itself.
</p>
<pre>
-&commat;BindingAdapter({<strong>"bind:imageUrl"</strong>, <strong>"bind:error"</strong>})
-<strong>public static void </strong>loadImage(ImageView view, String url, Drawable error) {
- Picasso.<em>with</em>(view.getContext()).load(url).error(error).into(view);
+&commat;BindingAdapter({"bind:imageUrl", "bind:error"})
+public static void loadImage(ImageView view, String url, Drawable error) {
+ Picasso.with(view.getContext()).load(url).error(error).into(view);
}
</pre>
<pre>
@@ -1406,6 +1481,123 @@ app:error=“&commat;{&commat;drawable/venueError}”/&gt;
</li>
</ul>
+<p>
+ Binding adapter methods may optionally take the old values in their handlers. A method
+ taking old and new values should have all old values for the attributes come first, followed
+ by the new values:
+</p>
+<pre>
+&commat;BindingAdapter("android:paddingLeft")
+public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
+ if (oldPadding != newPadding) {
+ view.setPadding(newPadding,
+ view.getPaddingTop(),
+ view.getPaddingRight(),
+ view.getPaddingBottom());
+ }
+}
+</pre>
+<p>
+ Event handlers may only be used with interfaces or abstract classes with one abstract method.
+ For example:
+</p>
+<pre>
+&commat;BindingAdapter("android:onLayoutChange")
+public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
+ View.OnLayoutChangeListener newValue) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ if (oldValue != null) {
+ view.removeOnLayoutChangeListener(oldValue);
+ }
+ if (newValue != null) {
+ view.addOnLayoutChangeListener(newValue);
+ }
+ }
+}
+</pre>
+<p>
+ When a listener has multiple methods, it must be split into multiple listeners. For example,
+ {@link android.view.View.OnAttachStateChangeListener} has two methods:
+ {@link android.view.View.OnAttachStateChangeListener#onViewAttachedToWindow onViewAttachedToWindow()} and
+ {@link android.view.View.OnAttachStateChangeListener#onViewDetachedFromWindow onViewDetachedFromWindow()}.
+ We must then create two interfaces to differentiate the attributes and handlers for them.
+</p>
+
+<pre>
+&commat;TargetApi(VERSION_CODES.HONEYCOMB_MR1)
+public interface OnViewDetachedFromWindow {
+ void onViewDetachedFromWindow(View v);
+}
+
+&commat;TargetApi(VERSION_CODES.HONEYCOMB_MR1)
+public interface OnViewAttachedToWindow {
+ void onViewAttachedToWindow(View v);
+}
+</pre>
+<p>
+ Because changing one listener will also affect the other, we must have three different
+ binding adapters, one for each attribute and one for both, should they both be set.
+</p>
+<pre>
+&commat;BindingAdapter("android:onViewAttachedToWindow")
+public static void setListener(View view, OnViewAttachedToWindow attached) {
+ setListener(view, null, attached);
+}
+
+&commat;BindingAdapter("android:onViewDetachedFromWindow")
+public static void setListener(View view, OnViewDetachedFromWindow detached) {
+ setListener(view, detached, null);
+}
+
+&commat;BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
+public static void setListener(View view, final OnViewDetachedFromWindow detach,
+ final OnViewAttachedToWindow attach) {
+ if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
+ final OnAttachStateChangeListener newListener;
+ if (detach == null && attach == null) {
+ newListener = null;
+ } else {
+ newListener = new OnAttachStateChangeListener() {
+ &commat;Override
+ public void onViewAttachedToWindow(View v) {
+ if (attach != null) {
+ attach.onViewAttachedToWindow(v);
+ }
+ }
+
+ &commat;Override
+ public void onViewDetachedFromWindow(View v) {
+ if (detach != null) {
+ detach.onViewDetachedFromWindow(v);
+ }
+ }
+ };
+ }
+ final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
+ newListener, R.id.onAttachStateChangeListener);
+ if (oldListener != null) {
+ view.removeOnAttachStateChangeListener(oldListener);
+ }
+ if (newListener != null) {
+ view.addOnAttachStateChangeListener(newListener);
+ }
+ }
+}
+</pre>
+<p>
+ The above example is slightly more complicated than normal because View uses add and remove
+ for the listener instead of a set method for {@link android.view.View.OnAttachStateChangeListener}.
+ The <code>android.databinding.adapters.ListenerUtil</code> class helps keep track of the previous
+ listeners so that they may be removed in the Binding Adaper.
+</p>
+<p>
+ By annotating the interfaces <code>OnViewDetachedFromWindow</code> and
+ <code>OnViewAttachedToWindow</code> with
+ <code>&commat;TargetApi(VERSION_CODES.HONEYCOMB_MR1)</code>, the data binding code
+ generator knows that the listener should only be generated when running on Honeycomb MR1
+ and new devices, the same version supported by
+ {@link android.view.View#addOnAttachStateChangeListener}.
+</p>
<h2 id="converters">
Converters
</h2>
@@ -1426,10 +1618,10 @@ app:error=“&commat;{&commat;drawable/venueError}”/&gt;
</p>
<pre>
-&lt;<strong>TextView
+&lt;TextView
android:text=&apos;&commat;{userMap["lastName"]}&apos;
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/&gt;
+ android:layout_height="wrap_content"/&gt;
</pre>
<p>
@@ -1447,10 +1639,10 @@ The <code>userMap</code> returns an Object and that Object will be automatically
</p>
<pre>
-&lt;<strong>View
+&lt;View
android:background="&commat;{isError ? &commat;color/red : &commat;color/white}"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/&gt;
+ android:layout_height="wrap_content"/&gt;
</pre>
<p>
Here, the background takes a <code>Drawable</code>, but the color is an
@@ -1462,8 +1654,8 @@ The <code>userMap</code> returns an Object and that Object will be automatically
<pre>
&commat;BindingConversion
-<strong>public static </strong>ColorDrawable convertColorToDrawable(<strong>int </strong>color) {
- <strong>return new </strong>ColorDrawable(color);
+public static ColorDrawable convertColorToDrawable(int color) {
+ return new ColorDrawable(color);
}
</pre>
<p>
@@ -1472,8 +1664,8 @@ The <code>userMap</code> returns an Object and that Object will be automatically
</p>
<pre>
-&lt;<strong>View
+&lt;View
android:background="&commat;{isError ? &commat;drawable/error : &commat;color/white}"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/&gt;
+ android:layout_height="wrap_content"/&gt;
</pre>
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 338b3f863151..a0e3868c1c9d 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -50,7 +50,8 @@
<activity
android:name=".StandaloneActivity"
android:theme="@style/StandaloneTheme"
- android:icon="@drawable/ic_doc_text"
+ android:icon="@drawable/ic_files_app"
+ android:label="@string/files_label"
android:enabled="@bool/productivity_device">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/packages/DocumentsUI/res/drawable/ic_files_app.xml b/packages/DocumentsUI/res/drawable/ic_files_app.xml
new file mode 100644
index 000000000000..ff7189e94e8b
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/ic_files_app.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/icon256"
+ android:tint="?android:attr/colorControlNormal"
+ android:autoMirrored="true" />
diff --git a/packages/DocumentsUI/res/drawable/icon256.png b/packages/DocumentsUI/res/drawable/icon256.png
new file mode 100644
index 000000000000..631c95144f60
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/icon256.png
Binary files differ
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index b970f68d5c0b..616f4dd9492c 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -18,6 +18,9 @@
<!-- Title of the documents application [CHAR LIMIT=32] -->
<string name="app_label">Documents</string>
+ <!-- Title of the standalone files activity. [CHAR LIMIT=32] -->
+ <string name="files_label">Files</string>
+
<!-- Action bar title prompting user to choose a location to open a document from [CHAR LIMIT=32] -->
<string name="title_open">Open from</string>
<!-- Action bar title prompting user to choose a location to save a document to [CHAR LIMIT=32] -->
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
index d265e0dc8b81..4abb795b267b 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
@@ -16,8 +16,12 @@
package com.android.keyguard;
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.RenderNode;
+import android.view.RenderNodeAnimator;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
@@ -114,10 +118,8 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
enableClipping(false);
setAlpha(1f);
setTranslationY(mAppearAnimationUtils.getStartTranslation());
- animate()
- .setDuration(500)
- .setInterpolator(mAppearAnimationUtils.getInterpolator())
- .translationY(0);
+ AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 500 /* duration */,
+ 0, mAppearAnimationUtils.getInterpolator());
mAppearAnimationUtils.startAnimation2d(mViews,
new Runnable() {
@Override
@@ -131,10 +133,8 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
public boolean startDisappearAnimation(final Runnable finishRunnable) {
enableClipping(false);
setTranslationY(0);
- animate()
- .setDuration(280)
- .setInterpolator(mDisappearAnimationUtils.getInterpolator())
- .translationY(mDisappearYTranslation);
+ AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */,
+ mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator());
mDisappearAnimationUtils.startAnimation2d(mViews,
new Runnable() {
@Override
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 356842974cdc..b000e26a7658 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -17,6 +17,7 @@ package com.android.keyguard;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
@@ -27,6 +28,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
+import android.view.RenderNodeAnimator;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
@@ -334,10 +336,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
enableClipping(false);
setAlpha(1f);
setTranslationY(mAppearAnimationUtils.getStartTranslation());
- animate()
- .setDuration(500)
- .setInterpolator(mAppearAnimationUtils.getInterpolator())
- .translationY(0);
+ AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 500 /* duration */,
+ 0, mAppearAnimationUtils.getInterpolator());
mAppearAnimationUtils.startAnimation2d(
mLockPatternView.getCellStates(),
new Runnable() {
@@ -362,10 +362,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
mLockPatternView.clearPattern();
enableClipping(false);
setTranslationY(0);
- animate()
- .setDuration(300)
- .setInterpolator(mDisappearAnimationUtils.getInterpolator())
- .translationY(-mDisappearAnimationUtils.getStartTranslation());
+ AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 300 /* duration */,
+ -mDisappearAnimationUtils.getStartTranslation(),
+ mDisappearAnimationUtils.getInterpolator());
mDisappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
new Runnable() {
@Override
@@ -398,43 +397,16 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
long duration, float translationY, final boolean appearing,
Interpolator interpolator,
final Runnable finishListener) {
- if (appearing) {
- animatedCell.scale = 0.0f;
- animatedCell.alpha = 1.0f;
- }
- animatedCell.translateY = appearing ? translationY : 0;
- ValueAnimator animator = ValueAnimator.ofFloat(animatedCell.translateY,
- appearing ? 0 : translationY);
- animator.setInterpolator(interpolator);
- animator.setDuration(duration);
- animator.setStartDelay(delay);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float animatedFraction = animation.getAnimatedFraction();
- if (appearing) {
- animatedCell.scale = animatedFraction;
- } else {
- animatedCell.alpha = 1 - animatedFraction;
- }
- animatedCell.translateY = (float) animation.getAnimatedValue();
- mLockPatternView.invalidate();
- }
- });
+ mLockPatternView.startCellStateAnimation(animatedCell,
+ 1f, appearing ? 1f : 0f, /* alpha */
+ appearing ? translationY : 0f, appearing ? 0f : translationY, /* translation */
+ appearing ? 0f : 1f, 1f /* scale */,
+ delay, duration, interpolator, finishListener);
if (finishListener != null) {
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- finishListener.run();
- }
- });
-
// Also animate the Emergency call
mAppearAnimationUtils.createAnimation(mEcaView, delay, duration, translationY,
appearing, interpolator, null);
}
- animator.start();
- mLockPatternView.invalidate();
}
@Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 3c5dae3eda36..6baa4b374345 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -679,7 +679,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
if (resolvedActivities.get(0).activityInfo.exported) {
intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, mPrintJob);
intent.putExtra(PrintService.EXTRA_PRINTER_INFO, printer);
- intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO,
+ intent.putExtra(PrintService.EXTRA_PRINT_DOCUMENT_INFO,
mPrintedDocument.getDocumentInfo().info);
// This is external activity and may not be there.
diff --git a/packages/SettingsLib/src/com/android/settingslib/animation/AppearAnimationUtils.java b/packages/SettingsLib/src/com/android/settingslib/animation/AppearAnimationUtils.java
index 441474d7fc32..df76125a99f2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/animation/AppearAnimationUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/animation/AppearAnimationUtils.java
@@ -16,11 +16,18 @@
package com.android.settingslib.animation;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.view.RenderNodeAnimator;
import android.view.View;
+import android.view.ViewPropertyAnimator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import com.android.internal.widget.LockPatternView;
import com.android.settingslib.R;
/**
@@ -174,24 +181,63 @@ public class AppearAnimationUtils implements AppearAnimationCreator<View> {
}
@Override
- public void createAnimation(View view, long delay, long duration, float translationY,
- boolean appearing, Interpolator interpolator, Runnable endRunnable) {
+ public void createAnimation(final View view, long delay, long duration, float translationY,
+ boolean appearing, Interpolator interpolator, final Runnable endRunnable) {
if (view != null) {
view.setAlpha(appearing ? 0f : 1.0f);
view.setTranslationY(appearing ? translationY : 0);
- view.animate()
- .alpha(appearing ? 1f : 0f)
- .translationY(appearing ? 0 : translationY)
- .setInterpolator(interpolator)
- .setDuration(duration)
- .setStartDelay(delay);
+ Animator alphaAnim;
+ float targetAlpha = appearing ? 1f : 0f;
+ if (view.isHardwareAccelerated()) {
+ RenderNodeAnimator alphaAnimRt = new RenderNodeAnimator(RenderNodeAnimator.ALPHA,
+ targetAlpha);
+ alphaAnimRt.setTarget(view);
+ alphaAnim = alphaAnimRt;
+ } else {
+ alphaAnim = ObjectAnimator.ofFloat(view, View.ALPHA, view.getAlpha(), targetAlpha);
+ }
+ alphaAnim.setInterpolator(interpolator);
+ alphaAnim.setDuration(duration);
+ alphaAnim.setStartDelay(delay);
if (view.hasOverlappingRendering()) {
- view.animate().withLayer();
+ view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ alphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ });
}
if (endRunnable != null) {
- view.animate().withEndAction(endRunnable);
+ alphaAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endRunnable.run();
+ }
+ });
}
+ alphaAnim.start();
+ startTranslationYAnimation(view, delay, duration, appearing ? 0 : translationY,
+ interpolator);
+ }
+ }
+
+ public static void startTranslationYAnimation(View view, long delay, long duration,
+ float endTranslationY, Interpolator interpolator) {
+ Animator translationAnim;
+ if (view.isHardwareAccelerated()) {
+ RenderNodeAnimator translationAnimRt = new RenderNodeAnimator(
+ RenderNodeAnimator.TRANSLATION_Y, endTranslationY);
+ translationAnimRt.setTarget(view);
+ translationAnim = translationAnimRt;
+ } else {
+ translationAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
+ view.getTranslationY(), endTranslationY);
}
+ translationAnim.setInterpolator(interpolator);
+ translationAnim.setDuration(duration);
+ translationAnim.setStartDelay(delay);
+ translationAnim.start();
}
public class AppearAnimationProperties {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index dd97dac6029e..81ab4db46197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -26,11 +26,13 @@ import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
@@ -42,6 +44,7 @@ import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardStatusView;
+import com.android.systemui.DejankUtils;
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
@@ -79,6 +82,8 @@ public class NotificationPanelView extends PanelView implements
private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
+ private static final Rect mDummyDirtyRect = new Rect(0, 0, 1, 1);
+
public static final long DOZE_ANIMATION_DURATION = 700;
private KeyguardAffordanceHelper mAfforanceHelper;
@@ -1777,7 +1782,22 @@ public class NotificationPanelView extends PanelView implements
mIsExpanding = false;
mScrollYOverride = -1;
if (isFullyCollapsed()) {
- setListening(false);
+ DejankUtils.postAfterTraversal(new Runnable() {
+ @Override
+ public void run() {
+ setListening(false);
+ }
+ });
+
+ // Workaround b/22639032: Make sure we invalidate something because else RenderThread
+ // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go
+ // ahead with rendering and we jank.
+ postOnAnimation(new Runnable() {
+ @Override
+ public void run() {
+ getParent().invalidateChild(NotificationPanelView.this, mDummyDirtyRect);
+ }
+ });
} else {
setListening(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 5d58cd0aac2e..7ee47dff37e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -789,7 +789,9 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
mQsDetailHeader.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- detail.setToggleState(!mQsDetailHeaderSwitch.isChecked());
+ boolean checked = !mQsDetailHeaderSwitch.isChecked();
+ mQsDetailHeaderSwitch.setChecked(checked);
+ detail.setToggleState(checked);
}
});
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 4102f37ff386..447fe871754f 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -859,26 +859,35 @@ public final class BatteryStatsService extends IBatteryStats.Stub
public boolean isOnBattery() {
return mStats.isOnBattery();
}
-
- public void setBatteryState(int status, int health, int plugType, int level,
- int temp, int volt) {
- enforceCallingPermission();
- synchronized (mStats) {
- final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE;
- if (mStats.isOnBattery() == onBattery) {
- // The battery state has not changed, so we don't need to sync external
- // stats immediately.
- mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
- return;
- }
- }
- // Sync external stats first as the battery has changed states. If we don't sync
- // immediately here, we may not collect the relevant data later.
- updateExternalStats("battery-state", UPDATE_ALL);
- synchronized (mStats) {
- mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
- }
+ @Override
+ public void setBatteryState(final int status, final int health, final int plugType,
+ final int level, final int temp, final int volt) {
+ enforceCallingPermission();
+
+ // BatteryService calls us here and we may update external state. It would be wrong
+ // to block such a low level service like BatteryService on external stats like WiFi.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mStats) {
+ final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE;
+ if (mStats.isOnBattery() == onBattery) {
+ // The battery state has not changed, so we don't need to sync external
+ // stats immediately.
+ mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
+ return;
+ }
+ }
+
+ // Sync external stats first as the battery has changed states. If we don't sync
+ // immediately here, we may not collect the relevant data later.
+ updateExternalStats("battery-state", UPDATE_ALL);
+ synchronized (mStats) {
+ mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
+ }
+ }
+ });
}
public long getAwakeTimeBattery() {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 395aa275f412..cd982d32d45d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1520,9 +1520,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
synchronized (mRulesLock) {
- final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
- if (oldPolicy != policy) {
- setUidPolicyUncheckedLocked(uid, policy, true);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+ if (oldPolicy != policy) {
+ setUidPolicyUncheckedLocked(uid, policy, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
}