Android约束布局-圆形定位

时间:2020-02-23 14:28:50  来源:igfitidea点击:

在本教程中,我们将讨论约束布局中的一个很酷的属性,即圆形定位。
我们将使用相同的Android应用程序来开发自定义时钟UI。

Android约束布局

我们已经在本教程中讨论了约束布局。

这是一个很棒的布局,并提供了许多要点来设置彼此之间的视图。

通过引入ConstraintLayout 1.1,我们现在也可以基于角度和半径设置位置。
这称为圆形定位。

循环定位

圆形定位包含以下三个属性,这些属性需要在ConstraintLayout子视图中定义:

  • layout_constraintCircle:这将在视图B中定义。
    它将设置为A的ID。

  • layout_constraintCircleRadius:两个视图的中心之间的距离。

  • layout_constraintCircleAngle:角度定义B相对于A的圆形位置。
    以度数(0到360)设置。
    角度为0表示视图B垂直于A。

我们可以创建一个完整的圆,并以不同角度放置视图。

在以下部分中,我们将创建一个自定义时钟,其中TextViews将围绕ImageView循环放置。

每个文本视图将代表一个小时的时钟。
另外,我们将另一个ImageView设置为秒指针,该指针将根据定义的值设置动画并更新圆形位置。

项目结构

代码

让我们画一个用作时钟指针的圆形。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="https://schemas.android.com/apk/res/android"
  android:shape="oval">

  <solid android:color="#ff00ff" 

  <size
      android:width="8dp"
      android:height="8dp" 
</shape>

下面给出了activity_main.xml布局文件的代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".MainActivity">

  <ImageView
      android:id="@+id/imgClock"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@android:drawable/checkbox_off_background"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent" 

  <ImageView
      android:id="@+id/imgPointer"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/circle"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="0"
      app:layout_constraintCircleRadius="70dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="12"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="0"
      app:layout_constraintCircleRadius="90dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="1"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="30"
      app:layout_constraintCircleRadius="90dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="2"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="60"
      app:layout_constraintCircleRadius="90dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="3"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="90"
      app:layout_constraintCircleRadius="90dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="4"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="120"
      app:layout_constraintCircleRadius="90dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="5"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="150"
      app:layout_constraintCircleRadius="90dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="6"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="180"
      app:layout_constraintCircleRadius="90dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="7"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="210"
      app:layout_constraintCircleRadius="90dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="8"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="240"
      app:layout_constraintCircleRadius="90dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="9"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="270"
      app:layout_constraintCircleRadius="90dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="10"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="300"
      app:layout_constraintCircleRadius="90dp" 

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="11"
      android:textColor="@android:color/black"
      app:layout_constraintCircle="@+id/imgClock"
      app:layout_constraintCircleAngle="330"
      app:layout_constraintCircleRadius="90dp" 

</android.support.constraint.ConstraintLayout>

MainActivity.java类的代码如下:

package com.theitroad.androidconstraintcircles;

import android.animation.ValueAnimator;
import android.support.constraint.ConstraintLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.Toast;

import java.util.concurrent.TimeUnit;

public class MainActivity extends AppCompatActivity {

  ImageView imgPointer, imgClock;
  ValueAnimator clockAnimator;

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

      imgPointer = findViewById(R.id.imgPointer);
      imgClock = findViewById(R.id.imgClock);

      clockAnimator = animatePointer(TimeUnit.SECONDS.toMillis(60));

      imgClock.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {

              if (clockAnimator.isPaused()) {
                  clockAnimator.resume();
                  Toast.makeText(getApplicationContext(), "Resumed", Toast.LENGTH_SHORT).show();
              } else if (clockAnimator.isRunning()) {
                  Toast.makeText(getApplicationContext(), "Paused", Toast.LENGTH_SHORT).show();
                  clockAnimator.pause();
              } else
                  clockAnimator.start();

          }
      });
  }

  
  private ValueAnimator animatePointer(long orbitDuration) {
      ValueAnimator anim = ValueAnimator.ofInt(0, 359);

      anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
          @Override
          public void onAnimationUpdate(ValueAnimator valueAnimator) {
              int val = (Integer) valueAnimator.getAnimatedValue();
              ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) imgPointer.getLayoutParams();
              layoutParams.circleAngle = val;
              imgPointer.setLayoutParams(layoutParams);
          }
      });
      anim.setDuration(orbitDuration);
      anim.setInterpolator(new LinearInterpolator());
      anim.setRepeatMode(ValueAnimator.RESTART);
      anim.setRepeatCount(ValueAnimator.INFINITE);

      return anim;
  }
}

在上面的代码中,我们定义了一个" ValueAnimator"类对象。
它用于基于值对视图进行动画处理。

当ValueAnimator的间隔更新时,我们将更新imgPointer约束相对于中心ImageView的圆形位置角度。

在这里它每秒发生一次,因此给您的印象是imgPointer是时钟的秒针!

但是,上面的代码中存在漏洞。

动画与活动的生命周期无关。
因此,如果应用程序在后台运行,将导致内存泄漏。
为此,我们可以在onResume(),onPause()和onDestroy()方法中禁用和启用ValueAnimator动画。

在上面的MainActivity中添加以下代码段:

@Override
  protected void onResume() {
      super.onResume();
      if (clockAnimator != null) {
          if (clockAnimator.isPaused()) {
              clockAnimator.resume();
          }
      }
  }

  @Override
  protected void onPause() {
      super.onPause();
      if (clockAnimator.isRunning()) {
          clockAnimator.pause();
      }
  }

  @Override
  protected void onDestroy() {
      super.onDestroy();
      if (clockAnimator.isRunning()) {
          clockAnimator.cancel();
      }
  }