| 知乎专栏 |
在相对布局中,可以通过以下的属性让的组合让控件处于父容器左上角、右上角、左下角、右下角、上下左右居中,正居中等九个位置。属性如下:
android:layout_alignParentLeft="true" 父容器左边 android:layout_alignParentRight="true" 父容器右边 android:layout_alignParentTop="true" 父容器顶部 android:layout_alignParentBottom="true" 父容器底部 android:layout_centerHorizontal="true" 水平方向居中 android:layout_centerVertical="true" 垂直方向居中 android:layout_centerInParent="true" 水平垂直都居中
android:layout_toLeftOf="@+id/button1" 在button1控件左方 android:layout_toRightOf="@+id/button1" 在button1控件右方 android:layout_above="@+id/button1" 在button1控件上方 android:layout_below="@+id/button1" 在button1控件下方 android:layout_alignLeft="@+id/button1" 与button1控件左边平齐 android:layout_alignRight="@+id/button1" 与button1控件右边平齐 android:layout_alignTop="@+id/button1" 与button1控件上边平齐 android:layout_alignBottom="@+id/button1" 与button1控件下边平齐
垂直/水平剧中
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:gravity="center_horizontal"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="唤醒词"
android:textColor="#70ffffff"
android:textSize="14sp" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="小梅小梅"
android:textColor="#00F8C1"
android:textSize="24sp" />
</LinearLayout>
FrameLayout 浮动在另其他 UI 上方,点击 FrameLayout 某些问之,触发了下面的事件,解决方法是增加:
android:clickable="true"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:RoundedCornerImageView="http://schemas.android.com/apk/res-auto"
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"
android:background="@color/transparent"
tools:ignore="MissingDefaultResource"
android:clickable="true">
方法二
mFrameLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundGravity="left|top"
tools:context=".BlankFragment">
<TextView
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@color/colorPrimary"
android:gravity="bottom|right"
android:text="第一层" />
<TextView
android:layout_width="150dp"
android:layout_height="150dp"
android:background="@color/colorAccent"
android:gravity="bottom|right"
android:text="第二层" />
<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@color/colorPrimaryDark"
android:gravity="bottom|right"
android:text="第三层" />
</FrameLayout>
figure.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageViewFigure"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="MissingConstraints" />
</FrameLayout>
activity_main.xml 中引用
<FrameLayout
android:id="@+id/frameLayoutFigure"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_gravity="bottom|end"
android:layout_marginEnd="50dp"
android:layout_marginBottom="80dp"
android:visibility="visible">
<include
layout="@layout/figure"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
package cn.netkiller.student.fragment;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.drawable.AnimationDrawable;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.annotation.DrawableRes;
import cn.aigcsst.student.R;
public class AnimationFigure {
private static final String TAG = AnimationFigure.class.getSimpleName();
private final ImageView imageViewFigure;
private final FrameLayout frameLayoutFigure;
private final AnimationDrawable animationDrawable;
private int screenWidth, screenHeight;
View.OnTouchListener onTouchListener = new View.OnTouchListener() {
private float x;
private float y;
private float rawX;
private float rawY;
private long downTime, eventTime;
private int figureWidth, figureHeight;
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
// float rawX = motionEvent.getRawX();
// float rawY = motionEvent.getRawY();
// float x = motionEvent.getX();
// float y = motionEvent.getY();
Log.e(TAG, "getAction: " + motionEvent.getAction() +
" getX: " + motionEvent.getX() +
" getY: " + motionEvent.getY() +
" getRawX: " + motionEvent.getRawX() +
" getRawY: " + motionEvent.getRawY() +
" Time: " + (motionEvent.getEventTime() - motionEvent.getDownTime())
);
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
figureWidth = view.getWidth();
figureHeight = view.getHeight();
Log.e(TAG, "screenWidth: " + screenWidth);
Log.e(TAG, "figureWidth: " + figureWidth);
downTime = motionEvent.getDownTime();
x = motionEvent.getX();
y = motionEvent.getY();
break;
case MotionEvent.ACTION_MOVE:
rawX = motionEvent.getRawX();
rawY = motionEvent.getRawY();
float moveToX = rawX - x;
float moveToY = rawY - y;
Log.e(TAG, "X: " + moveToX + " Y: " + moveToY + " screen x: " + screenWidth);
if (moveToX < 0) {
moveToX = 0;
} else if (moveToX + figureWidth > screenWidth) {
moveToX = screenWidth - figureWidth;
}
if (moveToY < 0) {
moveToY = 0;
} else if (moveToY + figureHeight > screenHeight) {
moveToY = screenHeight - figureHeight;
}
Log.e(TAG, "figureWidth: " + figureWidth + "Y: " + moveToY);
frameLayoutFigure.setX(moveToX);
frameLayoutFigure.setY(moveToY);
break;
case MotionEvent.ACTION_UP:
eventTime = motionEvent.getEventTime();
if (eventTime - downTime > 3000) {
frameLayoutFigure.setVisibility(View.GONE);
}
break;
}
return false;
}
};
public AnimationFigure(Activity activity, @DrawableRes int drawableRes) {
this.screenWidth = activity.getWindowManager().getDefaultDisplay().getWidth();
this.screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
this.imageViewFigure = activity.findViewById(R.id.imageViewFigure);
this.imageViewFigure.setImageResource(drawableRes);
this.animationDrawable = (AnimationDrawable) this.imageViewFigure.getDrawable();
this.frameLayoutFigure = activity.findViewById(R.id.frameLayoutFigure);
this.imageViewFigure.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
this.imageViewFigure.setOnTouchListener(onTouchListener);
}
public AnimationFigure(View view, @DrawableRes int drawableRes) {
this.imageViewFigure = view.findViewById(R.id.imageViewFigure);
this.imageViewFigure.setBackgroundResource(drawableRes);
this.animationDrawable = (AnimationDrawable) this.imageViewFigure.getBackground();
this.frameLayoutFigure = view.findViewById(R.id.frameLayoutFigure);
this.imageViewFigure.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
this.imageViewFigure.setOnTouchListener(onTouchListener);
}
public void start() {
animationDrawable.start();
}
public void start(int delayMillis) {
start();
new Handler(Looper.getMainLooper()).postDelayed(() -> {
stop();
}, delayMillis);
// new Handler(Looper.getMainLooper()).postDelayed(this::start, delayMillis);
}
public void stop() {
animationDrawable.stop();
}
public void setVisibility(boolean staus) {
if (staus) {
this.frameLayoutFigure.setVisibility(View.VISIBLE);
} else {
this.frameLayoutFigure.setVisibility(View.INVISIBLE);
}
}
public void animationFigure() {
AnimationDrawable animationDrawable = (AnimationDrawable) imageViewFigure.getDrawable();
if (animationDrawable.isRunning()) {
animationDrawable.stop();
frameLayoutFigure.setVisibility(View.INVISIBLE);
} else {
animationDrawable.start();
frameLayoutFigure.setVisibility(View.VISIBLE);
}
Log.d(TAG, "animationFigure: " + animationDrawable.isRunning());
Log.d(TAG, "animationFigure isActivated: " + imageViewFigure.isActivated());
}
public void running(boolean status, int delayMillis) {
if (status) {
start(delayMillis);
} else {
start();
}
}
public enum Figure {
LISTENING, SPEAKING
}
}
播放逐帧动画
AnimationFigure animationFigure = new AnimationFigure(activity, drawableRes);
animationFigure.setVisibility(true);
animationFigure.start();
package cn.netkiller.album.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class SoundWaveView extends View {
private Paint paint;
private float[] amplitudes;
public SoundWaveView(Context context) {
super(context);
init();
}
public SoundWaveView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(2);
}
public void setAmplitudes(float[] amplitudes) {
this.amplitudes = amplitudes;
invalidate(); // 刷新视图
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (amplitudes == null) {
return;
}
int width = getWidth();
int height = getHeight();
int centerY = height / 2;
for (int i = 0; i < amplitudes.length; i++) {
float x = width * i / amplitudes.length;
float y = centerY + amplitudes[i] * centerY;
canvas.drawLine(x, centerY, x, y, paint);
}
}
}
public class MainActivity extends AppCompatActivity {
private SoundWaveView soundWaveView;
private AudioRecord audioRecord;
private boolean isRecording = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
soundWaveView = findViewById(R.id.sound_wave_view);
int bufferSize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
startRecording();
}
private void startRecording() {
isRecording = true;
audioRecord.startRecording();
new Thread(new Runnable() {
@Override
public void run() {
short[] buffer = new short[1024];
while (isRecording) {
int read = audioRecord.read(buffer, 0, buffer.length);
if (read > 0) {
float[] amplitudes = new float[read];
for (int i = 0; i < read; i++) {
amplitudes[i] = buffer[i] / 32768f; // 归一化为[-1, 1]
}
soundWaveView.setAmplitudes(amplitudes);
}
}
audioRecord.stop();
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
isRecording = false;
}
}
<com.example.myapplication.SoundWaveView
android:id="@+id/sound_wave_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />