Android 录制音视频的完整代码

 更新时间:2021年06月23日 10:10:58   作者:原总破局  
Android中,如果要录制音频的话有两个选择,一个是MediaRecorder,另一个就是AudioRecord,前者使用简单,后者就相对复杂点,本文通过代码给大家介绍Android 录制音视频的相关知识,一起看看吧

打开camera

private void openCamera(int position) {
 
if (mCamera == null) {
 
mCamera = Camera.open(position);
 
int degree = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 0 : 90;
 
mCamera.setDisplayOrientation(degree);
 
}
 
}

camera默认是横屏的,所以我们要使用竖屏录制要旋转90度

int degree = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 0 : 90;
 
mCamera.setDisplayOrientation(degree);

camera预览

我们要选择一个与我们要显示的SurfaceView大小比例最接近的一个camera预览大小,这里要特别注意camera支持的宽高都是宽大于高。

所以就有了下面这段选择代码

private Size getBestCameraResolution(Camera.Parameters parameters, Size screenResolution) {
 
float tmp = 0f;
 
float mindiff = 100f;
 
Log.e("yuanVideo", "screen width=" + screenResolution.getWidth() + " height=" + screenResolution.getHeight());
 
float width_d_height;
 
if (screenResolution.getWidth() > screenResolution.getHeight()) {
 
width_d_height = (float) screenResolution.getWidth() / (float) screenResolution.getHeight();
 
} else {
 
width_d_height = (float) screenResolution.getHeight() / (float) screenResolution.getWidth();
 
}
 
Log.e("yuanVideo", "width_d_height=" + width_d_height);
 
Camera.Size best = null;
 
List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
 
for (Camera.Size s : supportedPreviewSizes) {
 
tmp = Math.abs(((float) s.width / (float) s.height) - width_d_height);
 
Log.e("yuanVideo", "support width=" + s.width + " height=" + s.height + " ratio=" + tmp);
 
if (tmp < mindiff) {
 
mindiff = tmp;
 
best = s;
 
}
 
}
 
Log.e("yuanVideo", "best width=" + best.width + " height=" + best.height);
 
return new Size(best.width, best.height);
 
}

初始化MediaRecorder

private boolean prepareMediaRecorder() {
 
// 创建MediaPlayer对象
 
mCamera.unlock();
 
mRecorder = new MediaRecorder();
 
mRecorder.reset();
 
mRecorder.setCamera(mCamera);
 
// 设置从麦克风采集声音(或来自录像机的声音AudioSource.CAMCORDER)
 
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
 
// 设置从摄像头采集图像
 
mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
 
Log.e("yuanProfile", "QUALITY_LOW=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_LOW));
 
Log.e("yuanProfile", "QUALITY_HIGH=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_HIGH));
 
Log.e("yuanProfile", "QUALITY_QCIF=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QCIF));
 
Log.e("yuanProfile", "QUALITY_480P=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P));
 
Log.e("yuanProfile", "QUALITY_720P=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P));
 
Log.e("yuanProfile", "QUALITY_1080P=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P));
 
Log.e("yuanProfile", "QUALITY_QVGA=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QVGA));
 
Log.e("yuanProfile", "QUALITY_2160P=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2160P));
 
Log.e("yuanProfile", "QUALITY_VGA=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_VGA));
 
Log.e("yuanProfile", "QUALITY_4KDCI=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_4KDCI));
 
Log.e("yuanProfile", "QUALITY_QHD=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QHD));
 
Log.e("yuanProfile", "QUALITY_2K=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2K));
 
if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {
 
mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_720P));
 
} else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P)) {
 
mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_1080P));
 
} else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) {
 
mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_480P));
 
} else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_LOW)) {
 
mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_LOW));
 
} else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_HIGH)) {
 
mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
 
} else {
 
return false;
 
}
 
// mTempList.add(mCurrentTempRecordData);
 
mRecorder.setOutputFile(mCurPath);
 
mRecorder.setPreviewDisplay(activtityVideoRecordBinding.sView.getHolder().getSurface()); // ①
 
int degree;
 
if(getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE){
 
int degree ;
 
if (cameraPosition == Camera.CameraInfo.CAMERA_FACING_FRONT) {
 
degree = 270;
 
} else {
 
degree = 90;
 
}
 
mRecorder.setOrientationHint(degree);
 
}
 
try {
 
mRecorder.prepare();
 
} catch (Exception e) {
 
e.printStackTrace();
 
return false;
 
}
 
return true;
 
}

这里也要设置视频的旋转参数

if(getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE){
 
int degree ;
 
if (cameraPosition == Camera.CameraInfo.CAMERA_FACING_FRONT) {
 
degree = 270;
 
} else {
 
degree = 90;
 
}
 
mRecorder.setOrientationHint(degree);
 
}

下面是完整的代码

package com.yuanxuzhen.ffmpeg;
 
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
 
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
 
import com.yuanxuzhen.ffmpeg.databinding.ActivtityVideoRecordBinding;
 
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class VideoRecordActivity extends Activity {
    ActivtityVideoRecordBinding activtityVideoRecordBinding;
    MediaRecorder mRecorder;
    private boolean isRecording = false;
 
    private int cameraPosition = Camera.CameraInfo.CAMERA_FACING_FRONT;//0代表前置摄像头,1代表后置摄像头
    private Camera mCamera;
    private Camera.Parameters mParameters;
    private String mCurPath = null;
    private VideoTempRecordData mCurrentTempRecordData = null;
 
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        getWindow().setFormat(PixelFormat.TRANSLUCENT);
        mCurPath = DirUtil.getCacheDir(this) + File.separator + "out.mp4";
        activtityVideoRecordBinding = ActivtityVideoRecordBinding.inflate(getLayoutInflater());
        setContentView(activtityVideoRecordBinding.getRoot());
        activtityVideoRecordBinding.sView.getHolder().setKeepScreenOn(true);
        activtityVideoRecordBinding.sView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void surfaceCreated(@NonNull SurfaceHolder holder) {
                openPreView();
            }
 
 
            @Override
            public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
                Log.e("yuanVideo", "surfaceChanged width=" + width + " height=" + height);
            }
 
            @Override
            public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
 
            }
        });
 
 
        activtityVideoRecordBinding.recordOrStop.setText("开始");
        activtityVideoRecordBinding.recordOrStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isRecording) {
                    Log.d("TAG", "停止录像");
                    stopRecord();
                } else {
                    startRecord();
                }
            }
        });
        activtityVideoRecordBinding.change.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onClick(View v) {
                if(isRecording){
                    return;
                }
                releaseCamera();
                cameraPosition = cameraPosition == Camera.CameraInfo.CAMERA_FACING_FRONT ? Camera.CameraInfo.CAMERA_FACING_BACK : Camera.CameraInfo.CAMERA_FACING_FRONT;
                openCamera(cameraPosition);
                openPreView();
            }
        });
    }
 
 
    /**
     * 1.打开相机
     */
    private void openCamera(int position) {
        if (mCamera == null) {
            mCamera = Camera.open(position);
            int degree = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 0 : 90;
            mCamera.setDisplayOrientation(degree);
        }
    }
 
 
    /**
     * initCameraAndSurfaceViewHolder初始化hoder后
     * 2.设置预览功能
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void openPreView() {
        try {
            if (mCamera != null) {
                mParameters = mCamera.getParameters();
                mCamera.setPreviewDisplay(activtityVideoRecordBinding.sView.getHolder());
                Size screenPoint = getScreenMetrics(VideoRecordActivity.this);
                Size bestPreviewSize = getBestCameraResolution(mCamera.getParameters(), screenPoint);
                mParameters.setPreviewSize(bestPreviewSize.getWidth(), bestPreviewSize.getHeight());
                mCamera.setParameters(mParameters);
                mCamera.startPreview();
                mCamera.autoFocus(new Camera.AutoFocusCallback() {
                    @Override
                    public void onAutoFocus(boolean success, Camera camera) {
                        Log.e("yuanVideo", "autoFocus success=" + success);
 
                    }
                });
 
                mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                    @Override
                    public void onPreviewFrame(byte[] data, Camera camera) {
                        Log.i("TAG", "获取预览帧...");
                        Log.d("TAG", "预览帧大小:" + String.valueOf(data.length));
                    }
                });
 
 
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
 
    private boolean prepareMediaRecorder() {
        // 创建MediaPlayer对象
        mCamera.unlock();
        mRecorder = new MediaRecorder();
        mRecorder.reset();
        mRecorder.setCamera(mCamera);
 
        // 设置从麦克风采集声音(或来自录像机的声音AudioSource.CAMCORDER)
        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        // 设置从摄像头采集图像
        mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        Log.e("yuanProfile", "QUALITY_LOW=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_LOW));
        Log.e("yuanProfile", "QUALITY_HIGH=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_HIGH));
        Log.e("yuanProfile", "QUALITY_QCIF=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QCIF));
        Log.e("yuanProfile", "QUALITY_480P=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P));
        Log.e("yuanProfile", "QUALITY_720P=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P));
        Log.e("yuanProfile", "QUALITY_1080P=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P));
        Log.e("yuanProfile", "QUALITY_QVGA=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QVGA));
        Log.e("yuanProfile", "QUALITY_2160P=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2160P));
        Log.e("yuanProfile", "QUALITY_VGA=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_VGA));
        Log.e("yuanProfile", "QUALITY_4KDCI=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_4KDCI));
        Log.e("yuanProfile", "QUALITY_QHD=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QHD));
        Log.e("yuanProfile", "QUALITY_2K=" + CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2K));
 
        if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {
            mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_720P));
        } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P)) {
            mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_1080P));
        } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) {
            mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_480P));
        } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_LOW)) {
            mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_LOW));
        } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_HIGH)) {
            mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
        } else {
            return false;
        }
//        mTempList.add(mCurrentTempRecordData);
        mRecorder.setOutputFile(mCurPath);
        mRecorder.setPreviewDisplay(activtityVideoRecordBinding.sView.getHolder().getSurface()); // ①
 
        if(getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE){
            int degree ;
            if (cameraPosition == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                degree = 270;
            } else {
                degree = 90;
            }
            mRecorder.setOrientationHint(degree);
        }
 
        try {
            mRecorder.prepare();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
 
    }
 
    private void startRecord() {
        if (prepareMediaRecorder()) {
            mRecorder.start();
            isRecording = true;
            activtityVideoRecordBinding.recordOrStop.setText("停止");
        } else {
            releaseMediaRecorder();
            isRecording = false;
            activtityVideoRecordBinding.recordOrStop.setText("开始");
        }
 
    }
 
    private void stopRecord() {
        if (mRecorder == null) {
            return;
        }
        mRecorder.stop();
        releaseMediaRecorder();
        isRecording = false;
        activtityVideoRecordBinding.recordOrStop.setText("开始");
    }
 
    @Nullable
    @Override
    public CharSequence onCreateDescription() {
        return super.onCreateDescription();
    }
 
    @Override
    protected void onDestroy() {
        releaseCamera();
        releaseMediaRecorder();
        super.onDestroy();
    }
 
    /**
     * 释放相机资源
     */
    private void releaseCamera() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }
 
    private void releaseMediaRecorder() {
        if (mRecorder != null) {
            mRecorder.reset();
            mRecorder.release();
            mRecorder = null;
            mCamera.lock();
        }
    }
 
    @Override
    protected void onResume() {
        super.onResume();
        openCamera(cameraPosition);
    }
 
    @Override
    protected void onPause() {
        super.onPause();
        releaseMediaRecorder();
        releaseCamera();
    }
 
    /**
     * 获取最佳预览大小
     *
     * @param parameters       相机参数
     * @param screenResolution 屏幕宽高
     * @return
     */
 
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private Size getBestCameraResolution(Camera.Parameters parameters, Size screenResolution) {
        float tmp = 0f;
 
        float mindiff = 100f;
        Log.e("yuanVideo", "screen width=" + screenResolution.getWidth() + " height=" + screenResolution.getHeight());
 
        float width_d_height;
        if (screenResolution.getWidth() > screenResolution.getHeight()) {
            width_d_height = (float) screenResolution.getWidth() / (float) screenResolution.getHeight();
        } else {
            width_d_height = (float) screenResolution.getHeight() / (float) screenResolution.getWidth();
        }
        Log.e("yuanVideo", "width_d_height=" + width_d_height);
 
        Camera.Size best = null;
 
        List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
        for (Camera.Size s : supportedPreviewSizes) {
            tmp = Math.abs(((float) s.width / (float) s.height) - width_d_height);
            Log.e("yuanVideo", "support width=" + s.width + " height=" + s.height + " ratio=" + tmp);
 
            if (tmp < mindiff) {
                mindiff = tmp;
 
                best = s;
            }
 
        }
        Log.e("yuanVideo", "best width=" + best.width + " height=" + best.height);
 
        return new Size(best.width, best.height);
    }
 
 
    /**
     * 获取屏幕宽度和高度,单位为px
     *
     * @param context
     * @return
     */
 
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public static Size getScreenMetrics(Context context) {
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
 
        int w_screen = dm.widthPixels;
 
        int h_screen = dm.heightPixels;
 
        return new Size(w_screen, h_screen);
    }
 
 
}

布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- 显示视频预览的SurfaceView -->
    <com.yuanxuzhen.ffmpeg.ResizeAbleSurfaceView
        android:id="@+id/sView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"/>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        >
        <TextView
            android:id="@+id/tv_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="0秒"
            android:layout_centerInParent="true"
            android:textColor="@color/white"
            />
        <Button
            android:id="@+id/change"
            android:layout_width="wrap_content"
            android:layout_height="66dp"
            android:text="切换摄像头"
            android:layout_alignParentEnd="true"
            />
    </RelativeLayout>
 
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true">
        <Button
            android:id="@+id/record_or_stop"
            android:layout_width="66dp"
            android:layout_height="66dp"
            android:text="录制"
            />
        <Button
            android:id="@+id/save"
            android:layout_width="66dp"
            android:layout_height="66dp"
            android:text="保存"
            />
    </LinearLayout>
</RelativeLayout>
package com.yuanxuzhen.ffmpeg;
 
import android.content.Context;
import android.util.AttributeSet;
import android.view.SurfaceView;
 
public class ResizeAbleSurfaceView extends SurfaceView {
 
    private int mWidth = -1;
    private int mHeight = -1;
 
    public ResizeAbleSurfaceView(Context context) {
        super(context);
    }
 
    public ResizeAbleSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    public ResizeAbleSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
 
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (-1 == mWidth || -1 == mHeight) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
        else {
            setMeasuredDimension(mWidth, mHeight);
        }
    }
 
    public void resize(int width, int height) {
        mWidth = width;
        mHeight = height;
        getHolder().setFixedSize(width, height);
        requestLayout();
        invalidate();
    }
 
}
package com.yuanxuzhen.ffmpeg;
 
import android.content.Context;
import android.os.Environment;
 
 
import java.io.File;
 
public class DirUtil {
    public static final String WEBVIEW_CACHE = ".webviewCache";
    public static final String IMAGE_PATH = "image";
    public static final String DOWNLOAD_PATH = "download";
    public static final String VIDEO_PATH = ".video";
    public static final String NET_PATH = ".net";
 
 
    //image
    public static String getImageDir(Context context) {
        return getCacheDir(context) + File.separator + IMAGE_PATH;
    }
 
    //webview
    public static String getWebviewCache(Context context) {
        return getCacheDir(context) + File.separator + WEBVIEW_CACHE;
    }
 
    //download
    public static String getDownloadDir(Context context) {
        return getCacheDir(context) + File.separator + DOWNLOAD_PATH;
    }
 
    //video
    public static String getVideoPath(Context context) {
        return getCacheDir(context) + File.separator + VIDEO_PATH;
    }
 
    //net
    public static String getNetPath(Context context) {
        return getCacheDir(context) + File.separator + NET_PATH;
    }
 
 
    public static String getCacheDir(Context context) {
 
        if (context == null) {
            return "";
        }
        String path = null;
        if (context.getExternalCacheDir() != null
                && (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable())) {
            //外部存储可用
            path = context.getExternalCacheDir().getPath();
        } else {
            //内部存储不可用
            path = context.getCacheDir().getPath();
        }
        return path;
    }
 
}

以上就是Android 录制音视频的详细内容,更多关于Android 录制音视频的资料请关注脚本之家其它相关文章!

相关文章

  • Android开发Kotlin语言协程中的并发问题和互斥锁

    Android开发Kotlin语言协程中的并发问题和互斥锁

    Android开发Kotlin语言提供了多种机制来处理并发和同步,其中包括高层次和低层次的工具,对于常规的并发任务,可以利用 Kotlin 协程提供的结构化并发方式,而对于需要更低层次的锁定机制,可以使用Mutex(互斥锁)来实现对共享资源的线程安全访问
    2024-06-06
  • Android GestureDetector实现手势滑动效果

    Android GestureDetector实现手势滑动效果

    这篇文章主要为大家详细介绍了Android GestureDetector实现手势滑动效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • Android使用元数据实现配置信息的传递方法详细介绍

    Android使用元数据实现配置信息的传递方法详细介绍

    这篇文章主要介绍了Android使用元数据实现配置信息的传递方法,也就是实现配置快捷菜单功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-09-09
  • Android 如何拦截用户频繁操作(点击事件)

    Android 如何拦截用户频繁操作(点击事件)

    本文主要介绍了Android 如何拦截用户频繁操作,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Android使用Room数据库解决本地持久化的操作

    Android使用Room数据库解决本地持久化的操作

    Room 是一个持久性库,属于 Android Jetpack 的一部分,Room 是 SQLite 数据库之上的一个抽象层,Room 并不直接使用 SQLite,而是负责简化数据库设置和配置以及与数据库交互方面的琐碎工作,本文介绍了Android使用Room数据库解决本地持久化的操作,需要的朋友可以参考下
    2024-09-09
  • android实现App活动定时自动跳转效果

    android实现App活动定时自动跳转效果

    本篇文章主要介绍了android实现App活动定时自动跳转效果,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • Android开发实现按钮点击切换背景并修改文字颜色的方法

    Android开发实现按钮点击切换背景并修改文字颜色的方法

    这篇文章主要介绍了Android开发实现按钮点击切换背景并修改文字颜色的方法,涉及Android界面布局与相关属性设置技巧,需要的朋友可以参考下
    2018-01-01
  • 使用adb命令向Android模拟器中导入通讯录联系人的方法

    使用adb命令向Android模拟器中导入通讯录联系人的方法

    这篇文章主要介绍了使用adb命令向Android模拟器中导入通讯录联系人的方法,实例分析了导入通讯录存储文件的技巧,需要的朋友可以参考下
    2015-01-01
  • Android Studio报错unable to access android sdk add-on list解决方案

    Android Studio报错unable to access android sdk add-on list解决方案

    这篇文章主要介绍了Android Studio报错unable to access android sdk add-on list解决方案,本文通过多种方式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • 详解Flutter Image组件如何处理图片加载过程中的错误

    详解Flutter Image组件如何处理图片加载过程中的错误

    在Flutter中,Image组件可以通过监听加载过程中的错误来处理图片加载过程中的错误,本文小编将给大家详细介绍了Flutter Image组件是如何处理图片加载过程中的错误,文中有详细的代码示例供大家参考,需要的朋友可以参下
    2023-10-10

最新评论