Home | 简体中文 | 繁体中文 | 杂文 | Github | 知乎专栏 | Facebook | Linkedin | Youtube | 打赏(Donations) | About
知乎专栏

7.3. 样式布局

7.3.1. ConstraintLayout

7.3.1.1. 父容器定位

在相对布局中,可以通过以下的属性让的组合让控件处于父容器左上角、右上角、左下角、右下角、上下左右居中,正居中等九个位置。属性如下:

		
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控件下边平齐		
		
			

7.3.1.2. 对齐布局

垂直/水平剧中

		
    <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" />		
		
			

7.3.2. LinearLayout

7.3.2.1. 外边距设置

			
    android:layout_marginLeft="10dp"
    android:layout_marginTop="10dp"
    android:layout_marginRight="10dp"
    android:layout_marginBottom="10dp"
			
			

7.3.2.2. 内边距设置

			
    android:padding="10dp"
			
			

7.3.2.3. 水平剧中

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>			
			
			

7.3.3. FrameLayout

7.3.3.1. FrameLayout 事件穿透

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

7.3.3.2. 叠加层

			
<?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>			
			
			

7.3.3.3. 悬浮并且可以拖动改变位置的 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();
			
			

7.3.4. 声音波形图

		
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" />