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.ILayoutLog; 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.common.util.ReflectionUtils; 24 import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.content.Context; 29 import android.view.View; 30 31 import static com.android.layoutlib.common.util.ReflectionUtils.getCause; 32 import static com.android.layoutlib.common.util.ReflectionUtils.getMethod; 33 import static com.android.layoutlib.common.util.ReflectionUtils.invoke; 34 35 /** 36 * Utility class for working with android.support.v7.widget.RecyclerView and 37 * androidx.widget.RecyclerView 38 */ 39 public class RecyclerViewUtil { 40 public static final String[] CN_RECYCLER_VIEW = { 41 "android.support.v7.widget.RecyclerView", 42 "androidx.recyclerview.widget.RecyclerView" 43 }; 44 45 private static final Class<?>[] LLM_CONSTRUCTOR_SIGNATURE = new Class<?>[]{Context.class}; 46 47 /** 48 * Tries to create an Adapter ({@code android.support.v7.widget.RecyclerView.Adapter} and a 49 * LayoutManager {@code RecyclerView.LayoutManager} and assign these to the {@code RecyclerView} 50 * that is passed. 51 * <p/> 52 * Any exceptions thrown during the process are logged in {@link Bridge#getLog()} 53 */ setAdapter(@onNull View recyclerView, @NonNull BridgeContext context, @NonNull LayoutlibCallback layoutlibCallback, int adapterLayout, int itemCount)54 public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context, 55 @NonNull LayoutlibCallback layoutlibCallback, int adapterLayout, int itemCount) { 56 Class<?> recyclerViewClass = 57 ReflectionUtils.getParentClass(recyclerView, RecyclerViewUtil.CN_RECYCLER_VIEW); 58 if (recyclerViewClass == null) { 59 Bridge.getLog().error(ILayoutLog.TAG_BROKEN, 60 "Unable to setup RecyclerView. No parent found.", null, null, null); 61 return; 62 } 63 String recyclerViewClassName = recyclerViewClass.getName(); 64 String recyclerViewPackageName = recyclerViewClass.getPackage().getName(); 65 String adapterClassName = recyclerViewClassName + "$Adapter"; 66 String layoutMgrClassName = recyclerViewClassName + "$LayoutManager"; 67 68 try { 69 setLayoutManager(recyclerView, recyclerViewPackageName, layoutMgrClassName, context, layoutlibCallback); 70 Object adapter = createAdapter(layoutlibCallback, adapterClassName); 71 if (adapter != null) { 72 setProperty(recyclerView, adapterClassName, adapter, "setAdapter"); 73 setProperty(adapter, int.class, adapterLayout, "setLayoutId"); 74 75 if (itemCount != -1) { 76 setProperty(adapter, int.class, itemCount, "setItemCount"); 77 } 78 } 79 } catch (ReflectionException e) { 80 Throwable cause = getCause(e); 81 Bridge.getLog().error(ILayoutLog.TAG_BROKEN, 82 "Error occurred while trying to setup RecyclerView.", cause, null, null); 83 } 84 } 85 setLayoutManager(@onNull View recyclerView, @NonNull String recyclerViewPackageName, @NonNull String layoutMgrClassName, @NonNull BridgeContext context, @NonNull LayoutlibCallback callback)86 private static void setLayoutManager(@NonNull View recyclerView, 87 @NonNull String recyclerViewPackageName, 88 @NonNull String layoutMgrClassName, @NonNull BridgeContext context, 89 @NonNull LayoutlibCallback callback) throws ReflectionException { 90 if (getLayoutManager(recyclerView) == null) { 91 String linearLayoutMgrClassManager = recyclerViewPackageName + ".LinearLayoutManager"; 92 // Only set the layout manager if not already set by the recycler view. 93 Object layoutManager = 94 createLayoutManager(context, linearLayoutMgrClassManager, callback); 95 if (layoutManager != null) { 96 setProperty(recyclerView, layoutMgrClassName, layoutManager, "setLayoutManager"); 97 } 98 } 99 } 100 101 /** Creates a LinearLayoutManager using the provided context. */ 102 @Nullable createLayoutManager(@onNull Context context, @NonNull String linearLayoutMgrClassName, @NonNull LayoutlibCallback callback)103 private static Object createLayoutManager(@NonNull Context context, 104 @NonNull String linearLayoutMgrClassName, @NonNull LayoutlibCallback callback) 105 throws ReflectionException { 106 try { 107 return callback.loadClass(linearLayoutMgrClassName, LLM_CONSTRUCTOR_SIGNATURE, 108 new Object[]{context}); 109 } catch (Exception e) { 110 throw new ReflectionException(e); 111 } 112 } 113 114 @Nullable getLayoutManager(View recyclerView)115 private static Object getLayoutManager(View recyclerView) throws ReflectionException { 116 return invoke(getMethod(recyclerView.getClass(), "getLayoutManager"), recyclerView); 117 } 118 119 @Nullable createAdapter(@onNull LayoutlibCallback layoutlibCallback, @NonNull String adapterClassName)120 private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback, 121 @NonNull String adapterClassName) throws ReflectionException { 122 try { 123 return layoutlibCallback.loadClass(adapterClassName, new Class[0], new Object[0]); 124 } catch (Exception e) { 125 throw new ReflectionException(e); 126 } 127 } 128 setProperty(@onNull Object object, @NonNull String propertyClassName, @NonNull Object propertyValue, @NonNull String propertySetter)129 private static void setProperty(@NonNull Object object, @NonNull String propertyClassName, 130 @NonNull Object propertyValue, @NonNull String propertySetter) 131 throws ReflectionException { 132 Class<?> propertyClass = ReflectionUtils.getClassInstance(propertyValue, propertyClassName); 133 setProperty(object, propertyClass, propertyValue, propertySetter); 134 } 135 setProperty(@onNull Object object, @NonNull Class<?> propertyClass, @Nullable Object propertyValue, @NonNull String propertySetter)136 private static void setProperty(@NonNull Object object, @NonNull Class<?> propertyClass, 137 @Nullable Object propertyValue, @NonNull String propertySetter) 138 throws ReflectionException { 139 invoke(getMethod(object.getClass(), propertySetter, propertyClass), object, propertyValue); 140 } 141 142 } 143