From b1e21330f82ec0940658b16dfe7e14e8da8c5d33 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Mon, 6 Jul 2015 18:31:20 -0700 Subject: Implement tools:list_item for RecyclerView. [DO NOT MERGE] It's now possible to use tools:list_item attribute for RecyclerView to point to a default layout, rather than always using a TextView. Change-Id: I5d522b2f0ca38b420fddfcb0f73a26d95707da79 (cherry picked from commit 61f23e9bf7d784e7a52168196758c4f6c6853e77) --- .../bridge/src/android/view/BridgeInflater.java | 31 ++++++++--- .../android/layoutlib/bridge/BridgeConstants.java | 3 +- .../bridge/android/support/RecyclerViewUtil.java | 60 +++++++++++++--------- .../layoutlib/bridge/impl/RenderSessionImpl.java | 20 +------- .../layoutlib/bridge/util/ReflectionUtils.java | 16 ++++++ 5 files changed, 79 insertions(+), 51 deletions(-) (limited to 'tools') diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index 72d660c..cbbc463 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -25,7 +25,9 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.BridgeConstants; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; +import com.android.layoutlib.bridge.android.support.RecyclerViewUtil; import com.android.layoutlib.bridge.impl.ParserFactory; +import com.android.layoutlib.bridge.util.ReflectionUtils; import com.android.resources.ResourceType; import com.android.util.Pair; @@ -112,14 +114,8 @@ public final class BridgeInflater extends LayoutInflater { } // Finally try again using the custom view loader - try { - if (view == null) { - view = loadCustomView(name, attrs); - } - } catch (ClassNotFoundException e) { - // If the class was not found, we throw the exception directly, because this - // method is already expected to throw it. - throw e; + if (view == null) { + view = loadCustomView(name, attrs); } } catch (Exception e) { // Wrap the real exception in a ClassNotFoundException, so that the calling method @@ -242,6 +238,25 @@ public final class BridgeInflater extends LayoutInflater { bc.setScrollYPos(view, value); } } + if (ReflectionUtils.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) { + Integer resourceId = null; + String attrVal = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, + BridgeConstants.ATTR_LIST_ITEM); + if (attrVal != null && !attrVal.isEmpty()) { + ResourceValue resValue = bc.getRenderResources().findResValue(attrVal, false); + if (resValue.isFramework()) { + resourceId = Bridge.getResourceId(resValue.getResourceType(), + resValue.getName()); + } else { + resourceId = mLayoutlibCallback.getResourceId(resValue.getResourceType(), + resValue.getName()); + } + } + if (resourceId == null) { + resourceId = 0; + } + RecyclerViewUtil.setAdapter(view, bc, mLayoutlibCallback, resourceId); + } } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java index 1af6998..661c08b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java @@ -41,6 +41,7 @@ public class BridgeConstants { /** App auto namespace */ public final static String NS_APP_RES_AUTO = "http://schemas.android.com/apk/res-auto"; + public final static String NS_TOOLS_URI = "http://schemas.android.com/tools"; public final static String R = "com.android.internal.R"; @@ -50,5 +51,5 @@ public class BridgeConstants { public final static String WRAP_CONTENT = "wrap_content"; /** Attribute in the tools namespace used to specify layout manager for RecyclerView. */ - public static final String ATTR_LAYOUT_MANAGER_TYPE = "layoutManager"; + public static final String ATTR_LIST_ITEM = "list_item"; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java index 5e5b3c9..4182cd9 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java @@ -18,7 +18,6 @@ package com.android.layoutlib.bridge.android.support; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutlibCallback; -import com.android.ide.common.rendering.api.SessionParams; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.RenderParamsFlags; @@ -37,7 +36,6 @@ import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke; /** * Utility class for working with android.support.v7.widget.RecyclerView */ -@SuppressWarnings("SpellCheckingInspection") // for "recycler". public class RecyclerViewUtil { private static final String RV_PKG_PREFIX = "android.support.v7.widget."; @@ -57,23 +55,34 @@ public class RecyclerViewUtil { * Any exceptions thrown during the process are logged in {@link Bridge#getLog()} */ public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context, - @NonNull SessionParams params) { + @NonNull LayoutlibCallback layoutlibCallback, int adapterLayout) { try { - setLayoutManager(recyclerView, context, params.getLayoutlibCallback()); - Object adapter = createAdapter(params); - setProperty(recyclerView, CN_ADAPTER, adapter, "setAdapter"); + setLayoutManager(recyclerView, context, layoutlibCallback); + Object adapter = createAdapter(layoutlibCallback); + if (adapter != null) { + setProperty(recyclerView, CN_ADAPTER, adapter, "setAdapter"); + setProperty(adapter, int.class, adapterLayout, "setLayoutId"); + } } catch (ReflectionException e) { + Throwable cause = getCause(e); Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Error occured while trying to setup RecyclerView.", e, null); + "Error occurred while trying to setup RecyclerView.", cause, null); } } + private static Throwable getCause(Throwable throwable) { + Throwable cause = throwable.getCause(); + return cause == null ? throwable : cause; + } + private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context, @NonNull LayoutlibCallback callback) throws ReflectionException { if (getLayoutManager(recyclerView) == null) { // Only set the layout manager if not already set by the recycler view. Object layoutManager = createLayoutManager(context, callback); - setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager"); + if (layoutManager != null) { + setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager"); + } } } @@ -84,41 +93,46 @@ public class RecyclerViewUtil { throws ReflectionException { try { return callback.loadView(CN_LINEAR_LAYOUT_MANAGER, LLM_CONSTRUCTOR_SIGNATURE, - new Object[]{ context}); + new Object[]{context}); } catch (Exception e) { throw new ReflectionException(e); } } @Nullable - private static Object getLayoutManager(View recyclerview) throws ReflectionException { - Method getLayoutManager = getMethod(recyclerview.getClass(), "getLayoutManager"); - return getLayoutManager != null ? invoke(getLayoutManager, recyclerview) : null; + private static Object getLayoutManager(View recyclerView) throws ReflectionException { + Method getLayoutManager = getMethod(recyclerView.getClass(), "getLayoutManager"); + return getLayoutManager != null ? invoke(getLayoutManager, recyclerView) : null; } @Nullable - private static Object createAdapter(@NonNull SessionParams params) throws ReflectionException { - Boolean ideSupport = params.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT); + private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback) + throws ReflectionException { + Boolean ideSupport = + layoutlibCallback.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT); if (ideSupport != Boolean.TRUE) { return null; } try { - return params.getLayoutlibCallback().loadView(CN_ADAPTER, new Class[0], new Object[0]); + return layoutlibCallback.loadClass(CN_ADAPTER, new Class[0], new Object[0]); } catch (Exception e) { throw new ReflectionException(e); } } - private static void setProperty(@NonNull View recyclerView, @NonNull String propertyClassName, + private static void setProperty(@NonNull Object object, @NonNull String propertyClassName, + @NonNull Object propertyValue, @NonNull String propertySetter) + throws ReflectionException { + Class propertyClass = getClassInstance(propertyValue, propertyClassName); + setProperty(object, propertyClass, propertyValue, propertySetter); + } + + private static void setProperty(@NonNull Object object, @NonNull Class propertyClass, @Nullable Object propertyValue, @NonNull String propertySetter) throws ReflectionException { - if (propertyValue != null) { - Class layoutManagerClass = getClassInstance(propertyValue, propertyClassName); - Method setLayoutManager = getMethod(recyclerView.getClass(), - propertySetter, layoutManagerClass); - if (setLayoutManager != null) { - invoke(setLayoutManager, recyclerView, propertyValue); - } + Method setter = getMethod(object.getClass(), propertySetter, propertyClass); + if (setter != null) { + invoke(setter, object, propertyValue); } } 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 9ef5156..e6d8ae9 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 @@ -45,7 +45,6 @@ import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.android.RenderParamsFlags; import com.android.layoutlib.bridge.android.support.DesignLibUtil; -import com.android.layoutlib.bridge.android.support.RecyclerViewUtil; import com.android.layoutlib.bridge.bars.AppCompatActionBar; import com.android.layoutlib.bridge.bars.BridgeActionBar; import com.android.layoutlib.bridge.bars.Config; @@ -116,6 +115,7 @@ import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLA 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 static com.android.layoutlib.bridge.util.ReflectionUtils.isInstanceOf; /** * Class implementing the render session. @@ -1351,8 +1351,6 @@ public class RenderSessionImpl extends RenderAction { } } } - } else if (isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) { - RecyclerViewUtil.setAdapter(view, getContext(), getParams()); } else if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; final int count = group.getChildCount(); @@ -1457,22 +1455,6 @@ public class RenderSessionImpl extends RenderAction { } /** - * Check if the object is an instance of a class named {@code className}. This doesn't work - * for interfaces. - */ - public static boolean isInstanceOf(Object object, String className) { - Class superClass = object.getClass(); - while (superClass != null) { - String name = superClass.getName(); - if (name.equals(className)) { - return true; - } - superClass = superClass.getSuperclass(); - } - return false; - } - - /** * Sets up a {@link TabHost} object. * @param tabHost the TabHost to setup. * @param layoutlibCallback The project callback object to access the project R class. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java index b324451..b78b613 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java @@ -52,6 +52,22 @@ public class ReflectionUtils { } /** + * Check if the object is an instance of a class named {@code className}. This doesn't work + * for interfaces. + */ + public static boolean isInstanceOf(Object object, String className) { + Class superClass = object.getClass(); + while (superClass != null) { + String name = superClass.getName(); + if (name.equals(className)) { + return true; + } + superClass = superClass.getSuperclass(); + } + return false; + } + + /** * Wraps all reflection related exceptions. Created since ReflectiveOperationException was * introduced in 1.7 and we are still on 1.6 */ -- cgit v1.1