1 /* 2 * Copyright (C) 2011 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.impl.binding; 18 19 import com.android.ide.common.rendering.api.AdapterBinding; 20 import com.android.ide.common.rendering.api.DataBindingItem; 21 import com.android.ide.common.rendering.api.IProjectCallback; 22 import com.android.ide.common.rendering.api.LayoutLog; 23 import com.android.ide.common.rendering.api.ResourceReference; 24 import com.android.ide.common.rendering.api.IProjectCallback.ViewAttribute; 25 import com.android.layoutlib.bridge.Bridge; 26 import com.android.layoutlib.bridge.android.BridgeContext; 27 import com.android.layoutlib.bridge.impl.RenderAction; 28 import com.android.util.Pair; 29 30 import android.database.DataSetObserver; 31 import android.view.View; 32 import android.view.ViewGroup; 33 import android.widget.AdapterView; 34 import android.widget.Checkable; 35 import android.widget.ImageView; 36 import android.widget.TextView; 37 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.List; 41 42 /** 43 * Base adapter to do fake data binding in {@link AdapterView} objects. 44 */ 45 public class BaseAdapter { 46 47 /** 48 * This is the items provided by the adapter. They are dynamically generated. 49 */ 50 protected final static class AdapterItem { 51 private final DataBindingItem mItem; 52 private final int mType; 53 private final int mFullPosition; 54 private final int mPositionPerType; 55 private List<AdapterItem> mChildren; 56 57 protected AdapterItem(DataBindingItem item, int type, int fullPosition, 58 int positionPerType) { 59 mItem = item; 60 mType = type; 61 mFullPosition = fullPosition; 62 mPositionPerType = positionPerType; 63 } 64 65 void addChild(AdapterItem child) { 66 if (mChildren == null) { 67 mChildren = new ArrayList<AdapterItem>(); 68 } 69 70 mChildren.add(child); 71 } 72 73 List<AdapterItem> getChildren() { 74 if (mChildren != null) { 75 return mChildren; 76 } 77 78 return Collections.emptyList(); 79 } 80 81 int getType() { 82 return mType; 83 } 84 85 int getFullPosition() { 86 return mFullPosition; 87 } 88 89 int getPositionPerType() { 90 return mPositionPerType; 91 } 92 93 DataBindingItem getDataBindingItem() { 94 return mItem; 95 } 96 } 97 98 private final AdapterBinding mBinding; 99 private final IProjectCallback mCallback; 100 private final ResourceReference mAdapterRef; 101 private boolean mSkipCallbackParser = false; 102 103 protected final List<AdapterItem> mItems = new ArrayList<AdapterItem>(); 104 105 protected BaseAdapter(ResourceReference adapterRef, AdapterBinding binding, 106 IProjectCallback callback) { 107 mAdapterRef = adapterRef; 108 mBinding = binding; 109 mCallback = callback; 110 } 111 112 // ------- Some Adapter method used by all children classes. 113 114 public boolean areAllItemsEnabled() { 115 return true; 116 } 117 118 public boolean hasStableIds() { 119 return true; 120 } 121 122 public boolean isEmpty() { 123 return mItems.size() == 0; 124 } 125 126 public void registerDataSetObserver(DataSetObserver observer) { 127 // pass 128 } 129 130 public void unregisterDataSetObserver(DataSetObserver observer) { 131 // pass 132 } 133 134 // ------- 135 136 137 protected AdapterBinding getBinding() { 138 return mBinding; 139 } 140 141 protected View getView(AdapterItem item, AdapterItem parentItem, View convertView, 142 ViewGroup parent) { 143 // we don't care about recycling here because we never scroll. 144 DataBindingItem dataBindingItem = item.getDataBindingItem(); 145 146 BridgeContext context = RenderAction.getCurrentContext(); 147 148 Pair<View, Boolean> pair = context.inflateView(dataBindingItem.getViewReference(), 149 parent, false /*attachToRoot*/, mSkipCallbackParser); 150 151 View view = pair.getFirst(); 152 mSkipCallbackParser |= pair.getSecond(); 153 154 if (view != null) { 155 fillView(context, view, item, parentItem); 156 } else { 157 // create a text view to display an error. 158 TextView tv = new TextView(context); 159 tv.setText("Unable to find layout: " + dataBindingItem.getViewReference().getName()); 160 view = tv; 161 } 162 163 return view; 164 } 165 166 private void fillView(BridgeContext context, View view, AdapterItem item, 167 AdapterItem parentItem) { 168 if (view instanceof ViewGroup) { 169 ViewGroup group = (ViewGroup) view; 170 final int count = group.getChildCount(); 171 for (int i = 0 ; i < count ; i++) { 172 fillView(context, group.getChildAt(i), item, parentItem); 173 } 174 } else { 175 int id = view.getId(); 176 if (id != 0) { 177 ResourceReference resolvedRef = context.resolveId(id); 178 if (resolvedRef != null) { 179 int fullPosition = item.getFullPosition(); 180 int positionPerType = item.getPositionPerType(); 181 int fullParentPosition = parentItem != null ? parentItem.getFullPosition() : 0; 182 int parentPositionPerType = parentItem != null ? 183 parentItem.getPositionPerType() : 0; 184 185 if (view instanceof TextView) { 186 TextView tv = (TextView) view; 187 Object value = mCallback.getAdapterItemValue( 188 mAdapterRef, context.getViewKey(view), 189 item.getDataBindingItem().getViewReference(), 190 fullPosition, positionPerType, 191 fullParentPosition, parentPositionPerType, 192 resolvedRef, ViewAttribute.TEXT, tv.getText().toString()); 193 if (value != null) { 194 if (value.getClass() != ViewAttribute.TEXT.getAttributeClass()) { 195 Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format( 196 "Wrong Adapter Item value class for TEXT. Expected String, got %s", 197 value.getClass().getName()), null); 198 } else { 199 tv.setText((String) value); 200 } 201 } 202 } 203 204 if (view instanceof Checkable) { 205 Checkable cb = (Checkable) view; 206 207 Object value = mCallback.getAdapterItemValue( 208 mAdapterRef, context.getViewKey(view), 209 item.getDataBindingItem().getViewReference(), 210 fullPosition, positionPerType, 211 fullParentPosition, parentPositionPerType, 212 resolvedRef, ViewAttribute.IS_CHECKED, cb.isChecked()); 213 if (value != null) { 214 if (value.getClass() != ViewAttribute.IS_CHECKED.getAttributeClass()) { 215 Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format( 216 "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s", 217 value.getClass().getName()), null); 218 } else { 219 cb.setChecked((Boolean) value); 220 } 221 } 222 } 223 224 if (view instanceof ImageView) { 225 ImageView iv = (ImageView) view; 226 227 Object value = mCallback.getAdapterItemValue( 228 mAdapterRef, context.getViewKey(view), 229 item.getDataBindingItem().getViewReference(), 230 fullPosition, positionPerType, 231 fullParentPosition, parentPositionPerType, 232 resolvedRef, ViewAttribute.SRC, iv.getDrawable()); 233 if (value != null) { 234 if (value.getClass() != ViewAttribute.SRC.getAttributeClass()) { 235 Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format( 236 "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s", 237 value.getClass().getName()), null); 238 } else { 239 // FIXME 240 } 241 } 242 } 243 } 244 } 245 } 246 } 247 } 248