Mircular positioning with Constraint Layout

Posted by menriquez on Fri, 17 May 2019 18:56:10 +0200

#0 Preface

In the last article, we learned about the basic use of Constraint Layout. If you don't know Constraint Layout, go and learn about it.

Guidelines for Constraint Layout Use

https://www.jianshu.com/p/958887ed4f5f

This number has also pushed some articles:

Learn some new features of Constraint Layout again

This article focuses on Constraint Layout's Circular positioning function.

Catalog

# 1 Introduction and Use

What is Circular positioning? We can call it circular positioning, that is, taking the target control as the center of the circle, by setting the angle and radius to determine the position of our current control, such as the official map:

For introductory use, you can see my previous blog about the use of Circular positioning, mainly layout_constraintCircle lock target control, layout_constraintCircleRadius and layout_constraintCircleAngle control radius and angle respectively. I will not repeat it here.

Guidelines for Constraint Layout Use

https://www.jianshu.com/p/958887ed4f5f

# 2Circular positioning advancement

Let's first look at the target effect:

1. Layout

The layout of the xml file is relatively long, the content is actually very simple, mainly four Floating Action Buttons and three groups, at this time you may be confused, why there are three groups?

I'll answer this question. As we mentioned in the previous article, Group is used to display and hide the unified control view in Constraint Layout. If only one Group can't display and hide our control in an orderly way, Floating Action Button can only use Group to manage the display and hide of Floating Action Button, because it can't use setVisibility method, so it uses three Groups to manage the display and hide of Floating Action Button. P implements the orderly display and hiding of the three Floating Action Buttons in the figure above (Floating Action Button is intended to reduce the workload instead of ImageView, but the problems caused by Floating Action Button increase the workload, haha-). activity_constraint.xml is as follows:

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.orient.test.ui.activity.ConstraintActivity">

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="32dp"
        android:layout_marginEnd="32dp"
        android:backgroundTint="@color/colorAccent"
        android:padding="10dp"
        android:src="@drawable/ic_constraint_add"
        app:fabSize="normal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:pressedTranslationZ="20dp"
        app:rippleColor="#1f000000" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_like"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="32dp"
        android:layout_marginEnd="32dp"
        android:visibility="gone"
        android:backgroundTint="@color/colorAccent"
        android:padding="10dp"
        android:src="@drawable/ic_constraint_like"
        app:fabSize="normal"
        app:layout_constraintCircle="@+id/fab_add"
        app:layout_constraintCircleRadius="80dp"
        app:layout_constraintCircleAngle="270"
        app:pressedTranslationZ="20dp"
        app:rippleColor="#1f000000" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_write"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="32dp"
        android:layout_marginEnd="32dp"
        android:backgroundTint="@color/colorAccent"
        android:padding="10dp"
        android:src="@drawable/ic_constraint_write"
        app:fabSize="normal"
        app:layout_constraintCircle="@+id/fab_add"
        app:layout_constraintCircleRadius="80dp"
        app:layout_constraintCircleAngle="315"
        app:pressedTranslationZ="20dp"
        app:rippleColor="#1f000000" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="32dp"
        android:layout_marginEnd="32dp"
        android:backgroundTint="@color/colorAccent"
        android:padding="10dp"
        android:src="@drawable/ic_constraint_top"
        app:fabSize="normal"
        app:layout_constraintCircle="@+id/fab_add"
        app:layout_constraintCircleRadius="80dp"
        app:layout_constraintCircleAngle="360"
        app:pressedTranslationZ="20dp"
        app:rippleColor="#1f000000" />

    <android.support.constraint.Group
        android:id="@+id/gp_like"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="fab_like"/>

    <android.support.constraint.Group
        android:id="@+id/gp_write"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="fab_write"/>

    <android.support.constraint.Group
        android:id="@+id/gp_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="fab_top"/>

</android.support.constraint.ConstraintLayout>

2. Coding

First, identify the examples we need to use:

private FloatingActionButton mAdd;
private FloatingActionButton mLike;
private FloatingActionButton mWrite;
private FloatingActionButton mTop;
private Group likeGroup;
private Group writeGroup;
private Group topGroup;
// A collection of animations used to control the orderly playback of animations.
private AnimatorSet animatorSet;
// Radius of circle
private int radius;
// Floating Action Button has the same width and height
private int width;

Then we initialize our control. The code here is relatively simple. We will introduce initListener() later.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_constraint);

    initWidget();
    initListener();
}

@Override
protected void onResume() {
    super.onResume();

    // Dynamic acquisition of the width of Floating Action Button
    mAdd.post(new Runnable() {
        @Override
        public void run() {
            width = mAdd.getMeasuredWidth();
        }
    });
    // The radius set in the xml file
    radius = UiUtils.dp2px(this, 80);
}

private void initWidget() {
    mAdd = findViewById(R.id.fab_add);
    mLike = findViewById(R.id.fab_like);
    mTop = findViewById(R.id.fab_top);
    mWrite = findViewById(R.id.fab_write);
    likeGroup = findViewById(R.id.gp_like);
    writeGroup = findViewById(R.id.gp_write);
    topGroup = findViewById(R.id.gp_top);
    // Hide three pop-up Floating Action Buttons
    setViewVisible(false);
}

private void setViewVisible(boolean isShow) {
    likeGroup.setVisibility(isShow?View.VISIBLE:View.GONE);
    writeGroup.setVisibility(isShow?View.VISIBLE:View.GONE);
    topGroup.setVisibility(isShow?View.VISIBLE:View.GONE);
}

Our focus is in initListener(), the idea is to use attribute animation to control ConstraintLayout.LayoutParams, thereby controlling the angle and radius of Circular positioning:

private void initListener() {
    mAdd.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // You can't click when playing animation.
            if(animatorSet != null && animatorSet.isRunning())
                return;

            // Judging whether to play or hide animation
            if(likeGroup.getVisibility() != View.VISIBLE) {
                animatorSet = new AnimatorSet();
                ValueAnimator likeAnimator = getValueAnimator(mLike, false, likeGroup,0);
                ValueAnimator writeAnimator = getValueAnimator(mWrite, false, writeGroup,45);
                ValueAnimator topAnimator = getValueAnimator(mTop, false, topGroup,90);
                animatorSet.playSequentially(likeAnimator, writeAnimator, topAnimator);
                animatorSet.start();
            }else {
                animatorSet = new AnimatorSet();
                ValueAnimator likeAnimator = getValueAnimator(mLike, true, likeGroup,0);
                ValueAnimator writeAnimator = getValueAnimator(mWrite, true, writeGroup,45);
                ValueAnimator topAnimator = getValueAnimator(mTop, true, topGroup,90);
                animatorSet.playSequentially(topAnimator, writeAnimator, likeAnimator);
                animatorSet.start();
            }

        }
    });
}

/**
 * Get Value Animator
 * 
 * @param button FloatingActionButton
 * @param reverse Start or hide
 * @param group Group
 * @param angle angle Angle of rotation
 * @return ValueAnimator
 */
private ValueAnimator getValueAnimator(final FloatingActionButton button, final boolean reverse, final Group group, final int angle) {
    ValueAnimator animator;
    if (reverse)
        animator = ValueAnimator.ofFloat(1, 0);
    else
        animator = ValueAnimator.ofFloat(0, 1);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float v = (float) animation.getAnimatedValue();
            ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) button.getLayoutParams();
            params.circleRadius = (int) (radius * v);
            //params.circleAngle = 270f + angle * v;
            params.width = (int) (width * v);
            params.height = (int) (width * v);
            button.setLayoutParams(params);
        }
    });
    animator.addListener(new SimpleAnimation() {
        @Override
        public void onAnimationStart(Animator animation) {
            group.setVisibility(View.VISIBLE);
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            if(group == likeGroup && reverse){
                setViewVisible(false);
            }
        }
    });
    animator.setDuration(300);
    animator.setInterpolator(new DecelerateInterpolator());
    return animator;
}

abstract class SimpleAnimation implements Animator.AnimatorListener{
    @Override
    public void onAnimationStart(Animator animation) {
    }

    @Override
    public void onAnimationEnd(Animator animation) {
    }

    @Override
    public void onAnimationCancel(Animator animation) {
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
    }
}

Then the effect comes out:

If you feel that the pop-up curve is not smooth enough, you can cancel the comment on //params.circleAngle = 270f + angle * v in the getValue Animator method; the effect of this line is as good as that at the beginning of this chapter.

summary

The idea of this paper is to use attribute animation to control ConstraintLayout.LayoutParams, so as to control the angle and radius of Circular positioning. The content is relatively simple, if you have to master the use of attribute animation and Constraint Layout. My level is limited, inevitably there are mistakes, if there are mistakes, welcome to put forward.

https://github.com/mCyp/Test

Free access to android Development Architecture (including Fulter, Advanced UI, Performance Optimization, Architect Course, NDK, Kotlin, React Native + Weex) and Topics Summary of First-Line Internet Companies'android Interview can be added [Android Development Architecture]

Topics: Android xml Attribute github