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();
}
}

