Android约束布局-圆形定位
在本教程中,我们将讨论约束布局中的一个很酷的属性,即圆形定位。
我们将使用相同的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(); } }