(C)アンドロイドフローティングウィンドウを実行するデスクトップ画面の録音ソース

1.背景画面記録サービス 

package com.android.systemui;

import android.annotation.TargetApi;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Binder;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.RequiresApi;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;

import android.graphics.PixelFormat;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.android.systemui.ScreenRecordActivity;

import com.android.systemui.R;
import com.android.systemui.util.CommonUtil;
import com.android.systemui.util.FileUtil;
import com.android.systemui.util.ScreenUtil;

import android.util.Log;

import java.io.File;
import java.io.IOException;

public class ScreenRecordService extends Service implements Handler.Callback {

    private final String TAG = "ScreenRecordService";
    private final String RecordScreenPath = "/RecordScreen/";
    private MediaProjectionManager mProjectionManager;
    private MediaProjection mMediaProjection;
    private MediaRecorder mMediaRecorder;
    private VirtualDisplay mVirtualDisplay;

    private boolean mIsRunning;
    private int mRecordWidth = CommonUtil.getScreenWidth();
    private int mRecordHeight = CommonUtil.getScreenHeight();
    private int mScreenDpi = CommonUtil.getScreenDpi();


    private int mResultCode;
    private Intent mResultData;

    //录屏文件的保存地址
    private String mRecordFilePath;

    private Handler mHandler;
    //已经录制多少秒了
    private int mRecordSeconds = 0;

    private static final int MSG_TYPE_COUNT_DOWN = 110;

    /**
     * 定义浮动窗口布局
     */
    LinearLayout mlayout;

    TextView recordTime;
    /**
     * 悬浮窗控件
     */
    ImageView recordHintButton;
    LinearLayout stopRecord;
    /**
     * 悬浮窗的布局
     */
    WindowManager.LayoutParams wmParams;
    LayoutInflater inflater;
    /**
     * 创建浮动窗口设置布局参数的对象
     */
    WindowManager mWindowManager;

    //触摸监听器
    GestureDetector mGestureDetector;

    FloatingListener mFloatingListener;

    @Override
    public IBinder onBind(Intent intent) {
        return new RecordBinder();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mIsRunning = false;
        mMediaRecorder = new MediaRecorder();
        mHandler = new Handler(Looper.getMainLooper(), this);
        ScreenUtil.addRecordListener(recordListener);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        clearRecordElement();
        try {
            if (mlayout != null) {
                // 移除悬浮窗口
                mWindowManager.removeView(mlayout);
            }
        } catch (Exception e) {
            Log.e(TAG, "not attached to window manager");
        }
    }

    public boolean isReady() {
        return mMediaProjection != null && mResultData != null;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void clearRecordElement() {
        try {
            clearAll();
            if (mMediaRecorder != null) {
                mMediaRecorder.reset();
                mMediaRecorder.release();
                mMediaRecorder = null;
            }
        } catch (Exception e) {
            mMediaRecorder = null;
            Log.w(TAG, "clearRecordElement  exception e = " + e);
        } finally {
            mResultData = null;
            mIsRunning = false;
            mRecordSeconds = 0;
        }
    }

    public boolean ismIsRunning() {
        return mIsRunning;
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public void setResultData(int resultCode, Intent resultData) {
        clearRecordElement();
        mResultCode = resultCode;
        mResultData = resultData;

        mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        if (mProjectionManager != null) {
            mMediaProjection = mProjectionManager.getMediaProjection(mResultCode, mResultData);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public boolean startRecord() {
        Log.d(TAG, "startRecord mIsRunning = " + mIsRunning);

        initWindow();//设置窗口的参数
        initFloating();//设置悬浮窗图标
        if (mMediaProjection == null) {
            mMediaProjection = mProjectionManager.getMediaProjection(mResultCode, mResultData);
        }
        setUpMediaRecorder();
        createVirtualDisplay();
        mMediaRecorder.start();
        ScreenUtil.startRecord();
        //最多录制三分钟
        mHandler.sendEmptyMessageDelayed(MSG_TYPE_COUNT_DOWN, 1000);
        mIsRunning = true;
        return true;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public boolean stopRecord(String tip) {
        Log.d(TAG, "stopRecord mIsRunning = " + mIsRunning);
        if (mlayout != null) {
            // 移除悬浮窗口
            mWindowManager.removeView(mlayout);
        }

        mIsRunning = false;
        if (mMediaRecorder != null) {
            try {
                mMediaRecorder.stop();
            } catch (Exception e) {
                mMediaRecorder = null;
                mMediaRecorder = new MediaRecorder();
                Log.w(TAG, "stopRecord  exception e = " + e);
            } finally {
                mHandler.removeMessages(MSG_TYPE_COUNT_DOWN);
                ScreenUtil.stopRecord(tip);
                if (mRecordSeconds <= 2) {
                    FileUtil.deleteSDFile(mRecordFilePath);
                } else {
                    //通知系统图库更新
                    FileUtil.fileScanVideo(this, mRecordFilePath, mRecordWidth, mRecordHeight, mRecordSeconds);
                }
                mVirtualDisplay.release();
                mMediaProjection.stop();
                mRecordSeconds = 0;
                mMediaProjection = null;
            }
            mMediaRecorder.reset();
            mMediaRecorder.release();
        }
        return true;
    }

    public void pauseRecord() {
        if (mMediaRecorder != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                mMediaRecorder.pause();
            }
        }
    }

    public void resumeRecord() {
        if (mMediaRecorder != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                mMediaRecorder.resume();
            }
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void createVirtualDisplay() {
        mVirtualDisplay = mMediaProjection.createVirtualDisplay("MainScreen", mRecordWidth, mRecordHeight, mScreenDpi,
                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mMediaRecorder.getSurface(), null, null);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void setUpMediaRecorder() {
        mRecordFilePath = getSaveDirectory() + File.separator + System.currentTimeMillis() + ".mp4";
        if (mMediaRecorder == null) {
            mMediaRecorder = new MediaRecorder();
        }
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mMediaRecorder.setOutputFile(mRecordFilePath);
        mMediaRecorder.setVideoSize(mRecordWidth, mRecordHeight);
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        mMediaRecorder.setVideoEncodingBitRate((int) (mRecordWidth * mRecordHeight * 3.6));
        mMediaRecorder.setVideoFrameRate(20);

        try {
            mMediaRecorder.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void clearAll() {
        if (mMediaProjection != null) {
            mMediaProjection.stop();
            mMediaProjection = null;
        }
    }

    public String getRecordFilePath() {
        return mRecordFilePath;
    }

    public String getSaveDirectory() {

        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            String path = Environment.getExternalStorageDirectory().getAbsolutePath() + RecordScreenPath;
            FileUtil.createSDFile(path);
            return path;
        } else {
            return null;
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {

            case MSG_TYPE_COUNT_DOWN: {

                String str = null;
                boolean enough = FileUtil.getSDFreeMemory() / (1024 * 1024) < 4;
                if (enough) {
                    //空间不足,停止录屏
                    str = getString(R.string.record_space_tip);
                    stopRecord(str);
                    Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
                    break;
                }

                mRecordSeconds++;
                int minute = 0, second = 0;
                if (mRecordSeconds >= 60) {
                    minute = mRecordSeconds / 60;
                    second = mRecordSeconds % 60;
                } else {
                    second = mRecordSeconds;
                }
                ScreenUtil.onRecording("0" + minute + ":" + (second < 10 ? "0" + second : second + ""));

                if (mRecordSeconds < 3 * 60) {
                    mHandler.sendEmptyMessageDelayed(MSG_TYPE_COUNT_DOWN, 1000);
                } else if (mRecordSeconds == 3 * 60) {
                    str = getString(R.string.record_time_end_tip);
                    stopRecord(str);
                    Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
                }

                break;
            }
        }
        return true;
    }

    public class RecordBinder extends Binder {
        public ScreenRecordService getRecordService() {
            return ScreenRecordService.this;
        }
    }

    /**
     * 初始化windowManager
     */
    private void initWindow() {
        if (mWindowManager == null) {
            mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
        }
        wmParams = getParams(wmParams);//设置好悬浮窗的参数
        // 悬浮窗默认显示以左上角为起始坐标
        wmParams.gravity = Gravity.LEFT | Gravity.TOP;
        //悬浮窗的开始位置,因为设置的是从左上角开始,所以屏幕左上角是x=0;y=0
        wmParams.x = 0;
        wmParams.y = 0;
        //得到容器,通过这个inflater来获得悬浮窗控件
        if (inflater == null) {
            inflater = LayoutInflater.from(getApplication());
        }
        // 获取浮动窗口视图所在布局
        if (mlayout == null) {
            mlayout = (LinearLayout) inflater.inflate(R.layout.record_screen_time_float, null);
        }
        // 添加悬浮窗的视图
        mWindowManager.addView(mlayout, wmParams);
    }

    /**
     * 对windowManager进行设置
     *
     * @param wmParams
     * @return
     */
    public WindowManager.LayoutParams getParams(WindowManager.LayoutParams wmParams) {
        if (wmParams == null) {
            wmParams = new WindowManager.LayoutParams();
        }

        //设置window type 下面变量2002是在屏幕区域显示,2003则可以显示在状态栏之上
        //wmParams.type = LayoutParams.TYPE_PHONE;
        //wmParams.type = LayoutParams.TYPE_SYSTEM_ALERT;
        wmParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
        //设置图片格式,效果为背景透明
        wmParams.format = PixelFormat.RGBA_8888;
        //设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
        //wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;
        //设置可以显示在状态栏上
        wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR |
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;

        //设置悬浮窗口长宽数据
        wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;

        return wmParams;
    }

    /**
     * 找到悬浮窗的图标,并且设置事件
     * 设置悬浮窗的点击、滑动事件
     */
    private void initFloating() {
        recordTime = (TextView) mlayout.findViewById(R.id.record_time);
        recordHintButton = (ImageView) mlayout.findViewById(R.id.record_hint_button);
        setFlickerAnimation(recordHintButton);
        stopRecord = (LinearLayout) mlayout.findViewById(R.id.stop_record);
        mlayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "OnClickListener");
                ScreenUtil.stopScreenRecord(ScreenRecordService.this);
            }
        });
        if (mGestureDetector == null) {
            mGestureDetector = new GestureDetector(this, new MyOnGestureListener());
        }
        if(mFloatingListener == null){
            //设置监听器
            mFloatingListener = new FloatingListener();
        }
        mlayout.setOnTouchListener(mFloatingListener);
        stopRecord.setOnTouchListener(mFloatingListener);

    }

    private void setFlickerAnimation(ImageView iv_chat_head) {
        final Animation animation = new AlphaAnimation(1, 0); // Change alpha from fully visible to invisible
        animation.setDuration(500); // duration - half a second
        animation.setInterpolator(new LinearInterpolator()); // do not alter animation rate
        animation.setRepeatCount(Animation.INFINITE); // Repeat animation infinitely
        animation.setRepeatMode(Animation.REVERSE); //
        iv_chat_head.setAnimation(animation);
    }

    private ScreenUtil.RecordListener recordListener = new ScreenUtil.RecordListener() {
        @Override
        public void onStartRecord() {

        }

        @Override
        public void onPauseRecord() {

        }

        @Override
        public void onResumeRecord() {

        }

        @Override
        public void onStopRecord(String stopTip) {
            //Toast.makeText(ScreenRecordActivity.this,stopTip,Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onRecording(String timeTip) {
            if (recordTime != null) {
                recordTime.setText(timeTip);
            }
        }
    };

    //开始触控的坐标,移动时的坐标(相对于屏幕左上角的坐标)
    private int mTouchStartX, mTouchStartY, mTouchCurrentX, mTouchCurrentY;
    //开始时的坐标和结束时的坐标(相对于自身控件的坐标)
    private int mStartX, mStartY, mStopX, mStopY;
    private boolean isMove;//判断悬浮窗是否移动

    private class FloatingListener implements OnTouchListener {

        @Override
        public boolean onTouch(View arg0, MotionEvent event) {

            int action = event.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    isMove = false;
                    mTouchStartX = (int) event.getRawX();
                    mTouchStartY = (int) event.getRawY();
                    mStartX = (int) event.getX();
                    mStartY = (int) event.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    mTouchCurrentX = (int) event.getRawX();
                    mTouchCurrentY = (int) event.getRawY();
                    wmParams.x += mTouchCurrentX - mTouchStartX;
                    wmParams.y += mTouchCurrentY - mTouchStartY;
                    if (mlayout != null) {
                        mWindowManager.updateViewLayout(mlayout, wmParams);
                    }

                    mTouchStartX = mTouchCurrentX;
                    mTouchStartY = mTouchCurrentY;
                    break;
                case MotionEvent.ACTION_UP:
                    mStopX = (int) event.getX();
                    mStopY = (int) event.getY();
                    if (Math.abs(mStartX - mStopX) >= 1 || Math.abs(mStartY - mStopY) >= 1) {
                        isMove = true;
                    }
                    break;
            }
            return mGestureDetector.onTouchEvent(event);  //此处必须返回false,否则OnClickListener获取不到监听
        }

    }

    class MyOnGestureListener extends SimpleOnGestureListener {
        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            if (!isMove) {
                System.out.println("onclick");
            }
            return super.onSingleTapConfirmed(e);
        }
    }

}

2.レコード画面の活動カウントダウン

package com.android.systemui;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.content.Context;
import android.os.Build;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import com.android.systemui.ScreenRecordService;
import com.android.systemui.util.ScreenUtil;
import com.android.systemui.util.CommonUtil;

import android.view.KeyEvent;

import java.lang.reflect.Field;
import java.lang.reflect.Method;


public class ScreenRecordActivity extends Activity {

    private static final String TAG = "ScreenRecordActivity";
    private static final int START_COUNTING = 1;
    private static final int COUNT_NUMBER = 3;

    public static ScreenRecordActivity instance;

    private TextView mTvTime;

    private int REQUEST_CODE = 1;
    private MyHandler mHandler = new MyHandler();
    public boolean CancleCount = false;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        collapseStatusBar();
        setContentView(R.layout.screen_record_activity);
        CommonUtil.init(this.getApplication());
        mTvTime = findViewById(R.id.count_text);

        startScreenRecordService();
        instance = this;
        Log.d(TAG, "onCreate");
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        if(intent != null && intent.getBooleanExtra("srartRecordScreen",false)){
            Log.d(TAG, "onNewIntent startCountTime");
            startCountTime();
        }
    }

    public void startCountTime(){
        Log.d(TAG,"startCountTime");
        Message msg = mHandler.obtainMessage();
        msg.what = START_COUNTING;
        msg.obj = COUNT_NUMBER;
        mHandler.sendMessageDelayed(msg, 10);
    }

    /**
     * collapse status bar
     *
     * @param context
     */
    public void collapseStatusBar() {
        try {
            Object statusBarManager = this.getSystemService("statusbar");
            Method collapse;
            if (Build.VERSION.SDK_INT <= 16) {
                collapse = statusBarManager.getClass().getMethod("collapse");
            } else {
                collapse = statusBarManager.getClass().getMethod("collapsePanels");
            }
            collapse.invoke(statusBarManager);
        } catch (Exception localException) {
            localException.printStackTrace();
        }
    }

    private ServiceConnection mServiceConnection;

    /**
     * satrt record Service
     */
    private void startScreenRecordService() {
        mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                ScreenRecordService.RecordBinder recordBinder = (ScreenRecordService.RecordBinder) service;
                ScreenRecordService screenRecordService = recordBinder.getRecordService();
                ScreenUtil.setScreenService(screenRecordService);
                startCountTime();
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };

        Intent intent = new Intent(this, ScreenRecordService.class);
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.d(TAG, "onActivityResult");
        if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
            try {
                moveTaskToBack(true);
                overridePendingTransition(R.anim.record_screen_anim_in, R.anim.record_screen_anim_out);
                ScreenUtil.setUpData(resultCode, data);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            Toast.makeText(ScreenRecordActivity.this, ScreenRecordActivity.this.getResources().getString(R.string.refuse_record_screen), Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            CancleCount = true;
            moveTaskToBack(true);
            return true;
        }else if(keyCode == KeyEvent.KEYCODE_HOME){
            CancleCount = true;
            moveTaskToBack(true);
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
        ScreenUtil.stopScreenRecord(this);
        if (mServiceConnection != null) {
            unbindService(mServiceConnection);
            mServiceConnection = null;
        }
        instance = null;
    }

    private class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
                case START_COUNTING:
                    if(CancleCount){
                        CancleCount = false;
                        return;
                    }
                    int count = (int) msg.obj;
                    mTvTime.setText(count + "");
                    if (count > 0) {
                        Message msg1 = obtainMessage();
                        msg1.what = START_COUNTING;
                        msg1.obj = count - 1;
                        sendMessageDelayed(msg1, 1000);
                    } else {
                        ScreenUtil.startScreenRecord(ScreenRecordActivity.this, REQUEST_CODE);
                    }
                    break;
                default:
                    break;
            }
        }
    };
}

レイアウトファイルscreen_record_activity.xml 3.レコード画面の活動

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:background="#01000000"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/count_text"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:layout_centerInParent="true"
        android:background="@drawable/count_time_shape"
        android:gravity="center"
        android:textColor="#6495ED"
        android:textSize="140sp" />

</RelativeLayout>

4.drawableディレクトリcount_time_shape.xml

<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#80708090" />
    <stroke
        android:width="4dp"
        android:color="#80708090" />
    <padding
        android:top="1dp"
        android:left="2dp"
        android:right="2dp"
        android:bottom="1dp"/>
    <size
        android:width="20dp"
        android:height="20dp" />
</shape>

サスペンションレイアウトウィンドウがrecord_screen_time_float.xmlを以下


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
 
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/corners_bg"
        android:paddingBottom="3dp"
        android:paddingTop="3dp"
        android:paddingLeft="15dp"
        android:paddingRight="8dp"
        android:layout_gravity="center"
        android:gravity="center"
        android:orientation="horizontal">
 
        <TextView
            android:id="@+id/record_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00:00"
            android:textColor="#ffffff"
            android:textSize="10dp" />
        <View
            android:layout_width="2dp"
            android:layout_height="match_parent"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:textColor="#ffffff" />
        <LinearLayout
            android:id="@+id/stop_record"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center"
            android:orientation="horizontal">
            <ImageView
                android:id="@+id/record_hint_button"
                android:layout_width="10dp"
                android:layout_height="10dp"
                android:layout_marginRight="5dp"
                android:background="#FF4040" />
 
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="结束"
                android:textColor="#ffffff"
                android:textSize="10dp" />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

このすべてのコードが提供されています。

 

公開された92元の記事 ウォン称賛27 ビュー90000 +

おすすめ

転載: blog.csdn.net/zhuxingchong/article/details/86605775