Android Open Source - yield-layout Yield Layout






From Project

Back to project page yield-layout.

License

The source code is released under:

Apache License

If you think the Android project yield-layout listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package me.tatarka.yieldlayout;
/*from w  w  w . j ava  2  s . c om*/
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;

import java.util.ArrayList;
import java.util.List;

/**
 * A layout that replaces itself with a given layout resource yielding children in places defined
 * by that layout. This way you can compose around views, complementary to how {@code <include/>}
 * composes views.
 * <p/>
 * The children of this layout may have {@code layout_yield_id}'s to define which {@link Yield} view
 * that they will replace. If not, then they will be replaced in the order that they are defined.
 * <p/>
 * It is an error to have more children than the layout has places for. However the reverse is
 * allowed. Any {@link Yield}'s that aren't replaced will simply be removed from the view hierarchy.
 */
public class YieldLayout extends ViewGroup {
    private int mLayoutResource;
    private View mLayoutView;
    private List<View> mChildren;

    public YieldLayout(Context context) {
        super(context);
        init(context);
    }

    public YieldLayout(Context context, int layoutResource) {
        super(context);
        mLayoutResource = layoutResource;
        init(context);
    }

    public YieldLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public YieldLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.YieldLayout);
            mLayoutResource = a.getResourceId(R.styleable.YieldLayout_yield_layout, 0);
            if (isInEditMode()) {
                mLayoutResource = a.getResourceId(R.styleable.YieldLayout_tools_yield_layout, mLayoutResource);
            }
            a.recycle();
        }

        init(context);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mLayoutResource != 0) {
            inflate();
        }
    }

    private void init(Context context) {
        setVisibility(View.GONE);
        setWillNotDraw(true);
    }

    public int getLayoutResource() {
        return mLayoutResource;
    }

    /**
     * Sets the layout resource for the layout. If the {@code YieldLayout} has already been added to
     * the view hierarchy, this will update the shown view.
     *
     * @param layoutResource the layout resource
     */
    public void setLayoutResource(int layoutResource) {
        mLayoutResource = layoutResource;
        inflate();
    }

    public View inflate(ViewGroup root) {
        return inflate(root, true);
    }

    public View inflate(ViewGroup root, boolean attachToParent) {
        View currentView = mLayoutView;
        if (currentView == null) currentView = this;
        return inflate(currentView, root, attachToParent);
    }

    private void inflate() {
        ViewParent viewParent = null;
        View currentView = this;

        if (mLayoutView != null) {
            currentView = mLayoutView;
            viewParent = mLayoutView.getParent();
        }

        if (viewParent == null) {
            viewParent = getParent();
        }

        if (viewParent == null) {
            return;
        }

        if (!(viewParent instanceof ViewGroup)) {
            throw new IllegalStateException("YieldLayout must have a non-null ViewGroup viewParent (Instead parent was: '" + viewParent + "')");
        }

        ViewGroup parent = (ViewGroup) viewParent;
        inflate(currentView, parent, true);
    }

    private View inflate(View currentView, ViewGroup root, boolean attachToParent) {
        if (mLayoutResource == 0) {
            throw new IllegalStateException("YieldLayout must have a valid layoutResource");
        }

        LayoutInflater factory = LayoutInflater.from(getContext());
        View view = factory.inflate(mLayoutResource, root, false);

        if (view instanceof ViewGroup) {
            replaceYieldWithChildren((ViewGroup) view);
        } else if (getChildCount() > 0) {
            throw new IllegalArgumentException("YieldLayout layout is not a ViewGroup and so does not expect children");
        }

        if (attachToParent && root != null) {
            replaceView(currentView, view);
        }

        return mLayoutView = view;
    }

    private void replaceYieldWithChildren(ViewGroup viewLayout) {
        List<View> children = takeChildren();
        List<Yield> yieldViews = collectYieldViews(viewLayout, new ArrayList<Yield>(children.size()));

        if (children.size() > yieldViews.size()) {
            throw new IllegalArgumentException("YieldLayout you have added more children (" + children.size() + ") than expected (" + yieldViews.size() + ")");
        }

        if (yieldViews.isEmpty()) return;

        if (children.isEmpty()) {
            removeEmptyYieldViews(yieldViews);
            return;
        }

        boolean hasExplicitYieldIds = children.get(0).getId() != NO_ID;

        if (hasExplicitYieldIds) {
            for (View child : children) {
                int childId = child.getId();
                if (childId == NO_ID) {
                    throw new IllegalArgumentException("Expected layout_yield_id for " + child.getClass().getSimpleName() + " (If at least one child has an id, they all must)");
                }

                Yield yield = takeYieldWithId(yieldViews, childId);
                if (yield == null) {
                    throw new IllegalArgumentException("YieldLayout includes child with layout_yield_id which is not in the layout:");
                }

                replaceView(yield, child);
            }
        } else {
            for (int i = children.size() - 1; i >= 0; i--) {
                Yield yield = yieldViews.remove(i);
                View child = children.get(i);
                int childId = child.getId();
                if (childId != NO_ID) {
                    //Means the previous one was NO_ID
                    throw new IllegalArgumentException("Expected layout_yield_id for " + children.get(i - 1).getClass().getSimpleName() + " (If at least one child has an id, they all must)");
                }

                replaceView(yield, child);
            }
        }

        removeEmptyYieldViews(yieldViews);
    }

    private List<View> takeChildren() {
        if (mLayoutView == null) {
            mChildren = new ArrayList<View>(getChildCount());
            for (int i = 0; i < getChildCount(); i++) {
                mChildren.add(getChildAt(i));
            }
            removeAllViewsInLayout();
        } else {
            for (View child : mChildren) {
                ViewParent viewParent = child.getParent();
                if (viewParent instanceof ViewGroup) {
                    ((ViewGroup) viewParent).removeViewInLayout(child);
                }
            }
        }
        return mChildren;
    }

    private void removeEmptyYieldViews(List<Yield> yieldViews) {
        for (Yield yield : yieldViews) {
            yield.notifyKept();
            if (yield.getKeepIfEmpty()) continue;

            ViewParent viewParent = yield.getParent();
            if (viewParent instanceof ViewGroup) {
                ((ViewGroup) viewParent).removeViewInLayout(yield);
            }
        }
    }

    private List<Yield> collectYieldViews(ViewGroup viewLayout, List<Yield> yieldViews) {
        for (int i = 0; i < viewLayout.getChildCount(); i++) {
            View child = viewLayout.getChildAt(i);
            if (child instanceof Yield) {
                yieldViews.add((Yield) child);
            } else if (child instanceof ViewGroup) {
                collectYieldViews((ViewGroup) child, yieldViews);
            }
        }
        return yieldViews;
    }

    private static Yield takeYieldWithId(List<Yield> list, int yieldId) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getId() == yieldId) {
                return list.remove(i);
            }
        }
        return null;
    }

    private static void replaceView(View toReplace, View replacement) {
        ViewGroup yieldParent = (ViewGroup) toReplace.getParent();
        int yieldIndex = yieldParent.indexOfChild(toReplace);
        yieldParent.removeViewInLayout(toReplace);
        ViewGroup.LayoutParams yieldParams = toReplace.getLayoutParams();

        if (replacement.getId() == NO_ID) {
            replacement.setId(toReplace.getId());
        }

        if (yieldParams != null) {
            yieldParent.addView(replacement, yieldIndex, yieldParams);
        } else {
            yieldParent.addView(replacement, yieldIndex);
        }
    }

    @Override
    protected void onLayout(boolean b, int i, int i2, int i3, int i4) {

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(0, 0);
    }
}




Java Source Code List

me.tatarka.yieldlayout.YieldLayoutInflater.java
me.tatarka.yieldlayout.YieldLayout.java
me.tatarka.yieldlayout.Yield.java
me.tatarka.yieldlayout.sample.BasicExampleFragment.java
me.tatarka.yieldlayout.sample.DynamicExampleFragment.java
me.tatarka.yieldlayout.sample.ExampleFragment.java
me.tatarka.yieldlayout.sample.ListExampleFragment.java
me.tatarka.yieldlayout.sample.MyActivity.java