Android Open Source - android-virtual-layout Render






From Project

Back to project page android-virtual-layout.

License

The source code is released under:

MIT License

If you think the Android project android-virtual-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 trikita.anvil;
/*from   w  w w  .j a v  a2  s. c  o  m*/
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;

import java.lang.reflect.InvocationTargetException;
import java.util.*;

public class Render {

  public interface Renderable {
    ViewNode view();
    ViewGroup getRootView();
  }

  //
  // Little trick to get type-safety.
  // T here is "View" if node is applicable to views and viewgroups
  // T here is "ViewGroup" if node is applicable to viewgroups only
  // Then v(Class<T>, INode<? super T>...) will:
  // - accept View class and INode<View> nodes
  // - accept ViewGroup class and INode<View> nodes
  // - accept ViewGroup class and INode<ViewGroup> nodes
  // - not accept View class and INode<ViewGroup> nodes (ViewGroup is not super of View)
  //
  public interface INode<T> {} // Node type signature

  // Nested views can be used with viewgroups only
  public static class ViewNode implements INode<ViewGroup> {
    Class<? extends View> viewClass;
    List<ViewNode> children = new ArrayList<ViewNode>();
    List<AttrNode> attrs = new ArrayList<AttrNode>();

    public ViewNode(Class<? extends View> c) {
      this.viewClass = c;
    }
  }

  // Attributes can be used with both views and viewgroups
  public static interface AttrNode extends INode<View> {
    void apply(View v);
  }


  // Map of active renderables and their actual view nodes
  private static Map<Renderable, ViewNode> mounts = new WeakHashMap<Renderable, ViewNode>();

  // v() method for viewgroups accepts both attributes and child view nodes
  // v() method for views accepts attributes only
  public static <T extends View> ViewNode v(Class<T> c, INode<? super T> ...args) {
    ViewNode node = new ViewNode(c);
    for (INode<?> n : args) {
      if (n instanceof ViewNode) {
        node.children.add((ViewNode) n);
      } else if (n instanceof AttrNode) {
        node.attrs.add((AttrNode) n);
      }
    }
    return node;
  }
  
  // Helper for attribute node containing only one value
  public static abstract class SimpleAttrNode<T> implements AttrNode {
    protected final T value;

    public SimpleAttrNode(T value) {
      this.value = value;
    }
    
    @Override
    public boolean equals(Object obj) {
      return (getClass() == obj.getClass() &&
          ((this.value == null && ((SimpleAttrNode) obj).value == null)
          || this.value.equals(((SimpleAttrNode) obj).value)));
    }
  }

  // Helper for multiple attributes (normally static) combined into one
  public static class MultiNode implements AttrNode {
    private AttrNode[] nodes;
    private int hash;

    public MultiNode(AttrNode... nodes) {
      this.nodes = nodes;
      for (AttrNode n : nodes) {
        hash ^= n.hashCode();
      }
    }

    public void apply(View v) {
      for (AttrNode n : nodes) {
        n.apply(v);
      }
    }

    public int hashCode() {
      return hash;
    }

    public boolean equals(Object obj) {
      if (obj instanceof MultiNode) {
        MultiNode m = (MultiNode) obj;
        if (this.nodes.length == m.nodes.length) {
          for (int i = 0; i < this.nodes.length; i++) {
            if (this.nodes[i].equals(m.nodes[i]) == false) {
              return false;
            }
          }
          return true;
        }
      }
      return false;
    }
  }

  public static AttrNode attrs(AttrNode ...nodes) {
    return new MultiNode(nodes);
  }

  //
  // Helpers for android attributes and dimensions
  //
  private static Deque<Renderable> renderStack = new ArrayDeque<Renderable>();

  public static boolean isPortait() {
    return getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
  }

  public static TypedValue attr(int attr) {
    TypedValue tv = new TypedValue();
    renderStack.peek().getRootView().getContext().getTheme().resolveAttribute(attr, tv, true);
    return tv;
  }

  public static int attrPx(int attr) {
    TypedValue tv = attr(attr);
    return Math.round(getResources().getDimension(tv.resourceId));
  }

  public static float dip(float value) {
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
        getResources().getDisplayMetrics());
  }

  public static int dip(int value) {
    return Math.round(dip((float) value));
  }

  private static Resources getResources() {
    return renderStack.peek().getRootView().getResources();
  }

  public static boolean startRendering(Renderable r) {
    if (renderStack.contains(r)) {
      return false;
    }
    renderStack.push(r);
    return true;
  }

  public static void stopRendering() {
    renderStack.pop();
  }


  private static boolean skipNextRender = false;

  public static void skipRender() {
    skipNextRender = true;
  }

  public static void render(Renderable r) {
    if (!startRendering(r)) {
      return; // Don't render same renderer recursively
    }
    ViewNode oldValue = mounts.get(r);
    ViewNode virt = r.view();
    mounts.put(r, virt);

    inflateNode(r.getRootView().getContext(), virt, oldValue, new ParentMount(r.getRootView(), 0));
    stopRendering();
  }

  // Render all mounted views
  public static void render() {
    if (skipNextRender) {
      skipNextRender = false;
      return;
    }
    for (Renderable r: mounts.keySet()) {
      render(r);
    }
  }

  // Mount is a placeholder for android view
  public interface Mount {
    public View get();
    public void set(View v);
  }

  // A real mount point in a parent view at certain index
  public static class ParentMount implements Mount {
    ViewGroup parent;
    int index;
    public ParentMount(ViewGroup parent, int index) {
      this.parent = parent;
      this.index = index;
    }
    public void set(View v) {
      if (index < parent.getChildCount()) {
        parent.removeViewAt(index);
      }
      parent.addView(v, index);
    }
    public View get() {
      if (index < parent.getChildCount()) {
        return parent.getChildAt(index);
      } else {
        return null;
      }
    }
  }

  public static View inflateNode(Context c, ViewNode node, ViewNode oldNode, Mount mount) {
    boolean isNewView = false;
    try {
      View v = mount.get();
      // Reuse view or create a new one
      if (v == null || oldNode == null || oldNode.viewClass == null ||
          node.viewClass != oldNode.viewClass) {
        v = (View) node.viewClass.getConstructor(Context.class).newInstance(c);
        mount.set(v);
      }

      int viewIndex = 0;
      for (int i = 0; i < node.children.size(); i++) {
        ViewNode subnode = node.children.get(i);
        ViewNode oldSubNode =
          (oldNode == null || oldNode.children.size() <= i ?  null : oldNode.children.get(i));
        inflateNode(c, subnode, oldSubNode, new ParentMount((ViewGroup) v, viewIndex));
        viewIndex++;
      }

      // Some views could be removed since last render
      if (v instanceof ViewGroup) {
        ViewGroup vg = (ViewGroup) v;
        if (viewIndex != 0 && vg.getChildCount() >= viewIndex) {
          vg.removeViews(viewIndex-1, vg.getChildCount() - viewIndex);
        }
      }
      for (int i = 0; i < node.attrs.size(); i++) {
        AttrNode subnode = node.attrs.get(i);
        AttrNode oldSubNode = 
          (oldNode == null || oldNode.attrs.size() <= i ?  null : oldNode.attrs.get(i));
        if (isNewView || oldSubNode == null || subnode.equals(oldSubNode) == false) {
          subnode.apply(v);
        }
      }
      return v;
    } catch (InvocationTargetException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    } catch (InstantiationException e) {
      throw new RuntimeException(e);
    }
  }

  public abstract static class RenderableView extends FrameLayout implements Renderable {

    // View must have a non-zero ID to make onSaveInstanceState() and
    // onRestoreInstanceState() called
    public final static int DEFAULT_RENDERABLE_ID = 0xaccede;

    // We can't do render(this) here because it would call overridden methods
    public RenderableView(Context c) {
      super(c);
      setId(DEFAULT_RENDERABLE_ID);
    }

    private boolean isRendered = false;
    // Seems to be a good place to start rendering our view
    public void onMeasure(int wspec, int hspec) {
      if (isRendered == false) {
        render(this);
        isRendered = true;
      }
      super.onMeasure(wspec, hspec);
    }

    public ViewGroup getRootView() {
      return this;
    }

    public void onLoad(Bundle b) {}
    public void onSave(Bundle b) {}

    @Override
    public Parcelable onSaveInstanceState() {
      System.out.println("onSaveInstanceState");
      Bundle b = new Bundle();
      b.putParcelable("instanceState", super.onSaveInstanceState());
      onSave(b);
      return b;
    }

    @Override
    public void onRestoreInstanceState(Parcelable p) {
      System.out.println("onRestoreInstanceState");
      if (p instanceof Bundle) {
        Bundle b = (Bundle) p;
        onLoad(b);
        super.onRestoreInstanceState(b.getParcelable("instanceState"));
      } else {
        super.onRestoreInstanceState(p);
      }
    }
  }

  //
  // In RenderableAdapter one must override getCount(), getItem() and itemView(pos)
  //
  public abstract static class RenderableAdapter extends BaseAdapter implements Renderable {
    private Map<View, ViewNode> activeViews = new WeakHashMap<View, ViewNode>();
    private ViewGroup mCurrentParentView;

    public long getItemId(int pos) {
      return pos; // just a most common implementation
    }
    public ViewGroup getRootView() {
      return mCurrentParentView; // parent view for currently rendered item
    }
    public ViewNode view() {
      return null; // Just to match the interface
    }

    public abstract ViewNode itemView(int pos);

    public View getView(int pos, View v, ViewGroup parent) {
      ViewNode m = activeViews.get(v);
      startRendering(this);
      mCurrentParentView = parent;
      ViewNode n = itemView(pos);
      mCurrentParentView = null;
      ViewGroup.LayoutParams params =
        new AbsListView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT);
      View oldView = v;
      v = inflateNode(parent.getContext(), n, m, new AdapterMount(v, params));
      activeViews.put(v, n);
      stopRendering();
      return v;
    }
  }

  public abstract static class RenderableArrayAdapter<T> extends RenderableAdapter {
    private List<T> items;

    public RenderableArrayAdapter(T[] items) {
      this.items = Arrays.asList(items);
    }

    public RenderableArrayAdapter(List<T> items) {
      this.items = items;
    }

    public int getCount() {
      return items.size();
    }

    public T getItem(int pos) {
      return items.get(pos);
    }

    public ViewNode itemView(int pos) {
      return itemView(pos, getItem(pos));
    }

    public abstract ViewNode itemView(int pos, T value);
  }

  // A mount point inside the adapter view.
  // Views can't be added to the adapter view, so we need to define their
  // layoutparams without adding
  private static class AdapterMount implements Mount {
    ViewGroup.LayoutParams params;
    View view;
    public AdapterMount(View v, ViewGroup.LayoutParams params) {
      this.params = params;
      this.view = v;
    }
    public void set(View v) {
      if (this.view != v && this.params != null) {
        this.view = v;
        this.view.setLayoutParams(this.params);
      }
    }
    public View get() {
      return this.view;
    }
  }
}




Java Source Code List

trikita.anvil.Props.java
trikita.anvil.Render.java
trikita.anvil.example.Backstack.java
trikita.anvil.example.CountDownView.java
trikita.anvil.example.MainActivity.java
trikita.anvil.example.StartView.java
trikita.anvil.example.Tasks.java
trikita.anvil.v10.Props.java
trikita.anvil.v15.Props.java