Home | History | Annotate | Download | only in support
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.layoutlib.bridge.android.support;
     18 
     19 import com.android.ide.common.rendering.api.LayoutLog;
     20 import com.android.ide.common.rendering.api.LayoutlibCallback;
     21 import com.android.layoutlib.bridge.Bridge;
     22 import com.android.layoutlib.bridge.android.BridgeContext;
     23 import com.android.layoutlib.bridge.android.RenderParamsFlags;
     24 import com.android.layoutlib.bridge.util.ReflectionUtils;
     25 import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
     26 
     27 import android.annotation.NonNull;
     28 import android.annotation.Nullable;
     29 import android.content.Context;
     30 import android.view.View;
     31 
     32 import static com.android.layoutlib.bridge.util.ReflectionUtils.getCause;
     33 import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
     34 import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
     35 
     36 /**
     37  * Utility class for working with android.support.v7.widget.RecyclerView
     38  */
     39 public class RecyclerViewUtil {
     40 
     41     private static final String RV_PKG_PREFIX = "android.support.v7.widget.";
     42     public static final String CN_RECYCLER_VIEW = RV_PKG_PREFIX + "RecyclerView";
     43     private static final String CN_LAYOUT_MANAGER = CN_RECYCLER_VIEW + "$LayoutManager";
     44     private static final String CN_ADAPTER = CN_RECYCLER_VIEW + "$Adapter";
     45 
     46     // LinearLayoutManager related constants.
     47     private static final String CN_LINEAR_LAYOUT_MANAGER = RV_PKG_PREFIX + "LinearLayoutManager";
     48     private static final Class<?>[] LLM_CONSTRUCTOR_SIGNATURE = new Class<?>[]{Context.class};
     49 
     50     /**
     51      * Tries to create an Adapter ({@code android.support.v7.widget.RecyclerView.Adapter} and a
     52      * LayoutManager {@code RecyclerView.LayoutManager} and assign these to the {@code RecyclerView}
     53      * that is passed.
     54      * <p/>
     55      * Any exceptions thrown during the process are logged in {@link Bridge#getLog()}
     56      */
     57     public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context,
     58             @NonNull LayoutlibCallback layoutlibCallback, int adapterLayout, int itemCount) {
     59         try {
     60             setLayoutManager(recyclerView, context, layoutlibCallback);
     61             Object adapter = createAdapter(layoutlibCallback);
     62             if (adapter != null) {
     63                 setProperty(recyclerView, CN_ADAPTER, adapter, "setAdapter");
     64                 setProperty(adapter, int.class, adapterLayout, "setLayoutId");
     65 
     66                 if (itemCount != -1) {
     67                     setProperty(adapter, int.class, itemCount, "setItemCount");
     68                 }
     69             }
     70         } catch (ReflectionException e) {
     71             Throwable cause = getCause(e);
     72             Bridge.getLog().error(LayoutLog.TAG_BROKEN,
     73                     "Error occurred while trying to setup RecyclerView.", cause, null);
     74         }
     75     }
     76 
     77     private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context,
     78             @NonNull LayoutlibCallback callback) throws ReflectionException {
     79         if (getLayoutManager(recyclerView) == null) {
     80             // Only set the layout manager if not already set by the recycler view.
     81             Object layoutManager = createLayoutManager(context, callback);
     82             if (layoutManager != null) {
     83                 setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager");
     84             }
     85         }
     86     }
     87 
     88     /** Creates a LinearLayoutManager using the provided context. */
     89     @Nullable
     90     private static Object createLayoutManager(@NonNull Context context,
     91             @NonNull LayoutlibCallback callback)
     92             throws ReflectionException {
     93         try {
     94             return callback.loadView(CN_LINEAR_LAYOUT_MANAGER, LLM_CONSTRUCTOR_SIGNATURE,
     95                     new Object[]{context});
     96         } catch (Exception e) {
     97             throw new ReflectionException(e);
     98         }
     99     }
    100 
    101     @Nullable
    102     private static Object getLayoutManager(View recyclerView) throws ReflectionException {
    103         return invoke(getMethod(recyclerView.getClass(), "getLayoutManager"), recyclerView);
    104     }
    105 
    106     @Nullable
    107     private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback)
    108             throws ReflectionException {
    109         Boolean ideSupport =
    110                 layoutlibCallback.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT);
    111         if (ideSupport != Boolean.TRUE) {
    112             return null;
    113         }
    114         try {
    115             return layoutlibCallback.loadClass(CN_ADAPTER, new Class[0], new Object[0]);
    116         } catch (Exception e) {
    117             throw new ReflectionException(e);
    118         }
    119     }
    120 
    121     private static void setProperty(@NonNull Object object, @NonNull String propertyClassName,
    122       @NonNull Object propertyValue, @NonNull String propertySetter)
    123             throws ReflectionException {
    124         Class<?> propertyClass = ReflectionUtils.getClassInstance(propertyValue, propertyClassName);
    125         setProperty(object, propertyClass, propertyValue, propertySetter);
    126     }
    127 
    128     private static void setProperty(@NonNull Object object, @NonNull Class<?> propertyClass,
    129             @Nullable Object propertyValue, @NonNull String propertySetter)
    130             throws ReflectionException {
    131         invoke(getMethod(object.getClass(), propertySetter, propertyClass), object, propertyValue);
    132     }
    133 
    134 }
    135