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默認(rèn)是橫屏的,所以我們要使用豎屏錄制要旋轉(zhuǎn)90度
int degree = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 0 : 90; mCamera.setDisplayOrientation(degree);
camera預(yù)覽
我們要選擇一個(gè)與我們要顯示的SurfaceView大小比例最接近的一個(gè)camera預(yù)覽大小,這里要特別注意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() {
// 創(chuàng)建MediaPlayer對象
mCamera.unlock();
mRecorder = new MediaRecorder();
mRecorder.reset();
mRecorder.setCamera(mCamera);
// 設(shè)置從麥克風(fēng)采集聲音(或來自錄像機(jī)的聲音AudioSource.CAMCORDER)
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 設(shè)置從攝像頭采集圖像
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;
}
這里也要設(shè)置視頻的旋轉(zhuǎn)參數(shù)
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.打開相機(jī)
*/
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.設(shè)置預(yù)覽功能
*/
@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", "獲取預(yù)覽幀...");
Log.d("TAG", "預(yù)覽幀大小:" + String.valueOf(data.length));
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean prepareMediaRecorder() {
// 創(chuàng)建MediaPlayer對象
mCamera.unlock();
mRecorder = new MediaRecorder();
mRecorder.reset();
mRecorder.setCamera(mCamera);
// 設(shè)置從麥克風(fēng)采集聲音(或來自錄像機(jī)的聲音AudioSource.CAMCORDER)
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 設(shè)置從攝像頭采集圖像
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();
}
/**
* 釋放相機(jī)資源
*/
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();
}
/**
* 獲取最佳預(yù)覽大小
*
* @param parameters 相機(jī)參數(shù)
* @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">
<!-- 顯示視頻預(yù)覽的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 {
//內(nèi)部存儲不可用
path = context.getCacheDir().getPath();
}
return path;
}
}
以上就是Android 錄制音視頻的詳細(xì)內(nèi)容,更多關(guān)于Android 錄制音視頻的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android開發(fā)Kotlin語言協(xié)程中的并發(fā)問題和互斥鎖
Android開發(fā)Kotlin語言提供了多種機(jī)制來處理并發(fā)和同步,其中包括高層次和低層次的工具,對于常規(guī)的并發(fā)任務(wù),可以利用 Kotlin 協(xié)程提供的結(jié)構(gòu)化并發(fā)方式,而對于需要更低層次的鎖定機(jī)制,可以使用Mutex(互斥鎖)來實(shí)現(xiàn)對共享資源的線程安全訪問2024-06-06
Android GestureDetector實(shí)現(xiàn)手勢滑動效果
這篇文章主要為大家詳細(xì)介紹了Android GestureDetector實(shí)現(xiàn)手勢滑動效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
Android使用元數(shù)據(jù)實(shí)現(xiàn)配置信息的傳遞方法詳細(xì)介紹
這篇文章主要介紹了Android使用元數(shù)據(jù)實(shí)現(xiàn)配置信息的傳遞方法,也就是實(shí)現(xiàn)配置快捷菜單功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-09-09
Android 如何攔截用戶頻繁操作(點(diǎn)擊事件)
本文主要介紹了Android 如何攔截用戶頻繁操作,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
Android使用Room數(shù)據(jù)庫解決本地持久化的操作
Room 是一個(gè)持久性庫,屬于 Android Jetpack 的一部分,Room 是 SQLite 數(shù)據(jù)庫之上的一個(gè)抽象層,Room 并不直接使用 SQLite,而是負(fù)責(zé)簡化數(shù)據(jù)庫設(shè)置和配置以及與數(shù)據(jù)庫交互方面的瑣碎工作,本文介紹了Android使用Room數(shù)據(jù)庫解決本地持久化的操作,需要的朋友可以參考下2024-09-09
android實(shí)現(xiàn)App活動定時(shí)自動跳轉(zhuǎn)效果
本篇文章主要介紹了android實(shí)現(xiàn)App活動定時(shí)自動跳轉(zhuǎn)效果,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-02-02
Android開發(fā)實(shí)現(xiàn)按鈕點(diǎn)擊切換背景并修改文字顏色的方法
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)按鈕點(diǎn)擊切換背景并修改文字顏色的方法,涉及Android界面布局與相關(guān)屬性設(shè)置技巧,需要的朋友可以參考下2018-01-01
使用adb命令向Android模擬器中導(dǎo)入通訊錄聯(lián)系人的方法
這篇文章主要介紹了使用adb命令向Android模擬器中導(dǎo)入通訊錄聯(lián)系人的方法,實(shí)例分析了導(dǎo)入通訊錄存儲文件的技巧,需要的朋友可以參考下2015-01-01
Android Studio報(bào)錯(cuò)unable to access android sdk add-on list解決方案
這篇文章主要介紹了Android Studio報(bào)錯(cuò)unable to access android sdk add-on list解決方案,本文通過多種方式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03
詳解Flutter Image組件如何處理圖片加載過程中的錯(cuò)誤
在Flutter中,Image組件可以通過監(jiān)聽加載過程中的錯(cuò)誤來處理圖片加載過程中的錯(cuò)誤,本文小編將給大家詳細(xì)介紹了Flutter Image組件是如何處理圖片加載過程中的錯(cuò)誤,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參下2023-10-10

