/******************************************************************************* * Copyright 2012-present Pixate, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.pixate.freestyle.styling.adapters; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import android.view.View; import android.widget.ListView; import android.widget.Spinner; import com.pixate.freestyle.PXAdapterInvocationHandler; import com.pixate.freestyle.annotations.PXDocElement; import com.pixate.freestyle.styling.stylers.PXDividerStyler; import com.pixate.freestyle.styling.stylers.PXStyler; import com.pixate.freestyle.styling.virtualStyleables.PXVirtualListOverscroll; import com.pixate.freestyle.util.ViewUtil; /** * ListView styling.<br> * A {@link ListView} styler controls the divider settings, selection mode and * over-scrolling attributes (as a virtual child). * * <pre> * - divider: paint * - divider-height: length * - overscroll: virtual-child * * (Defined in the AbsListView styler, but applicable for this list) * - selection-mode: single|multiple * - selector: virtual-child * * (Defined in the View styler, but applicable for this list) * - android-fading-edge-length: length * - android-vertical-fading-edge: enabled | disabled * - android-horizontal-fading-edge: enabled | disabled * </pre> * * For example: * * <pre> * #list { * divider: linear-gradient(black, orange); * divider-height: 5px; * } * * -- show drawables when overscrolling up or down -- * #list overscroll { * distance: 200px; * footer: linear-gradient(red, white); * header: url(top-overscroll.png); * } * </pre> * * @author Shalom Gibly */ @PXDocElement(hide=true) public class PXListViewStyleAdapter extends PXAbsListViewStyleAdapter { private static String ELEMENT_NAME = "list-view"; private static PXListViewStyleAdapter sInstance; protected PXListViewStyleAdapter() { } /* * (non-Javadoc) * @see * com.pixate.freestyle.styling.adapters.PXViewStyleAdapter#createStylers() */ @Override protected List<PXStyler> createStylers() { List<PXStyler> stylers = super.createStylers(); stylers.add(PXDividerStyler.getInstance()); return stylers; } /* * (non-Javadoc) * @see * com.pixate.freestyle.styling.adapters.PXViewStyleAdapter#getElementName * (java.lang.Object) */ @Override public String getElementName(Object object) { return ELEMENT_NAME; } public static PXListViewStyleAdapter getInstance() { synchronized (PXListViewStyleAdapter.class) { if (sInstance == null) { sInstance = new PXListViewStyleAdapter(); } } return sInstance; } /* * (non-Javadoc) * @see * com.pixate.freestyle.styling.adapters.PXStyleAdapter#getVirtualChildren * (java.lang.Object) */ @Override protected List<Object> getVirtualChildren(Object styleable) { List<Object> superVirtuals = super.getVirtualChildren(styleable); List<Object> result = new ArrayList<Object>(superVirtuals.size() + 1); result.addAll(superVirtuals); result.add(new PXVirtualListOverscroll(styleable)); return result; } /** * Special handling for list views used in the popup window for a * {@link Spinner}. Often they will be embedded inside a special container * on the popup window's surface. This container will not have the Spinner * anywhere above it in its view hierarchy, which, without the workaround * below, would mean that the list view children could not be styled via a * rule such as this: * * <pre> * #mySpinner list-view #text1 { * ... * } * </pre> * * In that example and without our workaround below, #mySpinner would never * be matched as an ancestor of the list view if the list view is in fact * embedded in one of these special popup containers. This in turn means * that when the text views within the list are evaluated for rule matching, * the matching won't succeed. Styling the list view alone, such as with * this rule, will actually work: * * <pre> * #mySpinner list-view { * ... * } * </pre> * * That works because the first pass at styling the list view comes when its * parent in the view hierarchy is null and therefore its "future parent", * which we set to the Spinner via the {@link PXAdapterInvocationHandler}, * will be used, causing a match and immediate styling. Later, when the * Spinner is no longer the parent because the special popup view container * has come into existence, it's okay because the list view was already * styled via the previous matches. <br> * But it won't work for the recycled children of the list view, because * different and multiple instances are created, re-used, etc., and at a * time when the special popup container has intervened and become the real * parent of the list view. <br> * To overcome this, we check if the special container class is the actual * parent of the list view and, if so, we then check if the list view's * "future parent" had been set by {@link PXAdapterInvocationHandler} to a * Spinner. When these conditions are met, it's safe to assume that the * Spinner is the parent for styling purposes, so we return it. */ @Override public Object getParent(Object styleable) { Object result = super.getParent(styleable); // These checks handle the list views that are displayed by the Spinner // in its drop-down and dialog modes. if (result != null && styleable instanceof View && (result.getClass().getSimpleName().contains("PopupViewContainer") || styleable .getClass().getSimpleName().contains("RecycleListView"))) { Object futureParent = ((View) styleable).getTag(ViewUtil.TAG_ELEMENT_FUTURE_PARENT); if (futureParent instanceof WeakReference<?>) { Object referent = ((WeakReference<?>) futureParent).get(); if (referent instanceof Spinner) { result = referent; } } } return result; } }