知乎专栏 |
在相对布局中,可以通过以下的属性让的组合让控件处于父容器左上角、右上角、左下角、右下角、上下左右居中,正居中等九个位置。属性如下:
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" />