6
\$\begingroup\$

I'd like to have a code review for a component which is supposed to be animatable on its height. It's supposed to be a transitioning element. I find it quite laggy on my phone.

private void init(AttributeSet attrs, int defStyle) {
    // Load attributes
    final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.GuillotineView, defStyle, 0);


    gradientFirstColor = a.getColor(R.styleable.GuillotineView_gradientFirstColor,Color.RED);
    gradientLastColor = a.getColor(R.styleable.GuillotineView_gradientLastColor, Color.YELLOW);
    maskHeight = a.getDimension(R.styleable.GuillotineView_maskHeight, 10.0f);
    shadowHeight = a.getDimension(R.styleable.GuillotineView_shadowHeight, 20.0f);
    borderRadius = a.getDimension(R.styleable.GuillotineView_borderRadius, 0.0f);
    a.recycle();
    this.refreshGradient();

    this.paint = new Paint();
    this.paint.setFlags(Paint.ANTI_ALIAS_FLAG);
    this.shadowPaint = new Paint();
    this.shadowPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
    setLayerType(LAYER_TYPE_SOFTWARE, this.shadowPaint);
}

private void refreshGradient() {
    this.gd = new GradientDrawable(GradientDrawable.Orientation.TR_BL, new int[]{gradientFirstColor,gradientLastColor});
    this.onSizeChanged(getWidth(), getHeight(), getWidth(), getHeight());
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    if (getWidth() <= 0 || (getHeight()- Math.round(shadowHeight) <= 0)) {
        return;
    }

    Bitmap gradientBitmap = DrawableExtension.drawableToBitmap(this.gd, getWidth(), getHeight()- Math.round(shadowHeight));
    BitmapShader shader = new BitmapShader(gradientBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    this.paint.setShader(shader);
    this.path = new Path();
    this.path.moveTo(0,this.borderRadius);
    this.path.lineTo(0,getHeight()-shadowHeight);
    this.path.lineTo(getWidth(),getHeight()-shadowHeight-maskHeight);
    this.path.lineTo(getWidth(),this.borderRadius);
    this.path.arcTo(new RectF(getWidth()-2*borderRadius,0f,(float)getWidth(),2f*borderRadius),0f, -90f);
    this.path.lineTo(borderRadius,0f);
    this.path.arcTo(new RectF(0f,0f,2f*borderRadius,2f*borderRadius),-90f, -90f);

    this.shadowPath = new Path();
    this.shadowPath.moveTo(0, 2 * borderRadius);
    this.shadowPath.lineTo(0, getHeight() - shadowHeight - 1);
    this.shadowPath.lineTo(getWidth(), getHeight() - 1 - shadowHeight - maskHeight);
    this.shadowPath.lineTo(getWidth(), 2 * borderRadius);
    this.shadowPaint.setColor(Color.RED);
    this.shadowPaint.setStyle(Paint.Style.FILL);
    this.shadowPaint.setShadowLayer(shadowHeight + 1, 0, 0, Color.GRAY);

}

// TODO optimize it: it should not have this much computation in the draw method
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if(shadowHeight > 0) {
        canvas.drawPath(shadowPath,this.shadowPaint);
    }
    canvas.drawPath(this.path, paint);
}

This component is used as a shared element within two fragments. its height inside both fragment is different so I can have a transitionning animation.

private void prepareTransitionGuillotine(Fragment fragmentFromTransition, Fragment fragment) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        ChangeBounds guillotineTransition = new ChangeBounds();
        guillotineTransition.setInterpolator(new OvershootInterpolator(0.5f));
        guillotineTransition.setDuration(700);
        fragment.setSharedElementEnterTransition(guillotineTransition);
        fragment.setSharedElementReturnTransition(guillotineTransition);

        Fade transition = new Fade();
        transition.setDuration(300);
        transition.setInterpolator(new DecelerateInterpolator());
        fragment.setEnterTransition(transition);
        fragmentFromTransition.setExitTransition(transition);
    }
}

It's used like this when pushing a fragment on the screen

prepareTransitionGuillotine(currentFragment, fragment);
manager.beginTransaction().addSharedElement(currentFragment.guillotineView,"guillotineTransition").replace(R.id.fragment_container, fragment).addToBackStack(backstackName).commit();
\$\endgroup\$
7
  • \$\begingroup\$ the draw method is quite efficent there is no need to optimize on that part... how do you know that the onDraw(Canvas canvas) method is the problem? \$\endgroup\$ Commented Feb 13, 2018 at 10:15
  • \$\begingroup\$ I just see the animation flickering that's my only clue on it :) \$\endgroup\$
    – Astyan
    Commented Feb 13, 2018 at 10:23
  • \$\begingroup\$ onDraw is called only once, when the Activity becomes visible or if you explicitly call invalidate(). so i guess your Activity is somewhere else forced to be redrawn. Can you show us, where you control the drawing? \$\endgroup\$ Commented Feb 13, 2018 at 10:30
  • \$\begingroup\$ yes i know - onDraw can be called on other issues as well (resize, rotate etc).... \$\endgroup\$ Commented Feb 13, 2018 at 10:31
  • 1
    \$\begingroup\$ At least, I presume the change bounds calls the onsizechange on each animation step \$\endgroup\$
    – Astyan
    Commented Feb 13, 2018 at 11:08

0