diff options
author | Xavier Ducrohet <xav@android.com> | 2011-05-13 16:43:37 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2011-05-13 16:43:37 -0700 |
commit | 8c2f85d94145a96f53e9041c609e283be7412a0f (patch) | |
tree | c86bfd0b8703eeb6e6bafd98d76b2dd0e129b179 /tools | |
parent | ba4990c77cbc643809f1d0e74fabe5a1d1d1c3dc (diff) | |
parent | d00541603fbbb4e38264e75a2b6abf8705efeb49 (diff) | |
download | frameworks_base-8c2f85d94145a96f53e9041c609e283be7412a0f.zip frameworks_base-8c2f85d94145a96f53e9041c609e283be7412a0f.tar.gz frameworks_base-8c2f85d94145a96f53e9041c609e283be7412a0f.tar.bz2 |
am d0054160: am 6d040a7f: am 738c5e60: am 2fae858d: LayoutLib: implement data binding for most AdapterView
* commit 'd00541603fbbb4e38264e75a2b6abf8705efeb49':
LayoutLib: implement data binding for most AdapterView
Diffstat (limited to 'tools')
11 files changed, 799 insertions, 52 deletions
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index e6e9647..3c0c9a4 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -25,6 +25,7 @@ import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.SessionParams; +import com.android.ide.common.rendering.api.Result.Status; import com.android.layoutlib.bridge.android.BridgeAssetManager; import com.android.layoutlib.bridge.impl.FontLoader; import com.android.layoutlib.bridge.impl.RenderDrawable; @@ -39,6 +40,9 @@ import android.graphics.Bitmap; import android.graphics.Typeface; import android.graphics.Typeface_Delegate; import android.os.Looper; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; import java.io.File; import java.lang.ref.SoftReference; @@ -196,7 +200,8 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { Capability.EMBEDDED_LAYOUT, Capability.VIEW_MANIPULATION, Capability.PLAY_ANIMATION, - Capability.ANIMATED_VIEW_MANIPULATION); + Capability.ANIMATED_VIEW_MANIPULATION, + Capability.ADAPTER_BINDING); BridgeAssetManager.initSystem(); @@ -369,6 +374,40 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { } } + @Override + public Result getViewParent(Object viewObject) { + if (viewObject instanceof View) { + return Status.SUCCESS.createResult(((View)viewObject).getParent()); + } + + throw new IllegalArgumentException("viewObject is not a View"); + } + + @Override + public Result getViewIndex(Object viewObject) { + if (viewObject instanceof View) { + View view = (View) viewObject; + ViewParent parentView = view.getParent(); + + if (parentView instanceof ViewGroup) { + Status.SUCCESS.createResult(((ViewGroup) parentView).indexOfChild(view)); + } + + return Status.SUCCESS.createResult(); + } + + throw new IllegalArgumentException("viewObject is not a View"); + } + + @Override + public int getViewBaseline(Object viewObject) { + if (viewObject instanceof View) { + return ((View) viewObject).getBaseline(); + } + + throw new IllegalArgumentException("viewObject is not a View"); + } + /** * Returns the lock for the bridge */ diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java index 765fd99..529be97 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java @@ -22,12 +22,10 @@ import com.android.ide.common.rendering.api.RenderParams; import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.ViewInfo; -import com.android.ide.common.rendering.api.Result.Status; import com.android.layoutlib.bridge.impl.RenderSessionImpl; import android.view.View; import android.view.ViewGroup; -import android.view.ViewParent; import java.awt.image.BufferedImage; import java.util.List; @@ -83,31 +81,6 @@ public class BridgeRenderSession extends RenderSession { } @Override - public Result getViewParent(Object viewObject) { - if (viewObject instanceof View) { - return Status.SUCCESS.createResult(((View)viewObject).getParent()); - } - - throw new IllegalArgumentException("viewObject is not a View"); - } - - @Override - public Result getViewIndex(Object viewObject) { - if (viewObject instanceof View) { - View view = (View) viewObject; - ViewParent parentView = view.getParent(); - - if (parentView instanceof ViewGroup) { - Status.SUCCESS.createResult(((ViewGroup) parentView).indexOfChild(view)); - } - - return Status.SUCCESS.createResult(); - } - - throw new IllegalArgumentException("viewObject is not a View"); - } - - @Override public Result render(long timeout) { try { Bridge.prepareThread(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 7d794bd..e536028 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -16,9 +16,11 @@ package com.android.layoutlib.bridge.android; +import com.android.ide.common.rendering.api.ILayoutPullParser; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.RenderResources; +import com.android.ide.common.rendering.api.ResourceReference; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.StyleResourceValue; import com.android.layoutlib.bridge.Bridge; @@ -27,6 +29,10 @@ import com.android.layoutlib.bridge.impl.Stack; import com.android.resources.ResourceType; import com.android.util.Pair; +import org.kxml2.io.KXmlParser; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import android.app.Activity; import android.app.Fragment; import android.content.BroadcastReceiver; @@ -59,11 +65,13 @@ import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; @@ -270,6 +278,107 @@ public final class BridgeContext extends Activity { } + public ResourceReference resolveId(int id) { + // first get the String related to this id in the framework + Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id); + + if (resourceInfo != null) { + return new ResourceReference(resourceInfo.getSecond(), true); + } + + // didn't find a match in the framework? look in the project. + if (mProjectCallback != null) { + resourceInfo = mProjectCallback.resolveResourceId(id); + + if (resourceInfo != null) { + return new ResourceReference(resourceInfo.getSecond(), false); + } + } + + return null; + } + + public Pair<View, Boolean> inflateView(ResourceReference resource, ViewGroup parent, + boolean attachToRoot, boolean skipCallbackParser) { + String layoutName = resource.getName(); + boolean isPlatformLayout = resource.isFramework(); + + if (isPlatformLayout == false && skipCallbackParser == false) { + // check if the project callback can provide us with a custom parser. + ILayoutPullParser parser = mProjectCallback.getParser(layoutName); + if (parser != null) { + BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(parser, + this, resource.isFramework()); + try { + pushParser(blockParser); + return Pair.of( + mBridgeInflater.inflate(blockParser, parent, attachToRoot), + true); + } finally { + popParser(); + } + } + } + + ResourceValue resValue; + if (resource instanceof ResourceValue) { + resValue = (ResourceValue) resource; + } else { + if (isPlatformLayout) { + resValue = mRenderResources.getFrameworkResource(ResourceType.LAYOUT, + resource.getName()); + } else { + resValue = mRenderResources.getProjectResource(ResourceType.LAYOUT, + resource.getName()); + } + } + + if (resValue != null) { + + File xml = new File(resValue.getValue()); + if (xml.isFile()) { + // we need to create a pull parser around the layout XML file, and then + // give that to our XmlBlockParser + try { + KXmlParser parser = new KXmlParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(new FileReader(xml)); + + // set the resource ref to have correct view cookies + mBridgeInflater.setResourceReference(resource); + + BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(parser, + this, resource.isFramework()); + try { + pushParser(blockParser); + return Pair.of( + mBridgeInflater.inflate(blockParser, parent, attachToRoot), + false); + } finally { + popParser(); + } + } catch (XmlPullParserException e) { + Bridge.getLog().error(LayoutLog.TAG_BROKEN, + "Failed to configure parser for " + xml, e, null /*data*/); + // we'll return null below. + } catch (FileNotFoundException e) { + // this shouldn't happen since we check above. + } finally { + mBridgeInflater.setResourceReference(null); + } + } else { + Bridge.getLog().error(LayoutLog.TAG_BROKEN, + String.format("File %s is missing!", xml), null); + } + } else { + Bridge.getLog().error(LayoutLog.TAG_BROKEN, + String.format("Layout %s%s does not exist.", isPlatformLayout ? "android:" : "", + layoutName), null); + } + + return Pair.of(null, false); + } + // ------------- Activity Methods @Override diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java index 5740e8b..d6bbebd 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java @@ -19,6 +19,7 @@ package com.android.layoutlib.bridge.android; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.MergeCookie; +import com.android.ide.common.rendering.api.ResourceReference; import com.android.ide.common.rendering.api.ResourceValue; import com.android.layoutlib.bridge.Bridge; import com.android.resources.ResourceType; @@ -44,6 +45,7 @@ public final class BridgeInflater extends LayoutInflater { private final IProjectCallback mProjectCallback; private boolean mIsInMerge = false; + private ResourceReference mResourceReference; /** * List of class prefixes which are tried first by default. @@ -223,23 +225,33 @@ public final class BridgeInflater extends LayoutInflater { // get the view key Object viewKey = parser.getViewCookie(); - // if there's no view key and the depth is 1 (ie this is the first tag), or 2 - // (this is first item in included merge layout) - // look for a previous parser in the context, and check if this one has a viewkey. - int testDepth = mIsInMerge ? 2 : 1; - if (viewKey == null && parser.getDepth() == testDepth) { + if (viewKey == null) { + int currentDepth = parser.getDepth(); + + // test whether we are in an included file or in a adapter binding view. BridgeXmlBlockParser previousParser = bc.getPreviousParser(); if (previousParser != null) { - viewKey = previousParser.getViewCookie(); + // looks like we inside an embedded layout. + // only apply the cookie of the calling node (<include>) if we are at the + // top level of the embedded layout. If there is a merge tag, then + // skip it and look for the 2nd level + int testDepth = mIsInMerge ? 2 : 1; + if (currentDepth == testDepth) { + viewKey = previousParser.getViewCookie(); + // if we are in a merge, wrap the cookie in a MergeCookie. + if (viewKey != null && mIsInMerge) { + viewKey = new MergeCookie(viewKey); + } + } + } else if (mResourceReference != null && currentDepth == 1) { + // else if there's a resource reference, this means we are in an adapter + // binding case. Set the resource ref as the view cookie only for the top + // level view. + viewKey = mResourceReference; } } if (viewKey != null) { - if (testDepth == 2) { - // include-merge case - viewKey = new MergeCookie(viewKey); - } - bc.addViewKey(view, viewKey); } } @@ -250,6 +262,10 @@ public final class BridgeInflater extends LayoutInflater { mIsInMerge = isInMerge; } + public void setResourceReference(ResourceReference reference) { + mResourceReference = reference; + } + @Override public LayoutInflater cloneInContext(Context newContext) { return new BridgeInflater(this, newContext); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java index 5e5aeb1..273e493 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java @@ -232,9 +232,8 @@ public final class BridgeResources extends Resources { try { // check if the current parser can provide us with a custom parser. - BridgeXmlBlockParser currentParser = mContext.getCurrentParser(); - if (currentParser != null) { - parser = currentParser.getParser(value.getName()); + if (mPlatformResourceFlag[0] == false) { + parser = mProjectCallback.getParser(value.getName()); } // create a new one manually if needed. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java index 2f54ae6..70dbaa4 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java @@ -69,14 +69,6 @@ public class BridgeXmlBlockParser implements XmlResourceParser { return mPlatformFile; } - public ILayoutPullParser getParser(String layoutName) { - if (mParser instanceof ILayoutPullParser) { - return ((ILayoutPullParser)mParser).getParser(layoutName); - } - - return null; - } - public Object getViewCookie() { if (mParser instanceof ILayoutPullParser) { return ((ILayoutPullParser)mParser).getViewCookie(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index c5eaef9..c53cb23 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -23,12 +23,14 @@ import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP_NO_CHILDREN; import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; +import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.IAnimationListener; import com.android.ide.common.rendering.api.ILayoutPullParser; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.RenderParams; import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.RenderSession; +import com.android.ide.common.rendering.api.ResourceReference; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.SessionParams; @@ -47,6 +49,8 @@ import com.android.layoutlib.bridge.bars.FakeActionBar; import com.android.layoutlib.bridge.bars.PhoneSystemBar; import com.android.layoutlib.bridge.bars.TabletSystemBar; import com.android.layoutlib.bridge.bars.TitleBar; +import com.android.layoutlib.bridge.impl.binding.FakeAdapter; +import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter; import com.android.resources.ResourceType; import com.android.resources.ScreenSize; import com.android.util.Pair; @@ -70,8 +74,13 @@ import android.view.ViewGroup; import android.view.View.AttachInfo; import android.view.View.MeasureSpec; import android.view.ViewGroup.LayoutParams; +import android.widget.AbsListView; +import android.widget.AbsSpinner; +import android.widget.AdapterView; +import android.widget.ExpandableListView; import android.widget.FrameLayout; import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.QuickContactBadge; import android.widget.TabHost; import android.widget.TabWidget; @@ -283,7 +292,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } - // content frame mContentRoot = new FrameLayout(context); layoutParams = new LinearLayout.LayoutParams( @@ -314,6 +322,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { View view = mInflater.inflate(mBlockParser, mContentRoot); + // done with the parser, pop it. + context.popParser(); + Fragment_Delegate.setProjectCallback(null); // set the AttachInfo on the root view. @@ -1091,6 +1102,75 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } else if (view instanceof QuickContactBadge) { QuickContactBadge badge = (QuickContactBadge) view; badge.setImageToDefault(); + } else if (view instanceof AdapterView<?>) { + // get the view ID. + int id = view.getId(); + + BridgeContext context = getContext(); + + // get a ResourceReference from the integer ID. + ResourceReference listRef = context.resolveId(id); + + if (listRef != null) { + SessionParams params = getParams(); + AdapterBinding binding = params.getAdapterBindings().get(listRef); + + // if there was no adapter binding, trying to get it from the call back. + if (binding == null) { + binding = params.getProjectCallback().getAdapterBinding(listRef, + context.getViewKey(view), view); + } + + if (binding != null) { + + if (view instanceof AbsListView) { + if ((binding.getFooterCount() > 0 || binding.getHeaderCount() > 0) && + view instanceof ListView) { + ListView list = (ListView) view; + + boolean skipCallbackParser = false; + + int count = binding.getHeaderCount(); + for (int i = 0 ; i < count ; i++) { + Pair<View, Boolean> pair = context.inflateView( + binding.getHeaderAt(i), + list, false /*attachToRoot*/, skipCallbackParser); + if (pair.getFirst() != null) { + list.addHeaderView(pair.getFirst()); + } + + skipCallbackParser |= pair.getSecond(); + } + + count = binding.getFooterCount(); + for (int i = 0 ; i < count ; i++) { + Pair<View, Boolean> pair = context.inflateView( + binding.getFooterAt(i), + list, false /*attachToRoot*/, skipCallbackParser); + if (pair.getFirst() != null) { + list.addFooterView(pair.getFirst()); + } + + skipCallbackParser |= pair.getSecond(); + } + } + + if (view instanceof ExpandableListView) { + ((ExpandableListView) view).setAdapter( + new FakeExpandableAdapter( + listRef, binding, params.getProjectCallback())); + } else { + ((AbsListView) view).setAdapter( + new FakeAdapter( + listRef, binding, params.getProjectCallback())); + } + } else if (view instanceof AbsSpinner) { + ((AbsSpinner) view).setAdapter( + new FakeAdapter( + listRef, binding, params.getProjectCallback())); + } + } + } } else if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup)view; final int count = group.getChildCount(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java index 649160e..96ab30f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java @@ -115,7 +115,7 @@ public final class ResourceHelper { public static ColorStateList getColorStateList(ResourceValue resValue, BridgeContext context) { String value = resValue.getValue(); - if (value != null) { + if (value != null && RenderResources.REFERENCE_NULL.equals(value) == false) { // first check if the value is a file (xml most likely) File f = new File(value); if (f.isFile()) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/BaseAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/BaseAdapter.java new file mode 100644 index 0000000..e0414fe --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/BaseAdapter.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2011 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. + */ + +package com.android.layoutlib.bridge.impl.binding; + +import com.android.ide.common.rendering.api.AdapterBinding; +import com.android.ide.common.rendering.api.DataBindingItem; +import com.android.ide.common.rendering.api.IProjectCallback; +import com.android.ide.common.rendering.api.LayoutLog; +import com.android.ide.common.rendering.api.ResourceReference; +import com.android.ide.common.rendering.api.IProjectCallback.ViewAttribute; +import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.layoutlib.bridge.impl.RenderAction; +import com.android.util.Pair; + +import android.database.DataSetObserver; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.Checkable; +import android.widget.ImageView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Base adapter to do fake data binding in {@link AdapterView} objects. + */ +public class BaseAdapter { + + /** + * This is the items provided by the adapter. They are dynamically generated. + */ + protected final static class AdapterItem { + private final DataBindingItem mItem; + private final int mType; + private final int mFullPosition; + private final int mPositionPerType; + private List<AdapterItem> mChildren; + + protected AdapterItem(DataBindingItem item, int type, int fullPosition, + int positionPerType) { + mItem = item; + mType = type; + mFullPosition = fullPosition; + mPositionPerType = positionPerType; + } + + void addChild(AdapterItem child) { + if (mChildren == null) { + mChildren = new ArrayList<AdapterItem>(); + } + + mChildren.add(child); + } + + List<AdapterItem> getChildren() { + if (mChildren != null) { + return mChildren; + } + + return Collections.emptyList(); + } + + int getType() { + return mType; + } + + int getFullPosition() { + return mFullPosition; + } + + int getPositionPerType() { + return mPositionPerType; + } + + DataBindingItem getDataBindingItem() { + return mItem; + } + } + + private final AdapterBinding mBinding; + private final IProjectCallback mCallback; + private final ResourceReference mAdapterRef; + private boolean mSkipCallbackParser = false; + + protected final List<AdapterItem> mItems = new ArrayList<AdapterItem>(); + + protected BaseAdapter(ResourceReference adapterRef, AdapterBinding binding, + IProjectCallback callback) { + mAdapterRef = adapterRef; + mBinding = binding; + mCallback = callback; + } + + // ------- Some Adapter method used by all children classes. + + public boolean areAllItemsEnabled() { + return true; + } + + public boolean hasStableIds() { + return true; + } + + public boolean isEmpty() { + return mItems.size() == 0; + } + + public void registerDataSetObserver(DataSetObserver observer) { + // pass + } + + public void unregisterDataSetObserver(DataSetObserver observer) { + // pass + } + + // ------- + + + protected AdapterBinding getBinding() { + return mBinding; + } + + protected View getView(AdapterItem item, AdapterItem parentItem, View convertView, + ViewGroup parent) { + // we don't care about recycling here because we never scroll. + DataBindingItem dataBindingItem = item.getDataBindingItem(); + + BridgeContext context = RenderAction.getCurrentContext(); + + Pair<View, Boolean> pair = context.inflateView(dataBindingItem.getViewReference(), + parent, false /*attachToRoot*/, mSkipCallbackParser); + + View view = pair.getFirst(); + mSkipCallbackParser |= pair.getSecond(); + + if (view != null) { + fillView(context, view, item, parentItem); + } else { + // create a text view to display an error. + TextView tv = new TextView(context); + tv.setText("Unable to find layout: " + dataBindingItem.getViewReference().getName()); + view = tv; + } + + return view; + } + + private void fillView(BridgeContext context, View view, AdapterItem item, + AdapterItem parentItem) { + if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup) view; + final int count = group.getChildCount(); + for (int i = 0 ; i < count ; i++) { + fillView(context, group.getChildAt(i), item, parentItem); + } + } else { + int id = view.getId(); + if (id != 0) { + ResourceReference resolvedRef = context.resolveId(id); + if (resolvedRef != null) { + int fullPosition = item.getFullPosition(); + int positionPerType = item.getPositionPerType(); + int fullParentPosition = parentItem != null ? parentItem.getFullPosition() : 0; + int parentPositionPerType = parentItem != null ? + parentItem.getPositionPerType() : 0; + + if (view instanceof TextView) { + TextView tv = (TextView) view; + Object value = mCallback.getAdapterItemValue( + mAdapterRef, context.getViewKey(view), + item.getDataBindingItem().getViewReference(), + fullPosition, positionPerType, + fullParentPosition, parentPositionPerType, + resolvedRef, ViewAttribute.TEXT, tv.getText().toString()); + if (value != null) { + if (value.getClass() != ViewAttribute.TEXT.getAttributeClass()) { + Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format( + "Wrong Adapter Item value class for TEXT. Expected String, got %s", + value.getClass().getName()), null); + } else { + tv.setText((String) value); + } + } + } + + if (view instanceof Checkable) { + Checkable cb = (Checkable) view; + + Object value = mCallback.getAdapterItemValue( + mAdapterRef, context.getViewKey(view), + item.getDataBindingItem().getViewReference(), + fullPosition, positionPerType, + fullParentPosition, parentPositionPerType, + resolvedRef, ViewAttribute.IS_CHECKED, cb.isChecked()); + if (value != null) { + if (value.getClass() != ViewAttribute.IS_CHECKED.getAttributeClass()) { + Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format( + "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s", + value.getClass().getName()), null); + } else { + cb.setChecked((Boolean) value); + } + } + } + + if (view instanceof ImageView) { + ImageView iv = (ImageView) view; + + Object value = mCallback.getAdapterItemValue( + mAdapterRef, context.getViewKey(view), + item.getDataBindingItem().getViewReference(), + fullPosition, positionPerType, + fullParentPosition, parentPositionPerType, + resolvedRef, ViewAttribute.SRC, iv.getDrawable()); + if (value != null) { + if (value.getClass() != ViewAttribute.SRC.getAttributeClass()) { + Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format( + "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s", + value.getClass().getName()), null); + } else { + // FIXME + } + } + } + } + } + } + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java new file mode 100644 index 0000000..c9bb424 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2011 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. + */ + +package com.android.layoutlib.bridge.impl.binding; + +import com.android.ide.common.rendering.api.AdapterBinding; +import com.android.ide.common.rendering.api.DataBindingItem; +import com.android.ide.common.rendering.api.IProjectCallback; +import com.android.ide.common.rendering.api.ResourceReference; + +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListAdapter; +import android.widget.SpinnerAdapter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Fake adapter to do fake data binding in {@link AdapterView} objects for {@link ListAdapter} + * and {@link SpinnerAdapter}. + * + */ +public class FakeAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter { + + // don't use a set because the order is important. + private final List<ResourceReference> mTypes = new ArrayList<ResourceReference>(); + + public FakeAdapter(ResourceReference adapterRef, AdapterBinding binding, + IProjectCallback callback) { + super(adapterRef, binding, callback); + + final int repeatCount = getBinding().getRepeatCount(); + final int itemCount = getBinding().getItemCount(); + + // Need an array to count for each type. + // This is likely too big, but is the max it can be. + int[] typeCount = new int[itemCount]; + + // We put several repeating sets. + for (int r = 0 ; r < repeatCount ; r++) { + // loop on the type of list items, and add however many for each type. + for (DataBindingItem dataBindingItem : getBinding()) { + ResourceReference viewRef = dataBindingItem.getViewReference(); + int typeIndex = mTypes.indexOf(viewRef); + if (typeIndex == -1) { + typeIndex = mTypes.size(); + mTypes.add(viewRef); + } + + int count = dataBindingItem.getCount(); + + int index = typeCount[typeIndex]; + typeCount[typeIndex] += count; + + for (int k = 0 ; k < count ; k++) { + mItems.add(new AdapterItem(dataBindingItem, typeIndex, mItems.size(), index++)); + } + } + } + } + + public boolean isEnabled(int position) { + return true; + } + + public int getCount() { + return mItems.size(); + } + + public Object getItem(int position) { + return mItems.get(position); + } + + public long getItemId(int position) { + return position; + } + + public int getItemViewType(int position) { + return mItems.get(position).getType(); + } + + public View getView(int position, View convertView, ViewGroup parent) { + // we don't care about recycling here because we never scroll. + AdapterItem item = mItems.get(position); + return getView(item, null /*parentGroup*/, convertView, parent); + } + + public int getViewTypeCount() { + return mTypes.size(); + } + + // ---- SpinnerAdapter + + public View getDropDownView(int position, View convertView, ViewGroup parent) { + // pass + return null; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java new file mode 100644 index 0000000..2c492e3 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2011 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. + */ + +package com.android.layoutlib.bridge.impl.binding; + +import com.android.ide.common.rendering.api.AdapterBinding; +import com.android.ide.common.rendering.api.DataBindingItem; +import com.android.ide.common.rendering.api.IProjectCallback; +import com.android.ide.common.rendering.api.ResourceReference; + +import android.view.View; +import android.view.ViewGroup; +import android.widget.ExpandableListAdapter; +import android.widget.HeterogeneousExpandableList; + +import java.util.ArrayList; +import java.util.List; + +public class FakeExpandableAdapter extends BaseAdapter implements ExpandableListAdapter, + HeterogeneousExpandableList { + + // don't use a set because the order is important. + private final List<ResourceReference> mGroupTypes = new ArrayList<ResourceReference>(); + private final List<ResourceReference> mChildrenTypes = new ArrayList<ResourceReference>(); + + public FakeExpandableAdapter(ResourceReference adapterRef, AdapterBinding binding, + IProjectCallback callback) { + super(adapterRef, binding, callback); + + createItems(binding, binding.getItemCount(), binding.getRepeatCount(), mGroupTypes, 1); + } + + private void createItems(Iterable<DataBindingItem> iterable, final int itemCount, + final int repeatCount, List<ResourceReference> types, int depth) { + // Need an array to count for each type. + // This is likely too big, but is the max it can be. + int[] typeCount = new int[itemCount]; + + // we put several repeating sets. + for (int r = 0 ; r < repeatCount ; r++) { + // loop on the type of list items, and add however many for each type. + for (DataBindingItem dataBindingItem : iterable) { + ResourceReference viewRef = dataBindingItem.getViewReference(); + int typeIndex = types.indexOf(viewRef); + if (typeIndex == -1) { + typeIndex = types.size(); + types.add(viewRef); + } + + List<DataBindingItem> children = dataBindingItem.getChildren(); + int count = dataBindingItem.getCount(); + + // if there are children, we use the count as a repeat count for the children. + if (children.size() > 0) { + count = 1; + } + + int index = typeCount[typeIndex]; + typeCount[typeIndex] += count; + + for (int k = 0 ; k < count ; k++) { + AdapterItem item = new AdapterItem(dataBindingItem, typeIndex, mItems.size(), + index++); + mItems.add(item); + + if (children.size() > 0) { + createItems(dataBindingItem, depth + 1); + } + } + } + } + } + + private void createItems(DataBindingItem item, int depth) { + if (depth == 2) { + createItems(item, item.getChildren().size(), item.getCount(), mChildrenTypes, depth); + } + } + + private AdapterItem getChildItem(int groupPosition, int childPosition) { + AdapterItem item = mItems.get(groupPosition); + + List<AdapterItem> children = item.getChildren(); + return children.get(childPosition); + } + + // ---- ExpandableListAdapter + + public int getGroupCount() { + return mItems.size(); + } + + public int getChildrenCount(int groupPosition) { + AdapterItem item = mItems.get(groupPosition); + return item.getChildren().size(); + } + + public Object getGroup(int groupPosition) { + return mItems.get(groupPosition); + } + + public Object getChild(int groupPosition, int childPosition) { + return getChildItem(groupPosition, childPosition); + } + + public View getGroupView(int groupPosition, boolean isExpanded, View convertView, + ViewGroup parent) { + // we don't care about recycling here because we never scroll. + AdapterItem item = mItems.get(groupPosition); + return getView(item, null /*parentItem*/, convertView, parent); + } + + public View getChildView(int groupPosition, int childPosition, boolean isLastChild, + View convertView, ViewGroup parent) { + // we don't care about recycling here because we never scroll. + AdapterItem parentItem = mItems.get(groupPosition); + AdapterItem item = getChildItem(groupPosition, childPosition); + return getView(item, parentItem, convertView, parent); + } + + public long getGroupId(int groupPosition) { + return groupPosition; + } + + public long getChildId(int groupPosition, int childPosition) { + return childPosition; + } + + public long getCombinedGroupId(long groupId) { + return groupId << 16 | 0x0000FFFF; + } + + public long getCombinedChildId(long groupId, long childId) { + return groupId << 16 | childId; + } + + public boolean isChildSelectable(int groupPosition, int childPosition) { + return true; + } + + public void onGroupCollapsed(int groupPosition) { + // pass + } + + public void onGroupExpanded(int groupPosition) { + // pass + } + + // ---- HeterogeneousExpandableList + + public int getChildType(int groupPosition, int childPosition) { + return getChildItem(groupPosition, childPosition).getType(); + } + + public int getChildTypeCount() { + return mChildrenTypes.size(); + } + + public int getGroupType(int groupPosition) { + return mItems.get(groupPosition).getType(); + } + + public int getGroupTypeCount() { + return mGroupTypes.size(); + } +} |