public class SAK {
  private static Scaffold sScaffold;

  public static void init(Application application, Config config) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
      Log.w("SAK", "暂不支持Android5.0以下设备");
      return;
    }

    if (application.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.M&&Build.VERSION.SDK_INT>=Build.VERSION_CODES.M && !Settings.canDrawOverlays(application)) {
//      if (ContextCompat.checkSelfPermission(application, Manifest.permission.SYSTEM_ALERT_WINDOW) != PackageManager.PERMISSION_GRANTED) {
      Toast.makeText(application, "需要悬浮窗权限才能使用SwissArmyKnife", Toast.LENGTH_LONG).show();
      Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      application.startActivity(intent);
      return;
//      }
    }
    if (sScaffold != null) {
      return;
    }
    sScaffold = new Scaffold();


    if (config == null) {
      config = new Config.Build(application)
          .addLayer(BorderLayer.class, application.getDrawable(R.drawable.sak_border_icon), application.getString(R.string.sak_border))
          .addLayer(GridLayer.class, application.getDrawable(R.drawable.sak_grid_icon), application.getString(R.string.sak_grid))
          .addLayer(PaddingLayer.class, application.getDrawable(R.drawable.sak_padding_icon), application.getString(R.string.sak_padding))
          .addLayer(MarginLayer.class, application.getDrawable(R.drawable.sak_margin_icon), application.getString(R.string.sak_margin))
          .addLayer(WidthHeightLayer.class, application.getDrawable(R.drawable.sak_width_height_icon), application.getString(R.string.sak_width_height))
          .addLayer(TextColorLayer.class, application.getDrawable(R.drawable.sak_text_color_icon), application.getString(R.string.sak_txt_color))
          .addLayer(TextSizeLayer.class, application.getDrawable(R.drawable.sak_text_size_icon), application.getString(R.string.sak_txt_size))
          .addLayer(BackgroundColorLayer.class, application.getDrawable(R.drawable.sak_background_color_icon), application.getString(R.string.sak_bag_color))
          .addLayer(ActivityNameLayerView.class, application.getDrawable(R.drawable.sak_page_name_icon), application.getString(R.string.sak_activity_name))
          .addLayer(FragmentNameLayer.class, application.getDrawable(R.drawable.sak_page_name_icon), application.getString(R.string.sak_fragment_name))
          .addLayer(HorizontalMeasureView.class, application.getDrawable(R.drawable.sak_hori_measure_icon), application.getString(R.string.sak_horizontal_measure))
          .addLayer(VerticalMeasureView.class, application.getDrawable(R.drawable.sak_ver_measure_icon), application.getString(R.string.sak_vertical_measure))
          .addLayer(TakeColorLayer.class, application.getDrawable(R.drawable.sak_color_picker_icon), application.getString(R.string.sak_take_color))
          .addLayer(ViewClassLayer.class, application.getDrawable(R.drawable.sak_controller_type_icon), application.getString(R.string.sak_view_name))
          .addLayer(TreeView.class, application.getDrawable(R.drawable.sak_layout_tree_icon), application.getString(R.string.sak_layout_tree))
          .addLayer(RelativeLayerView.class, application.getDrawable(R.drawable.sak_relative_distance_icon), application.getString(R.string.sak_relative_distance))
          .addLayer(TranslationLayerView.class, application.getDrawable(R.drawable.sak_drag_icon), application.getString(R.string.sak_translation_view))
          .addLayer(ScalpelFrameLayout.class, application.getDrawable(R.drawable.sak_layer_icon), "Scalpel")
//          .addLayer(ViewDrawPerformLayer.class, application.getDrawable(R.drawable.sak_performance_icon),application.getString(R.string.sak_performance) )
          .addLayer(BitmapWidthHeightLayer.class, application.getDrawable(R.drawable.sak_img_size),application.getString(R.string.sak_image_w_h) )
          .addLayer(FlashWindowLayer.class, application.getDrawable(R.drawable.sak_shake_icon),application.getString(R.string.sak_flash_when_ui_update) )
          .build();
    }
    sScaffold.install(application, config);
  }

  private SAK() {
  }
//
//  public static void unInstall() {
//    if (sScaffold != null) {
//      sScaffold.unInstall();
//      sScaffold = null;
//    }
//  }

} public class Config {

    private int minRange;
    private int maxRange;
//    private List<AbsLayer> mLayerViews = new ArrayList<>();
    private List<ISizeConverter> mSizeConverterList = new ArrayList<>();
    private int startRange;
    private int endRange;
    private boolean clipDraw;
    List<Item>mLayerList;
    private Config(Build build) {
//        mLayerViews.addAll(build.mDefaultLayerViews);
//        mLayerViews.addAll(build.mCustomerLayerViews);

        ViewFilter.FILTER = build.mViewFilter;
        mSizeConverterList.addAll(build.mSizeConverterList);

        minRange = build.min;
        maxRange = build.max;
        clipDraw = build.clipDraw;
        mLayerList = build.mLayerList;
    }

    public List<Item> getLayerList() {
        return mLayerList;
    }

//    public List<AbsLayer> getLayerViews() {
//        return mLayerViews;
//    }

    public List<ISizeConverter> getSizeConverters() {
        if (mSizeConverterList == null || mSizeConverterList.isEmpty()) {
            return Arrays.<ISizeConverter>asList(new OriginSizeConverter());
        }
        return mSizeConverterList;
    }

    public int getStartRange() {
        return startRange;
    }

    public void setStartRange(int startRange) {
        this.startRange = startRange;
    }

    public int getEndRange() {
        return endRange;
    }

    public void setEndRange(int endRange) {
        this.endRange = endRange;
    }

    public boolean isClipDraw() {
        return clipDraw;
    }

    public void setClipDraw(boolean clipDraw) {
        this.clipDraw = clipDraw;
    }

    public int getMinRange() {
        return minRange;
    }

    public int getMaxRange() {
        return maxRange;
    }

    public static class Build {
        Context mContext;
        List<ISizeConverter> mSizeConverterList = new ArrayList<>();
        ViewFilter mViewFilter;
        List<Item>mLayerList=new ArrayList<>();
        int min = 0;
        int max = 50;
        boolean clipDraw = true;

        public Build(Context context) {
            Check.isNull(context, "context");
            mContext = context.getApplicationContext();


            mSizeConverterList.add(new Px2DpSizeConverter());
            mSizeConverterList.add(new OriginSizeConverter());
            mSizeConverterList.add(new Px2SpSizeConverter());
            mViewFilter = ViewFilter.FILTER;
        }

        public Build addSizeConverter(ISizeConverter sizeConverter) {
            Check.isNull(sizeConverter, "sizeConverter");
            mSizeConverterList.add(sizeConverter);
            return this;
        }

//        public Build addLayerView(AbsLayer layerView) {
//            Check.isNull(layerView, "layerView");
////            mDefaultLayerViews.clear();
////            mCustomerLayerViews.add(layerView);
//            return this;
//        }

        public Build viewFilter(ViewFilter viewFilter) {
            Check.isNull(viewFilter, "viewFilter");
            mViewFilter = viewFilter;
            return this;
        }

        public Build range(int min, int max) {
            if (min < 0) {
                throw new IllegalArgumentException();
            }
            if (max < min) {
                throw new IllegalArgumentException();
            }
            this.min = min;
            this.max = max;
            return this;
        }

        public Build clipDraw(boolean clip) {
            clipDraw = clip;
            return this;
        }

        public Build addLayer(Class<? extends Layer> clz, Drawable icon, String iconName) {
            mLayerList.add(new Item(clz,icon,iconName));
            return this;
        }
        public Config build() {
            return new Config(this);
        }
    }
} public class ActivityNameLayerView extends Layer {

  private Paint mPaint;
  private String mActName;
  private int mRadius;

  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setTextSize(ScreenUtils.dp2px(getContext(), 10));
    mPaint.setColor(Color.RED);
    mPaint.setStrokeWidth(ScreenUtils.dp2px(getContext(), 1));
    mRadius = ScreenUtils.dp2px(getContext(), 8);
    getActivityName(rootView);
  }

  protected void getActivityName(View view) {
    Activity activity = Utils.findAct(view);
    if (activity == null) {
      return;
    }
    mActName = activity.getClass().getName();
    invalidate();
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    drawActivityInfo(canvas);
  }

  protected void drawActivityInfo(Canvas canvas) {
    if (mActName == null) {
      return;
    }
    drawInfo(canvas, 0, 0, canvas.getWidth(), canvas.getHeight(), mActName);
  }


  protected void drawInfo(Canvas canvas, int x, int y, int w, int h, String txt) {
    canvas.save();
    drawCorner(canvas, x, y, w, h);

    drawX(canvas, x, y, w, h);

    drawName(canvas, x, y, w, h, txt);
    canvas.restore();
  }

  private void drawName(Canvas canvas, int x, int y, int w, int h, String txt) {
    TextPaint textPaint = new TextPaint(mPaint);
    textPaint.setTextSize(ScreenUtils.dp2px(getContext(), 13));
    canvas.translate(x, y);
    SpannableString decorStr = new SpannableString(txt);
    decorStr.setSpan(new BackgroundColorSpan(0x99FFFFFF), 0, txt.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    StaticLayout staticLayout = new StaticLayout(decorStr, textPaint, (int) (w * 0.8), Layout.Alignment.ALIGN_CENTER, 1, 0, false);

    int txtW = staticLayout.getWidth();
    int txtH = staticLayout.getHeight();
    if (txtH > 0 && txtH > h * 0.8) {
      float scale = h * 0.8f / txtH;
      int realW = (int) (txtW * scale);
      int realH = (int) (txtH * scale);

      canvas.translate((w - realW) / 2f, (h - realH) / 2f);
      canvas.scale(scale, scale);
    } else {
      canvas.translate((w - txtW) / 2f, (h - txtH) / 2f);
    }

    staticLayout.draw(canvas);
  }

  private void drawX(Canvas canvas, int x, int y, int w, int h) {
    canvas.drawLine(x, y, x + w, y + h, mPaint);
    canvas.drawLine(x + w, y, x, y + h, mPaint);
  }

  private void drawCorner(Canvas canvas, int x, int y, int w, int h) {
    canvas.drawLine(x, y, x + mRadius, y, mPaint);
    canvas.drawLine(x, y, x, y + mRadius, mPaint);

    canvas.drawLine(x + w, y, x + w - mRadius, y, mPaint);
    canvas.drawLine(x + w, y, x + w, y + mRadius, mPaint);


    canvas.drawLine(x, y + h, x + mRadius, y + h, mPaint);
    canvas.drawLine(x, y + h, x, y + h - mRadius, mPaint);


    canvas.drawLine(x + w, y + h, x + w - mRadius, y + h, mPaint);
    canvas.drawLine(x + w, y + h, x + w, y + h - mRadius, mPaint);
  }
} public class BorderLayer extends ViewLayer {

  private Paint mBorderPaint;
  private Paint mCornerPaint;
  private int mCornerW;

  private int w;
  private int h;

  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mBorderPaint.setStyle(Paint.Style.STROKE);
    mBorderPaint.setColor(getBorderColor());

    mCornerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mCornerPaint.setStyle(Paint.Style.STROKE);
    mCornerPaint.setStrokeWidth(ScreenUtils.dp2px(getContext(), 1));
    mCornerPaint.setColor(getCornerColor());

    mCornerW = ScreenUtils.dp2px(getContext(), 6);
//    invalidate();
  }

  @Override
  protected void onDraw(Canvas canvas, View view) {
    w = view.getWidth();
    h = view.getHeight();
    drawBorder(canvas);
    drawCorner(canvas);
  }

  protected int getBorderColor() {
    return getContext().getResources().getColor(R.color.sak_color_primary);
  }

  protected int getCornerColor() {
    return Color.MAGENTA;
  }

  private void drawBorder(Canvas canvas) {
    canvas.drawRect(0, 0, w, h, mBorderPaint);
  }

  private void drawCorner(Canvas canvas) {
    //--
    canvas.drawLine(0, 0, mCornerW, 0, mCornerPaint);
    //|
    canvas.drawLine(0, 0, 0, mCornerW, mCornerPaint);
    //  --
    canvas.drawLine(w - mCornerW, 0, w, 0, mCornerPaint);
    //    |
    canvas.drawLine(w, 0, w, mCornerW, mCornerPaint);
    //
    canvas.drawLine(0, h, 0, h - mCornerW, mCornerPaint);
    canvas.drawLine(0, h, mCornerW, h, mCornerPaint);

    canvas.drawLine(w - mCornerW, h, w, h, mCornerPaint);
    canvas.drawLine(w, h - mCornerW, w, h, mCornerPaint);

  }

} public class FragmentNameLayer extends ActivityNameLayerView implements IRange, IClip {

  private int mStartLayer;
  private int mEndLayer = 100;// TODO: 2020/7/7
  private FragmentActivity mActivity;
  private Paint mBagPaint = new Paint();
  private boolean isClip = true;

  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    mBagPaint.setColor(Color.argb(63, 255, 0, 255));
    Activity activity = Utils.findAct(rootView);
    if (activity instanceof FragmentActivity) {
      mActivity = (FragmentActivity) activity;
      invalidate();
    }
  }


  @Override
  public void onStartRangeChange(int start) {
    mStartLayer = start;
    invalidate();
  }

  @Override
  public void onEndRangeChange(int end) {
    mEndLayer = end;
    invalidate();
  }


  @Override
  protected void onAfterTraversal(View rootView) {
    super.onAfterTraversal(rootView);
    invalidate();
  }

  @Override
  protected void onDraw(Canvas canvas) {
//    super.onDraw(canvas);
    if (mActivity == null || mActivity.isFinishing()) {
      return;
    }
//    mStartLayer = getStartRange();
//    mEndLayer = getEndRange();
    traversal(canvas, mActivity.getSupportFragmentManager(), 0);
  }


  private void traversal(Canvas canvas, FragmentManager fragmentManager, int curLayer) {
    if (curLayer > mEndLayer || fragmentManager == null) {
      return;
    }
    List<Fragment> fragments = fragmentManager.getFragments();
    if (fragments == null || fragments.isEmpty()) {
      return;
    }

    for (Fragment fragment : fragments) {
      if (fragment.isVisible() && curLayer >= mStartLayer) {
        View view = fragment.getView();
//        int localSize[] = getLocationAndSize(view);
//        canvas.drawRect(localSize[0], localSize[1], localSize[0] + localSize[2], localSize[1] + localSize[3], mBagPaint);
//        drawInfo(canvas, localSize[0], localSize[1], localSize[2], localSize[3], fragment.getClass().getName());
        canvas.save();
        traversals(view, canvas, getRootView(), fragment.getClass().getName());
        canvas.restore();
      }
      traversal(canvas, fragment.getChildFragmentManager(), curLayer + 1);
    }
  }

  private void traversals(View target, Canvas canvas, View curView, String name) {
    if (target == curView) {
      canvas.drawRect(0, 0, target.getWidth(), target.getHeight(), mBagPaint);
      drawInfo(canvas, 0, 0, target.getWidth(), target.getHeight(), name);
      return;
    }

    if (curView instanceof ViewGroup) {
      ViewGroup vg = ((ViewGroup) curView);
      int sx = curView.getScrollX();
      int sy = curView.getScrollY();
      canvas.save(); //save 1
      canvas.translate(-sx, -sy);

      for (int i = 0, len = vg.getChildCount(); i < len; i++) {
        View child = vg.getChildAt(i);
        canvas.save();//save 2
        canvas.translate(child.getX(), child.getY());
        if (isClip) {
          canvas.clipRect(0, 0, child.getWidth(), child.getHeight(), Region.Op.INTERSECT);
        }

//        canvas.save(); //save 3
        traversals(target, canvas, child, name);
//        canvas.restore();// restore 3

        canvas.restore();// restore 2
      }
      canvas.restore(); // restore 1
    }
  }

  @Override
  public void onClipChange(boolean clip) {
    isClip = clip;
    invalidate();
  }
} public class GridLayer extends Layer {

  private Paint mPaint;

  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(color());
    invalidate();
  }

  @Override
  protected void onEnableChange(boolean enable) {
    super.onEnableChange(enable);

  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    int w = getRootView().getWidth();
    int h = getRootView().getHeight();
    int space = space();

    for (int i = space; i < w; i += space) {
      canvas.drawLine(i, 0, i, h, mPaint);
    }
    for (int i = space; i < h; i += space) {
      canvas.drawLine(0, i, w, i, mPaint);
    }
  }


  @Override
  protected void onAfterTraversal(View rootView) {
    invalidate();
  }

  protected int color() {
    return 0x55000000;
  }

  protected int space() {
    return ScreenUtils.dp2px(getContext(), 5);
  }


} public class HorizontalMeasureView extends Layer implements ISize {
  protected Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  protected int m20DP;
  protected int maxHeight;
  protected int minHeight;
  private int height;
  private boolean consume;
  private float lastX, lastY;
  private Rect rect = new Rect();

  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    init();
  }


  private void init() {
    mPaint.setColor(Color.BLACK);
    m20DP = ScreenUtils.dp2px(getContext(), 20);
    mPaint.setTextSize(ScreenUtils.dp2px(getContext(), 8));
    maxHeight = ScreenUtils.dp2px(getContext(), 10);
    minHeight = ScreenUtils.dp2px(getContext(), 5);
    height = ScreenUtils.dp2px(getContext(), 60);
    rect.left = 0;
    rect.top = (int) (getRootView().getHeight() / 2f - height / 2f);
    rect.right = getRootView().getWidth();
    rect.bottom = (int) (getRootView().getHeight() / 2f + height / 2f);
    invalidate();
  }

  @Override
  protected void onAfterTraversal(View rootView) {
    super.onAfterTraversal(rootView);
    invalidate();
  }

  @Override
  protected boolean onBeforeInputEvent(View rootView, InputEvent event) {
    if (!(event instanceof MotionEvent)) {
      return false;
    }
    MotionEvent motionEvent = ((MotionEvent) event);
    if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
      lastX = motionEvent.getX();
      lastY = motionEvent.getY();
      if (rect.contains((int) lastX, (int) lastY)) {
        consume = true;
      } else {
        consume = false;
      }
    }
    if (consume == false) {
      return false;
    }
    float curX = motionEvent.getX();
    float curY = motionEvent.getY();

    int dx = (int) (curX - lastX);
    int dy = (int) (curY - lastY);
    rect.left += dx;
    rect.right += dx;
    rect.top += dy;
    rect.bottom += dy;
    invalidate();
    lastX = curX;
    lastY = curY;
    return true;
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.translate(rect.left, rect.top);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setColor(0xbbFFFFFF);
    canvas.drawRect(0, 0, rect.right - rect.left, rect.bottom - rect.top, mPaint);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setColor(Color.BLACK);
    canvas.drawRect(0, 0, rect.right - rect.left, rect.bottom - rect.top, mPaint);
    mPaint.setStyle(Paint.Style.FILL);
    int w = rect.right - rect.left;
    canvas.drawLine(0, 0, w, 0, mPaint);
    for (int i = 0; i <= w; i += m20DP) {
      canvas.drawLine(i, -maxHeight, i, maxHeight , mPaint);
      canvas.rotate(90, i, maxHeight);
      canvas.drawText(sizeConverter.convert(getContext(), i).toString(), i, maxHeight, mPaint);
      canvas.rotate(-90, i, maxHeight);
      for (int j = i; j <= w && j < i + m20DP; j += m20DP / 10) {
        canvas.drawLine(j, -minHeight, j, minHeight, mPaint);
      }
    }
  }

  ISizeConverter sizeConverter = ISizeConverter.CONVERTER;

  @Override
  public void onSizeConvertChange(ISizeConverter converter) {
    sizeConverter = converter;
    invalidate();
  }
} public class MarginLayer extends ViewLayer implements ISize {

  private Paint mTextPaint;
  private Paint mBgPaint;
  private Paint mMarginPaint;
  private int mTxtColor = 0xFF000000;
  private int mBgColor = 0x88FFFFFF;
  private int mMarginColor = 0x33FF0008;
  private Rect mRect = new Rect();
  private DecimalFormat mFormat = new DecimalFormat("#.###");

  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mTextPaint.setTextSize(ScreenUtils.dp2px(getContext(), 10));
    mTextPaint.setColor(mTxtColor);

    mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mBgPaint.setColor(mBgColor);

    mMarginPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mMarginPaint.setColor(mMarginColor);
  }

  @Override
  protected void onDraw(Canvas canvas, View view) {
    if (!(view instanceof ViewGroup)) {
      return;
    }
    int childCount = ((ViewGroup) view).getChildCount();
    for (int i = 0; i < childCount; i++) {
      canvas.save();
      drawMargin(canvas, ((ViewGroup) view).getChildAt(i));
      canvas.restore();
    }

  }

  private void drawMargin(Canvas canvas, View view) {
    ViewGroup.LayoutParams params = view.getLayoutParams();
    if (!(params instanceof ViewGroup.MarginLayoutParams)) {
      return;
    }//画 外边距
    int w = view.getWidth();
    int h = view.getHeight();
    ViewGroup.MarginLayoutParams marginLayoutParams = ((ViewGroup.MarginLayoutParams) params);

    canvas.translate(view.getX(), view.getY());

    int l = -marginLayoutParams.leftMargin;
    int t = -marginLayoutParams.topMargin;
    int r = w + marginLayoutParams.rightMargin;
    int b = h + marginLayoutParams.bottomMargin;

    canvas.drawRect(l, 0, 0, h, mMarginPaint);//left
    canvas.drawRect(w, 0, r, h, mMarginPaint);//right
    canvas.drawRect(0, t, w, 0, mMarginPaint);
    canvas.drawRect(0, h, w, b, mMarginPaint);
    ISizeConverter converter = getSizeConverter();
    Context context = getContext();

    if (marginLayoutParams.leftMargin != 0) {
      String txt = "L" + mFormat.format(converter.convert(context, marginLayoutParams.leftMargin).getLength());
      mTextPaint.getTextBounds(txt, 0, txt.length(), mRect);
      canvas.drawRect(l, h / 2f - mRect.height() / 2f, l + mRect.width(), h / 2f + mRect.height() / 2f, mBgPaint);
      canvas.drawText(txt, l, h / 2f + mRect.height() / 2f, mTextPaint);
    }
    if (marginLayoutParams.topMargin != 0) {
      String txt = "T" + mFormat.format(converter.convert(context, marginLayoutParams.topMargin).getLength());
      mTextPaint.getTextBounds(txt, 0, txt.length(), mRect);
      canvas.drawRect(w / 2f - mRect.width() / 2f, t, w / 2f + mRect.width() / 2f, t + mRect.height(), mBgPaint);
      canvas.drawText(txt, w / 2f - mRect.width() / 2f, t + mRect.height(), mTextPaint);
    }

    if (marginLayoutParams.rightMargin != 0) {
      String txt = "R" + mFormat.format(converter.convert(context, marginLayoutParams.rightMargin).getLength());
      mTextPaint.getTextBounds(txt, 0, txt.length(), mRect);
      canvas.drawRect(r - mRect.width(), h / 2f - mRect.height() / 2f, r, h / 2f + mRect.height() / 2f, mBgPaint);
      canvas.drawText(txt, r - mRect.width(), h / 2f + mRect.height() / 2f, mTextPaint);
    }

    if (marginLayoutParams.bottomMargin != 0) {
      String txt = "B" + mFormat.format(converter.convert(context, marginLayoutParams.bottomMargin).getLength());
      mTextPaint.getTextBounds(txt, 0, txt.length(), mRect);
      canvas.drawRect(w / 2f - mRect.width() / 2f, b - mRect.height(), w / 2f + mRect.width() / 2f, b, mBgPaint);
      canvas.drawText(txt, w / 2f - mRect.width() / 2f, b, mTextPaint);
    }
  }

  private ISizeConverter getSizeConverter() {
    return sizeConverter == null ? ISizeConverter.CONVERTER : sizeConverter;
  }

  private ISizeConverter sizeConverter;

  @Override
  public void onSizeConvertChange(ISizeConverter converter) {
    sizeConverter = converter;
    invalidate();
  }
} public class PaddingLayer extends ViewLayer implements ISize {
  private Paint mTextPaint;
  private Paint mBgPaint;
  private Paint mPaddingPaint;
  private int mTxtColor = 0xFF000000;
  private int mBgColor = 0x88FFFFFF;
  private int mPaddingColor = 0x3300FF0D;
  private Rect mRect = new Rect();
  private DecimalFormat mFormat = new DecimalFormat("#.###");

  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mTextPaint.setTextSize(ScreenUtils.dp2px(getContext(), 10));
    mTextPaint.setColor(mTxtColor);

    mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mBgPaint.setColor(mBgColor);

    mPaddingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaddingPaint.setColor(mPaddingColor);
  }

  protected void onDraw(Canvas canvas, View view) {
    int l = view.getPaddingLeft();
    int t = view.getPaddingTop();
    int r = view.getPaddingRight();
    int b = view.getPaddingBottom();
    int w = view.getWidth();
    int h = view.getHeight();

    canvas.drawRect(0, 0, l, h, mPaddingPaint);
    canvas.drawRect(0, 0, w, t, mPaddingPaint);
    canvas.drawRect(w - r, 0, w, h, mPaddingPaint);
    canvas.drawRect(0, h - b, w, h, mPaddingPaint);

    ISizeConverter converter = getSizeConverter();
    Context context = getContext();
    if (l != 0) {
      String txt = "L" + mFormat.format(converter.convert(context, l).getLength());
      mTextPaint.getTextBounds(txt, 0, txt.length(), mRect);
      canvas.drawRect(0, h / 2f - mRect.height() / 2f, mRect.width(), h / 2f + mRect.height() / 2f, mBgPaint);
      canvas.drawText(txt, 0, h / 2f + mRect.height() / 2f, mTextPaint);
    }
    if (t != 0) {
      String txt = "T" + mFormat.format(converter.convert(context, t).getLength());
      mTextPaint.getTextBounds(txt, 0, txt.length(), mRect);
      canvas.drawRect(w / 2f - mRect.width() / 2f, 0, w / 2f + mRect.width() / 2f, mRect.height(), mBgPaint);
      canvas.drawText(txt, w / 2f - mRect.width() / 2f, mRect.height(), mTextPaint);
    }
    if (r != 0) {
      String txt = "R" + mFormat.format(converter.convert(context, r).getLength());
      mTextPaint.getTextBounds(txt, 0, txt.length(), mRect);
      canvas.drawRect(w - mRect.width(), h / 2f - mRect.height() / 2f, w, h / 2f + mRect.height() / 2f, mBgPaint);
      canvas.drawText(txt, w - mRect.width(), h / 2f + mRect.height() / 2f, mTextPaint);
    }
    if (b != 0) {
      String txt = "B" + mFormat.format(converter.convert(context, b).getLength());
      mTextPaint.getTextBounds(txt, 0, txt.length(), mRect);
      canvas.drawRect(w / 2f - mRect.width() / 2f, h - mRect.height(), w / 2f + mRect.width() / 2f, h, mBgPaint);
      canvas.drawText(txt, w / 2f - mRect.width() / 2f, h, mTextPaint);
    }
  }


  private ISizeConverter getSizeConverter() {
    return sizeConverter == null ? ISizeConverter.CONVERTER : sizeConverter;
  }

  private ISizeConverter sizeConverter;

  @Override
  public void onSizeConvertChange(ISizeConverter converter) {
    sizeConverter = converter;
    invalidate();
  }
} public class RelativeLayerView extends Layer implements ISize {
  private int mTxtSize;
  private Paint mPaint;
  private int[] mLocation1 = new int[2];
  private int[] mLocation2 = new int[2];
  private View mFirstView;
  private View mSecondView;
  private View mTargetView;
  private int mLocation[] = new int[2];
  GestureDetectorCompat detectorCompat;

  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mTxtSize = ScreenUtils.dp2px(getContext(), 10);
    mPaint.setTextSize(mTxtSize);
    init();
  }

  private void init() {
    detectorCompat = new GestureDetectorCompat(getContext(), new GestureDetector.SimpleOnGestureListener() {
      @Override
      public void onLongPress(MotionEvent e) {
        super.onLongPress(e);
        longPress = true;
        View mTargetView = findPressView((int) e.getRawX(), (int) e.getRawY());
        if (mFirstView != null && mSecondView != null) {
          mFirstView = mSecondView;
          mSecondView = null;
        }
        if (mFirstView == null) {
          mFirstView = mTargetView;
        } else {
          mSecondView = mTargetView;
        }
        invalidate();
      }
    });

  }

  @Override
  protected void onAfterTraversal(View rootView) {
    super.onAfterTraversal(rootView);
    invalidate();
  }

  boolean longPress;

  @Override
  protected boolean onBeforeInputEvent(View rootView, InputEvent event) {
    if (event instanceof MotionEvent) {
      if (((MotionEvent) event).getActionMasked() == MotionEvent.ACTION_DOWN) {
        longPress = false;
      }
      detectorCompat.onTouchEvent((MotionEvent) event);
      if (longPress) {
        ((MotionEvent) event).setAction(MotionEvent.ACTION_CANCEL);
      }
    }
    return false;
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (mFirstView != null) {
      drawBorder(canvas, mFirstView, mLocation1);
    }
    if (mFirstView == mSecondView) {
      return;
    }
    if (mSecondView == null) {
      return;
    }
    drawBorder(canvas, mSecondView, mLocation2);

    mPaint.setStyle(Paint.Style.FILL);
    ISizeConverter sizeConverter = getSizeConverter();
    //2 在 1 左边
    if (mLocation2[0] + mSecondView.getWidth() < mLocation1[0]) {
      canvas.save();
      int x = mLocation2[0] + mSecondView.getWidth();
      int y = mLocation2[1] + mSecondView.getHeight() / 2;
      int length = mLocation1[0] - (mLocation2[0] + mSecondView.getWidth());
      canvas.translate(x, y);
      mPaint.setColor(Color.RED);
      canvas.drawLine(0, 0, length, 0, mPaint);
      Size size = sizeConverter.convert(getContext(), length);
      String txt = size.toString();
      float txtLength = mPaint.measureText(txt);
      canvas.translate(length / 2 - txtLength / 2, mTxtSize / 2);
      mPaint.setColor(Color.WHITE);
      canvas.drawRect(-2, -mTxtSize - 2, txtLength + 2, 2, mPaint);
      mPaint.setColor(Color.RED);
      canvas.drawText(txt, 0, 0, mPaint);
      canvas.restore();
    }
    //2 在 1 右边
    if (mLocation2[0] > mLocation1[0] + mFirstView.getWidth()) {
      canvas.save();
      int x = mLocation2[0];
      int y = mLocation2[1] + mSecondView.getHeight() / 2;
      int length = mLocation2[0] - (mLocation1[0] + mFirstView.getWidth());
      canvas.translate(x, y);
      mPaint.setColor(Color.RED);
      canvas.drawLine(0, 0, -length, 0, mPaint);
      Size size = sizeConverter.convert(getContext(), length);
      String txt = size.toString();
      float txtLength = mPaint.measureText(txt);
      canvas.translate(-length / 2 - txtLength / 2, mTxtSize / 2);
      mPaint.setColor(Color.WHITE);
      canvas.drawRect(-2, -mTxtSize - 2, txtLength + 2, 2, mPaint);
      mPaint.setColor(Color.RED);
      canvas.drawText(txt, 0, 0, mPaint);
      canvas.restore();
    }
    //2 在 1 上边
    if (mLocation2[1] + mSecondView.getHeight() < mLocation1[1]) {
      canvas.save();
      int x = mLocation2[0] + mSecondView.getWidth() / 2;
      int y = mLocation2[1] + mSecondView.getHeight();
      int length = mLocation1[1] - (mLocation2[1] + mSecondView.getHeight());
      canvas.translate(x, y);
      mPaint.setColor(Color.RED);
      canvas.drawLine(0, 0, 0, length, mPaint);
      Size size = sizeConverter.convert(getContext(), length);
      String txt = size.toString();
      float txtLength = mPaint.measureText(txt);
      canvas.translate(-txtLength / 2, length / 2 + mTxtSize / 2);
      mPaint.setColor(Color.WHITE);
      canvas.drawRect(-2, -mTxtSize - 2, txtLength + 2, 2, mPaint);
      mPaint.setColor(Color.RED);
      canvas.drawText(txt, 0, 0, mPaint);
      canvas.restore();
    }
    //2 在 1 下边
    if (mLocation2[1] > mLocation1[1] + mFirstView.getHeight()) {
      canvas.save();
      int x = mLocation2[0] + mSecondView.getWidth() / 2;
      int y = mLocation2[1];
      int length = mLocation2[1] - (mLocation1[1] + mFirstView.getHeight());
      canvas.translate(x, y);
      mPaint.setColor(Color.RED);
      canvas.drawLine(0, 0, 0, -length, mPaint);
      Size size = sizeConverter.convert(getContext(), length);
      String txt = size.toString();
      float txtLength = mPaint.measureText(txt);
      canvas.translate(-txtLength / 2, -length / 2 + mTxtSize / 2);
      mPaint.setColor(Color.WHITE);
      canvas.drawRect(-2, -mTxtSize - 2, txtLength + 2, 2, mPaint);
      mPaint.setColor(Color.RED);
      canvas.drawText(txt, 0, 0, mPaint);
      canvas.restore();
    }

  }

  private void drawBorder(Canvas canvas, View view, int[] mLocation) {
    view.getLocationInWindow(mLocation);
    canvas.save();
    mPaint.setColor(Color.RED);
    mPaint.setStyle(Paint.Style.STROKE);
    canvas.drawRect(mLocation[0], mLocation[1], mLocation[0] + view.getWidth(), mLocation[1] + view.getHeight(), mPaint);
    canvas.restore();
  }


  protected View findPressView(int x, int y) {
    mTargetView = getRootView();
    traversal(mTargetView, x, y);
    return mTargetView;
  }

  private void traversal(View view, int x, int y) {

    if (view.getVisibility() != VISIBLE) {
      return;

    }
    if (inRange(view, x, y) == false) {
      return;
    }

    mTargetView = view;
    if (view instanceof ViewGroup) {
      ViewGroup viewGroup = ((ViewGroup) view);
      for (int i = 0; i < viewGroup.getChildCount(); i++) {
        View child = viewGroup.getChildAt(i);
        traversal(child, x, y);
      }
    }
  }

  private boolean inRange(View view, int x, int y) {
    view.getLocationOnScreen(mLocation);

    return (mLocation[0] <= x
        && mLocation[1] <= y
        && mLocation[0] + view.getWidth() >= x
        && mLocation[1] + view.getHeight() >= y);
  }


  public void onDetached(View view) {
    clean();
  }

  private void clean() {
    mTargetView = mFirstView = mSecondView = null;
  }


  private ISizeConverter getSizeConverter() {
    return sizeConverter == null ? ISizeConverter.CONVERTER : sizeConverter;
  }

  private ISizeConverter sizeConverter;

  @Override
  public void onSizeConvertChange(ISizeConverter converter) {
    sizeConverter = converter;
    invalidate();
  }
} public class TakeColorLayer extends Layer {
  private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
  private Paint txtPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  private int x;
  private int y;
  private int r;
  private boolean consume;
  private float lastX, lastY;
  private Bitmap bitmap;
  private int strokeWidth;
  private Path path;
  private Matrix matrix = new Matrix();
  private Rect mRect = new Rect();

  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    x = getRootView().getWidth() / 2;
    y = getRootView().getHeight() / 2;
    r = ScreenUtils.dp2px(getContext(), 60);
    paint.setStyle(Paint.Style.STROKE);
    strokeWidth = ScreenUtils.dp2px(getContext(), 10);
    path = new Path();
    path.addCircle(0, 0, r, Path.Direction.CCW);
    txtPaint.setStyle(Paint.Style.FILL);
    txtPaint.setTextSize(getTextSize());
  }

  @Override
  protected boolean onBeforeInputEvent(View rootView, InputEvent event) {
    if (!(event instanceof MotionEvent)) {
      return false;
    }
    MotionEvent motionEvent = ((MotionEvent) event);
    if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
      lastX = motionEvent.getX();
      lastY = motionEvent.getY();
      if (contains(lastX, lastY)) {
        consume = true;
      } else {
        consume = false;
      }
    }
    if (consume == false) {
      return false;
    }
    float curX = motionEvent.getX();
    float curY = motionEvent.getY();

    int dx = (int) (curX - lastX);
    int dy = (int) (curY - lastY);
    x += dx;

    y += dy;
    drawBitmap();
    invalidate();
    lastX = curX;
    lastY = curY;
    return true;
  }

  private void drawBitmap() {
    View rootView = getRootView();
    int w = rootView.getWidth();
    int h = rootView.getHeight();
    if (bitmap == null || bitmap.getWidth() < w || bitmap.getHeight() < h) {
      bitmap = BitmapCreater.create(w, h, Bitmap.Config.ARGB_8888);
    }
    if (bitmap == null) {
      return;
    }
    Canvas canvas = new Canvas(bitmap);
    rootView.draw(canvas);
  }

  @Override
  protected void onAfterTraversal(View rootView) {
    super.onAfterTraversal(rootView);
    invalidate();
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.translate(x, y);
    canvas.clipPath(path);

    if (bitmap != null && x >= 0 && y >= 0 && x < bitmap.getWidth() && y < bitmap.getHeight()) {
      canvas.save();
      canvas.translate(-x, -y);
      matrix.setScale(8, 8, x, y);
      canvas.drawBitmap(bitmap, matrix, paint);
      canvas.restore();

      int color = bitmap.getPixel(x, y);
      paint.setColor(color);
      paint.setStrokeWidth(strokeWidth);
      canvas.drawCircle(0, 0, r - strokeWidth, paint);

      drawText(canvas, color);
    }

    paint.setColor(Color.BLACK);
    paint.setStrokeWidth(0);


    canvas.drawLine(-r, 0, r, 0, paint);
    canvas.drawLine(0, -r, 0, r, paint);
    paint.setStrokeWidth(strokeWidth);
    paint.setColor(Color.RED);
    canvas.drawCircle(0, 0, r, paint);
  }

  private void drawText(Canvas canvas, int color) {
    canvas.save();
    String txt = String.format("#%08x", color).toUpperCase();
    txtPaint.getTextBounds(txt, 0, txt.length(), mRect);

    canvas.translate(-(mRect.right - mRect.left) / 2, -r / 2);

    txtPaint.setColor(color);
    canvas.drawRect(0, 0, mRect.width(), mRect.height(), paint);
    txtPaint.setColor(~color | 0xFF000000);
    canvas.drawText(txt, 0, mRect.height(), txtPaint);

    canvas.restore();
  }

  protected float getTextSize() {
    return ScreenUtils.dp2px(getContext(), 10);
  }

  private boolean contains(float lastX, float lastY) {
    double d = Math.sqrt(Math.pow((lastX - x), 2) + Math.pow((lastY - y), 2));
    return d < r;
  }

} public class TextColorLayer extends LayerTxtAdapter {


  @Override
  protected String getTxt(View view) {
    if (view instanceof TextView) {
      CharSequence charSequence = ((TextView) view).getText();
      if (charSequence instanceof SpannableString) {
        // TODO: 2019/2/17
      }
      int color = ((TextView) view).getCurrentTextColor();
      return String.format("#%08x", color);

    }
    return "";
  }


} public class TextSizeLayer extends LayerTxtAdapter implements ISize {
  @Override
  protected String getTxt(View view) {
    if (view instanceof TextView) {
      float size = ((TextView) view).getTextSize();
      return String.valueOf(getSizeConverter().convert(getContext(), size).getLength());
    }

    return "";
  }



  private ISizeConverter getSizeConverter() {
    return sizeConverter == null ? ISizeConverter.CONVERTER : sizeConverter;
  }

  private ISizeConverter sizeConverter;

  @Override
  public void onSizeConvertChange(ISizeConverter converter) {
    sizeConverter = converter;
    invalidate();
  }


} public class TranslationLayerView extends Layer implements ISize {
  private int mTxtSize;
  private int[] mLocation = new int[2];
  private Paint mPaint;
  private ISizeConverter mSizeConverter;
  private View mTargetView;
  GestureDetectorCompat detectorCompat;
  boolean longPress;


  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    mSizeConverter = ISizeConverter.CONVERTER;
    init(getContext());
    invalidate();

    detectorCompat = new GestureDetectorCompat(getContext(), new GestureDetector.SimpleOnGestureListener() {
      @Override
      public void onLongPress(MotionEvent e) {
        super.onLongPress(e);
        longPress = true;
        mTargetView = findPressView((int) e.getRawX(), (int) e.getRawY());

        invalidate();
      }
    });

  }


  private void init(Context context) {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.RED);
    mTxtSize = ScreenUtils.dp2px(getContext(), 10);
    mPaint.setTextSize(mTxtSize);
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (mTargetView == null) {
      return;
    }
    View parent = (View) mTargetView.getParent();
    parent.getLocationInWindow(mLocation);
//    View rootView = getRootView();
    canvas.translate(mLocation[0], mLocation[1]);
    float x = mTargetView.getX();
    float y = mTargetView.getY();
    int w = mTargetView.getWidth();
    int h = mTargetView.getHeight();

    mPaint.setStyle(Paint.Style.STROKE);
    canvas.drawRect(x, y, x + w, y + h, mPaint);
    mPaint.setStyle(Paint.Style.FILL);
    canvas.drawLine(0, y + h / 2, x, y + h / 2, mPaint);
    canvas.drawLine(x + w / 2, 0, x + w / 2, y, mPaint);
    canvas.drawLine(x + w, y + h / 2, parent.getWidth(), y + h / 2, mPaint);
    canvas.drawLine(x + w / 2, y + h, x + w / 2, parent.getHeight(), mPaint);


    drawLeftTxt(canvas, x, y, h);

    drawTopTxt(canvas, x, y, w);

    drawRightTxt(canvas, parent, x, y, w, h);

    canvas.save();
    String txt = mSizeConverter.convert(getContext(), parent.getHeight() - y - h).toString();
    float txtLength = mPaint.measureText(txt);
    canvas.translate(x + (w - txtLength) / 2, (parent.getHeight() - y - h - mTxtSize) / 2 + y + h);
    mPaint.setColor(Color.WHITE);
    canvas.drawRect(-2, -mTxtSize - 2, txtLength + 2, 2, mPaint);
    mPaint.setColor(Color.RED);
    canvas.drawText(txt, 0, 0, mPaint);
    canvas.restore();

  }

  private void drawRightTxt(Canvas canvas, View parent, float x, float y, int w, int h) {
    canvas.save();
    String txt = mSizeConverter.convert(getContext(), parent.getWidth() - x - w).toString();
    float txtLength = mPaint.measureText(txt);
    canvas.translate((parent.getWidth() - x - w - txtLength) / 2 + x + w, y + (h + mTxtSize) / 2);
    mPaint.setColor(Color.WHITE);
    canvas.drawRect(-2, -mTxtSize - 2, txtLength + 2, 2, mPaint);
    mPaint.setColor(Color.RED);
    canvas.drawText(txt, 0, 0, mPaint);
    canvas.restore();
  }

  private void drawTopTxt(Canvas canvas, float x, float y, int w) {
    canvas.save();
    String txt = mSizeConverter.convert(getContext(), y).toString();
    float txtLength = mPaint.measureText(txt);
    canvas.translate(x + (w - txtLength) / 2, (y + mTxtSize) / 2);
    mPaint.setColor(Color.WHITE);
    canvas.drawRect(-2, -mTxtSize - 2, txtLength + 2, 2, mPaint);
    mPaint.setColor(Color.RED);
    canvas.drawText(txt, 0, 0, mPaint);
    canvas.restore();
  }

  private void drawLeftTxt(Canvas canvas, float x, float y, int h) {
    canvas.save();
    String txt = mSizeConverter.convert(getContext(), x).toString();
    float txtLength = mPaint.measureText(txt);
    canvas.translate((x - txtLength) / 2, y + (h + mTxtSize) / 2);
    mPaint.setColor(Color.WHITE);
    canvas.drawRect(-2, -mTxtSize - 2, txtLength + 2, 2, mPaint);
    mPaint.setColor(Color.RED);
    canvas.drawText(txt, 0, 0, mPaint);
    canvas.restore();
  }

  Float downX, downY;

  @Override
  protected boolean onBeforeInputEvent(View rootView, InputEvent event) {
    if ((event instanceof MotionEvent) == false) {
      return false;
    }
    MotionEvent motionEvent = (MotionEvent) event;
    if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
      longPress = false;
      downX = downY = null;
    }
    if (longPress == false) {
      detectorCompat.onTouchEvent(motionEvent);
    } else {
      motionEvent.setAction(MotionEvent.ACTION_CANCEL);
      if (mTargetView == null) {
        return false;
      }
      if (downX == null) {
        downX = motionEvent.getRawX();
        downY = motionEvent.getRawY();
      } else {
        float curX = motionEvent.getRawX();
        float curY = motionEvent.getRawY();
        mTargetView.setTranslationX(mTargetView.getTranslationX() + (curX - downX));
        mTargetView.setTranslationY(mTargetView.getTranslationY() + (curY - downY));
        invalidate();
        downX = curX;
        downY = curY;
      }
    }
    return false;
  }


  protected View findPressView(int x, int y) {
    mTargetView = getRootView();
    traversal(mTargetView, x, y);
    return mTargetView;
  }

  private void traversal(View view, int x, int y) {
    if (view.getVisibility() != VISIBLE) {
      return;

    }
    if (inRange(view, x, y) == false) {
      return;
    }

    mTargetView = view;
    if (view instanceof ViewGroup) {
      ViewGroup viewGroup = ((ViewGroup) view);
      for (int i = 0, len = viewGroup.getChildCount(); i < len; i++) {
        View child = viewGroup.getChildAt(i);
        traversal(child, x, y);
      }
    }
  }

  private boolean inRange(View view, int x, int y) {
    view.getLocationOnScreen(mLocation);
    return (mLocation[0] <= x
        && mLocation[1] <= y
        && mLocation[0] + view.getWidth() >= x
        && mLocation[1] + view.getHeight() >= y);
  }


  @Override
  public void onSizeConvertChange(ISizeConverter converter) {
    mSizeConverter = converter;
    invalidate();
  }
} public class TreeView extends Layer {
  private Paint mPaint;
  private int mTabW;
  private int mTxtH;
  private int mCurLayer;
  private Matrix mMatrix;
  private Matrix mDrawMatrix;
  private int[] mLocation = new int[2];
  private float mLastX;
  private float mLastY;
  private int mMode = 0;
  private float mLastDist;
  private boolean mIsScale;

  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    init();
    invalidate();
  }

  private int convertSize(int leng) {
    return (int) getSizeConverter().convert(getContext(), leng).getLength();
  }

  private ISizeConverter getSizeConverter() {
    return ISizeConverter.CONVERTER;
  }

  private void init() {
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(Color.WHITE);
    mPaint.setTextSize(ScreenUtils.dp2px(getContext(), 12));
    mTabW = ScreenUtils.dp2px(getContext(), 20);
    Rect rect = new Rect();
    mPaint.getTextBounds("Aj", 0, 2, rect);
    mTxtH = rect.height() * 2;
//        setBackgroundColor(0x88000000);
    mDrawMatrix = new Matrix();
  }

  @Override
  protected void onAfterTraversal(View rootView) {
    super.onAfterTraversal(rootView);
    invalidate();
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(0x88000000);
    canvas.setMatrix(mDrawMatrix);
    mCurLayer = -1;
    layerCount(canvas, getRootView());
  }

  private void layerCount(Canvas canvas, View view) {
//    if (view == null || view instanceof RootContainerView || getViewFilter().filter(view) == false) {
//      return;
//    }
    mCurLayer++;
    drawLayer(canvas, view);
    if (view instanceof ViewGroup) {
      ViewGroup vg = ((ViewGroup) view);
      for (int i = 0; i < vg.getChildCount(); i++) {
        View child = vg.getChildAt(i);
        layerCount(canvas, child);
      }
    }
    mCurLayer--;
  }

  private void drawLayer(Canvas canvas, View view) {
    canvas.translate(0, mTxtH);
    canvas.save();
    if (view.getVisibility() != VISIBLE) {
      mPaint.setColor(0xFFAAAAAA);
    } else {
      mPaint.setColor(Color.WHITE);
    }
    for (int i = 0; i < mCurLayer; i++) {
      canvas.translate(mTabW, 0);
      canvas.drawText("|", 0, 0, mPaint);
    }
    canvas.translate(mTabW, 0);
    String txt = getInfo(view);
    canvas.drawText(txt, 0, 0, mPaint);
    if (view instanceof ImageView) {
      float len = mPaint.measureText(txt);
      canvas.translate(len, 0);
      drawBitmap(canvas, ((ImageView) view));
      canvas.translate(-len, 0);
    }
    canvas.restore();
  }

  private void drawBitmap(Canvas canvas, ImageView view) {
    if (view == null) {
      return;
    }
    Drawable drawable = view.getDrawable();
    if (drawable instanceof BitmapDrawable) {
      Bitmap bmp = ((BitmapDrawable) drawable).getBitmap();
      if (bmp != null && !bmp.isRecycled()) {
        if (mMatrix == null) {
          mMatrix = new Matrix();
        } else {
          mMatrix.reset();
        }
        float scale = mTxtH * 1.0f / bmp.getHeight();
        float w = scale * bmp.getWidth();
        canvas.drawText(" -bmp(w:" + convertSize(bmp.getWidth()) + " h:" + convertSize(bmp.getHeight()) + ")", w, 0, mPaint);
        mMatrix.setScale(scale, scale);
        canvas.translate(0, -mTxtH >> 1);
        canvas.drawBitmap(bmp, mMatrix, mPaint);
        canvas.translate(0, mTxtH >> 1);
      }
    }

  }

  protected String getInfo(View view) {
    StringBuilder sb = new StringBuilder(100);
    sb.append(view.getClass().getName()).append(" ");

    sb.append("-(w:").append(convertSize(view.getWidth()));
    sb.append(" h:").append(convertSize(view.getHeight())).append(") ");

    view.getLocationOnScreen(mLocation);
    sb.append(" -loc(x:")
        .append(convertSize(mLocation[0]))
        .append("-")
        .append(convertSize(mLocation[0] + view.getWidth()))
        .append(" y:")
        .append(convertSize(mLocation[1]))
        .append("-")
        .append(convertSize(mLocation[1] + view.getHeight()))
        .append(")");

    sb.append(" -P(l:").append(convertSize(view.getPaddingLeft())).append(" t:").append(convertSize(view.getPaddingTop()))
        .append(" r:").append(convertSize(view.getPaddingRight())).append(" b:").append(convertSize(view.getPaddingBottom())).append(")");

    ViewGroup.LayoutParams param = view.getLayoutParams();
    if (param instanceof ViewGroup.MarginLayoutParams) {
      ViewGroup.MarginLayoutParams marginLayoutParams = ((ViewGroup.MarginLayoutParams) param);
      sb.append(" -M(l:").append(convertSize(marginLayoutParams.leftMargin)).append(" t:").append(convertSize(marginLayoutParams.topMargin))
          .append(" r:").append(convertSize(marginLayoutParams.rightMargin)).append(" b:").append(convertSize(marginLayoutParams.bottomMargin)).append(")");

    }
    int visible = view.getVisibility();
    String visStr = "";
    if (visible == VISIBLE) {
      visStr = "visible";
    } else if (visible == INVISIBLE) {
      visStr = "invisible";
    } else if (visible == GONE) {
      visStr = "gone";
    }

    if (view instanceof TextView) {
      String txt = ((TextView) view).getText().toString();
      sb.append(" -txt:").append(txt);
    }
    sb.append(" -visible:").append(visStr).append(" ");
    sb.append(" -extra:").append(view.getTag(InfoLayer.INFO_KEY));

    return sb.toString();
  }

  @Override
  protected boolean onBeforeInputEvent(View rootView, InputEvent event) {
    if (event instanceof MotionEvent) {
      onTouchEvent((MotionEvent) event);
      return true;
    }
    return false;
  }

  private boolean onTouchEvent(MotionEvent event) {

    switch (event.getAction() & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_DOWN:
        mLastX = event.getX();
        mLastY = event.getY();
        mIsScale = false;
        mMode = 1;
        break;
      case MotionEvent.ACTION_UP:
        mMode = 0;
        break;
      case MotionEvent.ACTION_POINTER_UP:
        mMode -= 1;
        break;
      case MotionEvent.ACTION_POINTER_DOWN:
        mLastDist = spacing(event);
        mMode += 1;
        break;

      case MotionEvent.ACTION_MOVE:
        if (mMode >= 2) {
          float newDist = spacing(event);
          float factor = newDist / mLastDist;
          mDrawMatrix.postScale(factor, factor);
          mLastDist = newDist;
          mIsScale = true;
        } else {
          if (mIsScale) {
            break;
          }
          float curX = event.getX();
          float curY = event.getY();
          mDrawMatrix.postTranslate(curX - mLastX, curY - mLastY);
          mLastX = curX;
          mLastY = curY;
        }
        invalidate();
        break;
    }

    return true;
  }


  private float spacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return (float) Math.sqrt(x * x + y * y);
  }


  protected int px2dp(float px) {
    final float scale = getContext().getResources().getDisplayMetrics().density;
    return (int) (px / scale + 0.5f);
  }


} public class VerticalMeasureView extends Layer implements ISize {
  protected Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  protected int m20DP;
  protected int maxHeight;
  protected int minHeight;
  private int width;
  private boolean consume;
  private float lastX, lastY;
  private Rect rect = new Rect();

  @Override
  protected void onAttach(View rootView) {
    super.onAttach(rootView);
    init();
  }


  private void init() {
    mPaint.setColor(Color.BLACK);
    m20DP = ScreenUtils.dp2px(getContext(), 20);
    mPaint.setTextSize(ScreenUtils.dp2px(getContext(), 8));
    maxHeight = ScreenUtils.dp2px(getContext(), 10);
    minHeight = ScreenUtils.dp2px(getContext(), 5);
    width = ScreenUtils.dp2px(getContext(), 60);
    rect.left = (int) (getRootView().getWidth() / 2f - width / 2f);
    rect.top = 0;
    rect.right = (int) (getRootView().getWidth() / 2f + width / 2f);
    rect.bottom = getRootView().getHeight();
    invalidate();
  }

  @Override
  protected void onAfterTraversal(View rootView) {
    super.onAfterTraversal(rootView);
    invalidate();
  }

  @Override
  protected boolean onBeforeInputEvent(View rootView, InputEvent event) {
    if (!(event instanceof MotionEvent)) {
      return false;
    }
    MotionEvent motionEvent = ((MotionEvent) event);
    if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
      lastX = motionEvent.getX();
      lastY = motionEvent.getY();
      if (rect.contains((int) lastX, (int) lastY)) {
        consume = true;
      } else {
        consume = false;
      }
    }
    if (consume == false) {
      return false;
    }
    float curX = motionEvent.getX();
    float curY = motionEvent.getY();

    int dx = (int) (curX - lastX);
    int dy = (int) (curY - lastY);
    rect.left += dx;
    rect.right += dx;
    rect.top += dy;
    rect.bottom += dy;
    invalidate();
    lastX = curX;
    lastY = curY;
    return true;
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.translate(rect.left, rect.top);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setColor(0xbbFFFFFF);
    canvas.drawRect(0, 0, rect.right - rect.left, rect.bottom - rect.top, mPaint);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setColor(Color.BLACK);
    canvas.drawRect(0, 0, rect.right - rect.left, rect.bottom - rect.top, mPaint);
    mPaint.setStyle(Paint.Style.FILL);
    int h = rect.bottom - rect.top;
    int w = rect.right - rect.left;
    canvas.drawLine(0, 0, w, 0, mPaint);

    for (int i = 0; i <= h; i += m20DP) {
      canvas.drawLine(-maxHeight, i, maxHeight, i, mPaint);
      canvas.drawText(sizeConverter.convert(getContext(), i).toString(), maxHeight, i, mPaint);
      for (int j = i; j <= h && j < i + m20DP; j += m20DP / 10) {
        canvas.drawLine(-minHeight, j, minHeight, j, mPaint);
      }
    }
  }

  ISizeConverter sizeConverter = ISizeConverter.CONVERTER;

  @Override
  public void onSizeConvertChange(ISizeConverter converter) {
    sizeConverter = converter;
    invalidate();
  }
} public class ViewClassLayer extends LayerTxtAdapter {


    @Override
    protected String getTxt(View view) {
        return view.getClass().getSimpleName();
    }

} public class WidthHeightLayer extends LayerTxtAdapter implements ISize {


  @Override
  protected String getTxt(View view) {
    int w = view.getWidth();
    int h = view.getHeight();
    ISizeConverter converter = getSizeConverter();
    return converter.convert(getContext(), w).getLength() + ":" + converter.convert(getContext(), h).getLength();
  }


  private ISizeConverter getSizeConverter() {
    return sizeConverter == null ? ISizeConverter.CONVERTER : sizeConverter;
  }

  private ISizeConverter sizeConverter;

  @Override
  public void onSizeConvertChange(ISizeConverter converter) {
    sizeConverter = converter;
    invalidate();
  }

}
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;
import android.widget.Toast;
import com.wanjian.sak.SAK;
import com.wanjian.sak.config.Config;
import com.wanjian.sak.layer.impl.ActivityNameLayerView;
import com.wanjian.sak.layer.impl.BorderLayer;
import com.wanjian.sak.layer.impl.FragmentNameLayer;
import com.wanjian.sak.layer.impl.GridLayer;
import com.wanjian.sak.layer.impl.HorizontalMeasureView;
import com.wanjian.sak.layer.impl.MarginLayer;
import com.wanjian.sak.layer.impl.PaddingLayer;
import com.wanjian.sak.layer.impl.RelativeLayerView;
import com.wanjian.sak.layer.impl.TakeColorLayer;
import com.wanjian.sak.layer.impl.TextColorLayer;
import com.wanjian.sak.layer.impl.TextSizeLayer;
import com.wanjian.sak.layer.impl.TranslationLayerView;
import com.wanjian.sak.layer.impl.TreeView;
import com.wanjian.sak.layer.impl.VerticalMeasureView;
import com.wanjian.sak.layer.impl.ViewClassLayer;
import com.wanjian.sak.layer.impl.WidthHeightLayer;
package com.wanjian.sak.demo;



/**
 * Created by wanjian on 2017/3/7.
 */

public class MainActivity extends AppCompatActivity {

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DisplayMetrics app = getResources().getDisplayMetrics();
        setContentView(R.layout.layout);


        findViewById(R.id.install).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                Config config=new Config.Build(getApplicationContext())
//                    .addLayer(BorderLayer.class)
//                    .addLayer(HorizontalMeasureView.class)
//                    .addLayer(RelativeLayerView.class)
//                    .build();
//                SAK.init(getApplication(), config);
//



                Config config=new Config.Build(getApplicationContext())
//                    .addLayer(TestLayer.class)
                    .addLayer(BorderLayer.class, getIcon(R.drawable.sak_border_icon), getString(R.string.sak_border))
                    .addLayer(GridLayer.class, getIcon(R.drawable.sak_grid_icon), getString(R.string.sak_grid))
                    .addLayer(PaddingLayer.class, getIcon(R.drawable.sak_padding_icon), getString(R.string.sak_padding))
                    .addLayer(MarginLayer.class, getIcon(R.drawable.sak_margin_icon), getString(R.string.sak_margin))
                    .addLayer(WidthHeightLayer.class, getIcon(R.drawable.sak_width_height_icon), getString(R.string.sak_width_height))
                    .addLayer(TextColorLayer.class, getIcon(R.drawable.sak_text_color_icon), getString(R.string.sak_txt_color))
                    .addLayer(TextSizeLayer.class, getIcon(R.drawable.sak_text_size_icon), getString(R.string.sak_txt_size))
                    .addLayer(ActivityNameLayerView.class, getIcon(R.drawable.sak_page_name_icon), getString(R.string.sak_activity_name))
                    .addLayer(FragmentNameLayer.class, getIcon(R.drawable.sak_page_name_icon), getString(R.string.sak_fragment_name))