OSDN Git Service

Implement tools:list_item for RecyclerView. [DO NOT MERGE]
authorDeepanshu Gupta <deepanshu@google.com>
Tue, 7 Jul 2015 01:31:20 +0000 (18:31 -0700)
committerDeepanshu Gupta <deepanshu@google.com>
Tue, 25 Aug 2015 19:06:50 +0000 (12:06 -0700)
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)

tools/layoutlib/bridge/src/android/view/BridgeInflater.java
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java

index 72d660c..cbbc463 100644 (file)
@@ -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);
+            }
         }
     }
 
index 1af6998..661c08b 100644 (file)
@@ -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";
 }
index 5e5b3c9..4182cd9 100644 (file)
@@ -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);
         }
     }
 
index 9ef5156..e6d8ae9 100644 (file)
@@ -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<SessionParams> {
                     }
                 }
             }
-        } 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<SessionParams> {
     }
 
     /**
-     * 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.
index b324451..b78b613 100644 (file)
@@ -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
      */