运行有问题或需要源码请点赞关注收藏后评论区留言~~~

一、使用录音机录制音频

手机有自带的系统相机,也有自带的系统录音机,只要在调用startActivityForResult之前指定该动作,就会自动跳转到系统的录音机界面 效果如下

当然这里最好连接真机测试 模拟机好像没有录音机 总之调试有点麻烦

代码如下

Java类

package com.example.chapter13;import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;import com.example.chapter13.util.DateUtil;
import com.example.chapter13.util.FileUtil;public class AudioRecordActivity extends AppCompatActivity implements View.OnClickListener {private final static String TAG = "AudioRecordActivity";private int RECORDER_CODE = 1; // 录制操作的请求码private TextView tv_audio;private ImageView iv_audio; // 该图标充当播放按钮private Uri mAudioUri; // 音频文件的uri路径@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_audio_record);tv_audio = findViewById(R.id.tv_audio);iv_audio = findViewById(R.id.iv_audio);findViewById(R.id.btn_recorder).setOnClickListener(this);findViewById(R.id.iv_audio).setOnClickListener(this);}@Overridepublic void onClick(View v) {if (v.getId() == R.id.btn_recorder) {// 下面打开系统自带的录音机Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);registerForActivityResult(intent, RECORDER_CODE); // 跳到录音机页面} else if (v.getId() == R.id.iv_audio) {// 下面打开系统自带的收音机Intent intent = new Intent(Intent.ACTION_VIEW);intent.setDataAndType(mAudioUri, "audio/*"); // 类型为音频startActivity(intent); // 跳到收音机页面}}private void registerForActivityResult(Intent intent, int recorder_code) {}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent intent) {super.onActivityResult(requestCode, resultCode, intent);if (resultCode==RESULT_OK && requestCode==RECORDER_CODE){mAudioUri = intent.getData(); // 获得录制好的音频uriString filePath = String.format("%s/%s.mp3",getExternalFilesDir(Environment.DIRECTORY_MUSIC), "audio_"+ DateUtil.getNowDateTime());FileUtil.saveFileFromUri(this, mAudioUri, filePath); // 保存为临时文件tv_audio.setText("录制完成的音频地址为:"+mAudioUri.toString());iv_audio.setVisibility(View.VISIBLE);}}}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Buttonandroid:id="@+id/btn_recorder"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="打开录音机"android:textColor="@color/black"android:textSize="17sp" /><TextViewandroid:id="@+id/tv_audio"android:layout_width="match_parent"android:layout_height="wrap_content"android:paddingLeft="5dp"android:textColor="@color/black"android:textSize="17sp" /><ImageViewandroid:id="@+id/iv_audio"android:layout_width="match_parent"android:layout_height="50dp"android:scaleType="fitCenter"android:src="@drawable/play_audio"android:visibility="gone" /></LinearLayout>

二、利用MediaPlayer播放音频

尽管让App跳到收音机界面就能播放音频,但是通常App都不希望用户离开自身页面,何况播音本来就是一个小功能,完全可以一边播放音频一边操作界面,若想在App内部自己播音,便用到了媒体播放器MediaPlayer,并且要找到音频文件的路径。

MediaPlayer常用方法如下

1:reset 重置播放器

2:prepare 准备播放

3:start 开始播放

4:pause 暂停播放

5:stop 停止播放

6:create 创建uri指定的播放器

7:setDataSource  设置播放器数据来源的文件路径

应用场景主要有以下两个1:播放指定音频文件

2:退出页面时准备释放媒体资源

代码如下

Java类

package com.example.chapter13;import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;import android.database.Cursor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;import com.example.chapter13.adapter.AudioRecyclerAdapter;
import com.example.chapter13.bean.MediaInfo;
import com.example.chapter13.util.FileUtil;
import com.example.chapter13.widget.RecyclerExtras;import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;public class AudioPlayActivity extends AppCompatActivity implements RecyclerExtras.OnItemClickListener {private final static String TAG = "AudioPlayActivity";private RecyclerView rv_audio; // 音频列表的循环视图private List<MediaInfo> mAudioList = new ArrayList<MediaInfo>(); // 音频列表private Uri mAudioUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; // 音频库的Uriprivate String[] mAudioColumn = new String[]{ // 媒体库的字段名称数组MediaStore.Audio.Media._ID, // 编号MediaStore.Audio.Media.TITLE, // 标题MediaStore.Audio.Media.DURATION, // 播放时长MediaStore.Audio.Media.SIZE, // 文件大小MediaStore.Audio.Media.DATA}; // 文件路径private AudioRecyclerAdapter mAdapter; // 音频列表的适配器private MediaPlayer mMediaPlayer = new MediaPlayer(); // 媒体播放器private Timer mTimer = new Timer(); // 计时器private int mLastPosition = -1; // 上次播放的音频序号@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_audio_play);rv_audio = findViewById(R.id.rv_audio);loadAudioList(); // 加载音频列表showAudioList(); // 显示音频列表}// 加载音频列表private void loadAudioList() {mAudioList.clear(); // 清空音频列表// 通过内容解析器查询音频库,并返回结果集的游标。记录结果按照修改时间降序返回Cursor cursor = getContentResolver().query(mAudioUri, mAudioColumn,null, null, "date_modified desc");if (cursor != null) {// 下面遍历结果集,并逐个添加到音频列表。简单起见只挑选前十个音频for (int i=0; i<10 && cursor.moveToNext(); i++) {MediaInfo audio = new MediaInfo(); // 创建一个音频信息对象audio.setId(cursor.getLong(0)); // 设置音频编号audio.setTitle(cursor.getString(1)); // 设置音频标题audio.setDuration(cursor.getInt(2)); // 设置音频时长audio.setSize(cursor.getLong(3)); // 设置音频大小audio.setPath(cursor.getString(4)); // 设置音频路径Log.d(TAG, audio.getTitle() + " " + audio.getDuration() + " " + audio.getSize() + " " + audio.getPath());if (!FileUtil.checkFileUri(this, audio.getPath())) {i--;continue;}mAudioList.add(audio); // 添加至音频列表}cursor.close(); // 关闭数据库游标}}// 显示音频列表private void showAudioList() {// 创建一个水平方向的线性布局管理器LinearLayoutManager manager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);rv_audio.setLayoutManager(manager); // 设置循环视图的布局管理器mAdapter = new AudioRecyclerAdapter(this, mAudioList); // 创建音频列表的线性适配器mAdapter.setOnItemClickListener(this); // 设置线性列表的点击监听器rv_audio.setAdapter(mAdapter); // 设置循环视图的列表适配器}@Overrideprotected void onDestroy() {super.onDestroy();mTimer.cancel(); // 取消计时器if (mMediaPlayer.isPlaying()) { // 是否正在播放mMediaPlayer.stop(); // 结束播放}mMediaPlayer.release(); // 释放媒体播放器}@Overridepublic void onItemClick(View view, final int position) {if (mLastPosition!=-1 && mLastPosition!=position) {MediaInfo last_audio = mAudioList.get(mLastPosition);last_audio.setProgress(-1); // 当前进度设为-1表示没在播放mAudioList.set(mLastPosition, last_audio);mAdapter.notifyItemChanged(mLastPosition); // 刷新此处的列表项}mLastPosition = position;final MediaInfo audio = mAudioList.get(position);Log.d(TAG, "onItemClick position="+position+",audio.getPath()="+audio.getPath());mTimer.cancel(); // 取消计时器mMediaPlayer.reset(); // 重置媒体播放器// mMediaPlayer.setVolume(0.5f, 0.5f); // 设置音量,可选mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流的类型为音乐try {mMediaPlayer.setDataSource(audio.getPath()); // 设置媒体数据的文件路径mMediaPlayer.prepare(); // 媒体播放器准备就绪mMediaPlayer.start(); // 媒体播放器开始播放} catch (Exception e) {e.printStackTrace();}mTimer = new Timer(); // 创建一个计时器mTimer.schedule(new TimerTask() {@Overridepublic void run() {audio.setProgress(mMediaPlayer.getCurrentPosition()); // 设置进度条的当前进度mAudioList.set(position, audio);// 界面刷新操作需要在主线程执行,故而向处理器发送消息,由处理器在主线程更新界面mHandler.sendEmptyMessage(position);Log.d(TAG, "CurrentPosition="+mMediaPlayer.getCurrentPosition()+",position="+position);}}, 0, 1000); // 计时器每隔一秒就更新进度条上的播放进度}private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);mAdapter.notifyItemChanged(msg.what); // 刷新此处的列表项}};}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="5dp"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="点击音频列表开始播放"android:textColor="@color/black"android:textSize="17sp" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="40dp"android:layout_margin="2dp"android:orientation="horizontal"><TextViewandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="3"android:gravity="left|center"android:text="音频名称"android:textColor="@color/black"android:textSize="15sp" /><TextViewandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="right|center"android:text="总时长"android:textColor="@color/black"android:textSize="15sp" /></LinearLayout><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv_audio"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /></LinearLayout>

三、利用MediaRecoder录制音频

Android提供了媒体录制器MediaRecoder,它既能录制音频也能录制视频,它可以在当前页面直接录音,而不必跳到系统自带的录音机界面。常用方法与MediaPlayer基本一致

它的基本应用场景也只有两个1:开始录制媒体文件

2:停止录制媒体文件

其中录制音频的场景需要经历以下步骤

重置录制器-设置媒体文件的路径-准备录制-开始录制

效果如下 可以自行选择编码格式和录制时长

代码如下

Java类

package com.example.chapter13;import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import com.example.chapter13.util.MediaUtil;import java.util.Timer;
import java.util.TimerTask;public class MediaRecorderActivity extends AppCompatActivity implements View.OnClickListener, MediaRecorder.OnInfoListener {private static final String TAG = "MediaRecorderActivity";private Button btn_record;private LinearLayout ll_progress;private ProgressBar pb_record; // 声明一个进度条对象private TextView tv_progress;private ImageView iv_audio; // 该图标充当播放按钮private MediaRecorder mMediaRecorder = new MediaRecorder(); // 媒体录制器private boolean isRecording = false; // 是否正在录制private int mAudioEncoder; // 音频编码private int mOutputFormat; // 输出格式private int mDuration; // 录制时长private String mRecordFilePath; // 录制文件的保存路径private Timer mTimer = new Timer(); // 计时器private int mTimeCount; // 时间计数private MediaPlayer mMediaPlayer = new MediaPlayer(); // 媒体播放器@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_media_recorder);btn_record = findViewById(R.id.btn_record);ll_progress = findViewById(R.id.ll_progress);pb_record = findViewById(R.id.pb_record);tv_progress = findViewById(R.id.tv_progress);iv_audio = findViewById(R.id.iv_audio);btn_record.setOnClickListener(this);iv_audio.setOnClickListener(this);initEncoderSpinner(); // 初始化音频编码的下拉框initFormatSpinner(); // 初始化输出格式的下拉框initDurationSpinner(); // 初始化录制时长的下拉框}// 初始化音频编码的下拉框private void initEncoderSpinner() {ArrayAdapter<String> encoderAdapter = new ArrayAdapter<String>(this,R.layout.item_select, encoderDescArray);Spinner sp_encoder = findViewById(R.id.sp_encoder);sp_encoder.setPrompt("请选择音频编码"); // 设置下拉框的标题sp_encoder.setAdapter(encoderAdapter); // 设置下拉框的数组适配器// 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法sp_encoder.setOnItemSelectedListener(new EncoderSelectedListener());sp_encoder.setSelection(0); // 设置下拉框默认显示第一项}private String[] encoderDescArray = {"默认编码","窄带编码","宽带编码","低复杂度的高级编码","高效率的高级编码","增强型低延时的高级编码"};private int[] encoderArray = {MediaRecorder.AudioEncoder.DEFAULT,MediaRecorder.AudioEncoder.AMR_NB,MediaRecorder.AudioEncoder.AMR_WB,MediaRecorder.AudioEncoder.AAC,MediaRecorder.AudioEncoder.HE_AAC,MediaRecorder.AudioEncoder.AAC_ELD};class EncoderSelectedListener implements AdapterView.OnItemSelectedListener {public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {mAudioEncoder = encoderArray[arg2];}public void onNothingSelected(AdapterView<?> arg0) {}}// 初始化输出格式的下拉框private void initFormatSpinner() {ArrayAdapter<String> formatAdapter = new ArrayAdapter<String>(this,R.layout.item_select, formatDescArray);Spinner sp_format = findViewById(R.id.sp_format);sp_format.setPrompt("请选择输出格式"); // 设置下拉框的标题sp_format.setAdapter(formatAdapter); // 设置下拉框的数组适配器sp_format.setSelection(0); // 设置下拉框默认显示第一项// 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法sp_format.setOnItemSelectedListener(new FormatSelectedListener());}private String[] formatDescArray = {"默认格式","窄带格式","宽带格式","高级的音频传输流格式"};private int[] formatArray = {MediaRecorder.OutputFormat.DEFAULT,MediaRecorder.OutputFormat.AMR_NB,MediaRecorder.OutputFormat.AMR_WB,MediaRecorder.OutputFormat.AAC_ADTS};class FormatSelectedListener implements AdapterView.OnItemSelectedListener {public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {mOutputFormat = formatArray[arg2];}public void onNothingSelected(AdapterView<?> arg0) {}}// 初始化录制时长的下拉框private void initDurationSpinner() {ArrayAdapter<String> durationAdapter = new ArrayAdapter<String>(this,R.layout.item_select, durationDescArray);Spinner sp_duration = findViewById(R.id.sp_duration);sp_duration.setPrompt("请选择录制时长"); // 设置下拉框的标题sp_duration.setAdapter(durationAdapter); // 设置下拉框的数组适配器sp_duration.setSelection(0); // 设置下拉框默认显示第一项// 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法sp_duration.setOnItemSelectedListener(new DurationSelectedListener());}private String[] durationDescArray = {"5秒", "10秒", "20秒", "30秒", "60秒"};private int[] durationArray = {5, 10, 20, 30, 60};class DurationSelectedListener implements AdapterView.OnItemSelectedListener {public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {mDuration = durationArray[arg2];}public void onNothingSelected(AdapterView<?> arg0) {}}@Overridepublic void onClick(View v) {if (v.getId() == R.id.btn_record) {if (!isRecording) { // 未在录音startRecord(); // 开始录音} else { // 正在录音stopRecord(); // 停止录音}} else if (v.getId() == R.id.iv_audio) {mMediaPlayer.reset(); // 重置媒体播放器mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流的类型为音乐try {mMediaPlayer.setDataSource(mRecordFilePath); // 设置媒体数据的文件路径mMediaPlayer.prepare(); // 媒体播放器准备就绪mMediaPlayer.start(); // 媒体播放器开始播放} catch (Exception e) {e.printStackTrace();}}}// 开始录音private void startRecord() {Log.d(TAG, "startRecord mAudioEncoder="+mAudioEncoder+", mOutputFormat="+mOutputFormat+", mDuration="+mDuration);ll_progress.setVisibility(View.VISIBLE);isRecording = !isRecording;btn_record.setText("停止录制");pb_record.setMax(mDuration); // 设置进度条的最大值mTimeCount = 0; // 时间计数清零mTimer = new Timer(); // 创建一个计时器mTimer.schedule(new TimerTask() {@Overridepublic void run() {pb_record.setProgress(mTimeCount); // 设置进度条的当前进度tv_progress.setText(MediaUtil.formatDuration(mTimeCount*1000));mTimeCount++;}}, 0, 1000); // 计时器每隔一秒就更新进度条上的录制进度// 获取本次录制的媒体文件路径mRecordFilePath = MediaUtil.getRecordFilePath(this, "RecordAudio", ".amr");// 下面是媒体录制器的处理代码mMediaRecorder.reset(); // 重置媒体录制器mMediaRecorder.setOnInfoListener(this); // 设置媒体录制器的信息监听器mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频源为麦克风mMediaRecorder.setOutputFormat(mOutputFormat); // 设置媒体的输出格式。该方法要先于setAudioEncoder调用mMediaRecorder.setAudioEncoder(mAudioEncoder); // 设置媒体的音频编码器// mMediaRecorder.setAudioSamplingRate(8); // 设置媒体的音频采样率。可选// mMediaRecorder.setAudioChannels(2); // 设置媒体的音频声道数。可选// mMediaRecorder.setAudioEncodingBitRate(1024); // 设置音频每秒录制的字节数。可选mMediaRecorder.setMaxDuration(mDuration * 1000); // 设置媒体的最大录制时长// mMediaRecorder.setMaxFileSize(1024*1024*10); // 设置媒体的最大文件大小// setMaxFileSize与setMaxDuration设置其一即可mMediaRecorder.setOutputFile(mRecordFilePath); // 设置媒体文件的保存路径try {mMediaRecorder.prepare(); // 媒体录制器准备就绪mMediaRecorder.start(); // 媒体录制器开始录制} catch (Exception e) {e.printStackTrace();}}// 停止录音private void stopRecord() {isRecording = !isRecording;btn_record.setText("开始录制");mTimer.cancel(); // 取消定时器mMediaRecorder.stop(); // 媒体录制器停止录制}@Overridepublic void onInfo(MediaRecorder mr, int what, int extra) {// 录制达到最大时长,或者达到文件大小限制,都停止录制if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED|| what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {stopRecord(); // 停止录音iv_audio.setVisibility(View.VISIBLE);Toast.makeText(this, "已结束录制,音频文件路径为"+mRecordFilePath, Toast.LENGTH_LONG).show();}}@Overrideprotected void onStop() {super.onStop();if (!TextUtils.isEmpty(mRecordFilePath) && isRecording) {stopRecord(); // 停止录音}if (mMediaPlayer.isPlaying()) { // 如果正在播放mMediaPlayer.stop(); // 停止播放}}@Overrideprotected void onDestroy() {super.onDestroy();mMediaRecorder.release(); // 释放媒体录制器mMediaPlayer.release(); // 释放媒体播放器}
}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="40dp"android:paddingLeft="5dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center"android:text="音频编码:"android:textColor="@color/black"android:textSize="17sp" /><Spinnerandroid:id="@+id/sp_encoder"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="left|center"android:spinnerMode="dialog" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="40dp"android:paddingLeft="5dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center"android:text="输出格式:"android:textColor="@color/black"android:textSize="17sp" /><Spinnerandroid:id="@+id/sp_format"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="left|center"android:spinnerMode="dialog" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="40dp"android:paddingLeft="5dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center"android:text="录制时长:"android:textColor="@color/black"android:textSize="17sp" /><Spinnerandroid:id="@+id/sp_duration"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="left|center"android:spinnerMode="dialog" /></LinearLayout><Buttonandroid:id="@+id/btn_record"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="开始录音"android:textColor="@color/black"android:textSize="17sp" /><LinearLayoutandroid:id="@+id/ll_progress"android:layout_width="match_parent"android:layout_height="30dp"android:paddingLeft="5dp"android:paddingRight="5dp"android:orientation="horizontal"android:visibility="gone"><ProgressBarandroid:id="@+id/pb_record"style="?android:attr/progressBarStyleHorizontal"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="4" /><TextViewandroid:id="@+id/tv_progress"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="right|center"android:textColor="@color/black"android:textSize="15sp" /></LinearLayout><ImageViewandroid:id="@+id/iv_audio"android:layout_width="match_parent"android:layout_height="50dp"android:scaleType="fitCenter"android:src="@drawable/play_audio"android:visibility="gone" /></LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

Android Studio App开发中使用录音机、MediaRecorder录制音频和MediaPlayer播放音频讲解及实战(附源码)相关推荐

  1. C#使用Xamarin开发可移植移动应用进阶篇(8.打包生成安卓APK并精简大小),附源码...

    C#使用Xamarin开发可移植移动应用进阶篇(8.打包生成安卓APK并精简大小),附源码 原文:C#使用Xamarin开发可移植移动应用进阶篇(8.打包生成安卓APK并精简大小),附源码 前言 系列 ...

  2. C#使用Xamarin开发可移植移动应用进阶篇(8.打包生成安卓APK并精简大小),附源码

    我记得,之前在写安卓方面的文章的时候,有人就问过我.Xamarin.Android为什么打包出来这么大?随便一个HelloWord就20-30MB? 嗯..今天我们就来解决这个问题.. 我们先从指定一 ...

  3. C#使用Xamarin开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码...

    原文:C#使用Xamarin开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码 前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github. ...

  4. C#使用Xamarin开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码

    本篇..基本可以算是Xamarin在应用开发过程中的核心了..真的很很很重要.. 想学习的..想用的..建议仔细阅读..嗯..打酱油的 ..快速滑倒下面点个推荐 - - 哈哈哈... 今天的学习内容? ...

  5. C#使用Xamarin开发可移植移动应用进阶篇(6.使用渲染器针对单个平台自定义控件),附源码

    本篇..基本可以算是Xamarin在应用开发过程中的核心了..真的很很很重要.. 想学习的..想用的..建议仔细阅读..嗯..打酱油的 ..快速滑倒下面点个推荐 - - 哈哈哈... 今天的学习内容? ...

  6. Android Studio App开发之网络通信中使用POST方式调用HTTP接口实现应用更新功能(附源码 超详细必看)

    运行有问题或需要源码请点赞关注收藏后评论区留言~~~ 一.POST方式调用HTTP接口 POST方式把接口地址与请求报文分开,允许使用自定义的报文格式,由此扩大了该方式的应用场景.POST请求与GET ...

  7. ANDROID物联网开发从入门到实战附源码

    本书从获取源码和搭建应用开发环境开始讲起,依次讲解了基础知识篇.数据传输篇.信息识别篇.传感器应用篇和技术提高篇这 5大部分内容. 目录 第1篇 基础知识篇 第1章 Android系统介绍 2 1.1 ...

  8. C#使用Xamarin开发可移植移动应用(5.进阶篇显示弹出窗口与通讯中心)附源码

    源码地址:https://github.com/l2999019/DemoApp 可以Star一下,随意 - - 说点什么.. 没啥好说的.开干吧. 今天的学习内容? 今天的内容比较简单. 就几个弹出 ...

  9. 仿酷狗音乐播放器开发日志二十二 动态调色板控件第二版(性能大幅提升附源码)...

    转载请说明原出处,谢谢~~ 在上次写的博客<仿酷狗音乐播放器开发日志二十一 开发动态调色板控件(附源码)>发布后,我在群里和网友讨论这个控件的性能和优 缺点,发现了他很多不足,还有很多提升 ...

最新文章

  1. 科研指导:深度学习的应用研究课程
  2. 'eval' is null or not an object
  3. android exchange服务,带有“ Service com.android.exchange.ExchangeService
  4. 吃透Java集合中的Set集合必备文章,快快收藏
  5. vue中如何画饼状图
  6. 【模拟】P1424 小鱼的航程(改进版)
  7. 直播预告 | 后广告时代数据助力融合媒体用户收入增长
  8. python三大神器===》装饰器
  9. go WaitGroup 简单示例
  10. 如何查看某个ABAP user针对某个authorization object的assignment status
  11. [转载]提升进程权限-OpenProcessToken等函数的用法
  12. Linux bash基本介绍
  13. mcafee杀死oracle,如何从卸载McAfee卸载工具
  14. 最新html word 分页符,分页符 有什么用
  15. Go语法·类型选择(type switch)
  16. linux ubuntu木马,Ubuntu病毒查杀 ClamAV 简介以及适用范围
  17. 智能时代悄然到来刷脸支付逐渐成为潮流
  18. 魔戒中超眩的武器装备!
  19. 【深度好文】香港富豪卧底贫民窟:“你没出息,是因为不努力”害了多少程序员
  20. FAFU OJ 依旧水水的dp3

热门文章

  1. c++调节控制台字体(c++入门练习)
  2. (4)量子态矢与算子(算符)
  3. python数据可视化是什么_Python数据可视化的四种简易方法
  4. Vue 在使用v-if的前提下,使用elementResizeDetector配合ref获取元素块的高度
  5. Jenkins+Newman+Postman生成接口自动化测试报告
  6. 二叉排序树中查找效率最高的是
  7. python中字符串输出乱码怎么解决_python字符乱码的解决小结
  8. 台式计算机蓝牙如何安装,台式电脑没有蓝牙怎么安装
  9. 电子信息类和计算机类专业网课表
  10. 解线性方程组的各种情况