Android开发App踩坑记录
[toc]
音频
录音
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| if (audio.getTag().toString().equals("0")) { audio.setImageResource(R.mipmap.stop); audio.setTag("1"); time = (String) DateFormat.format("yyyyMMdd_HHmmss", Calendar.getInstance(Locale.CHINA));
String dir_path = Note.this.getFilesDir().getPath() + "/recordings/"; Log.d(TAG, "onClick: dir: " + dir_path); File dir = new File(dir_path); if(!dir.exists()){ dir.mkdirs(); }
String file_name = dir_path + "/" + time + ".aac"; recordAudioFile = new File(file_name); try { if (!recordAudioFile.createNewFile()) { Log.d(TAG, "onClick: file create failed."); } else { Log.d(TAG, "onClick: file create done."); } } catch (IOException e) { e.printStackTrace(); }
mediaRecorder = new MediaRecorder(); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC_ELD); mediaRecorder.setOutputFile(recordAudioFile.getAbsolutePath()); try { mediaRecorder.prepare(); } catch (IOException e) { Log.d(TAG, "onClick: prepare failed."); } mediaRecorder.start();
} else { audio.setImageResource(R.mipmap.audio); audio.setTag("0");
if (mediaRecorder != null){ Log.d(TAG, "onClick: record stop: " + recordAudioFile.getPath()); mediaRecorder.stop(); mediaRecorder.release(); mediaRecorder = null; if (getCurrentFocus() != null) { SingleNoteLayout child = (SingleNoteLayout) getCurrentFocus().getParent().getParent(); create_record(linearLayout.indexOfChild(child), recordAudioFile.getAbsolutePath()); } else { create_record(linearLayout.getChildCount()-1, recordAudioFile.getAbsolutePath()); }
} }
|
播音:资源文件
1 2 3 4 5 6
| private MediaPlayer mediaPlayer;
mediaPlayer = MediaPlayer.create(this, resid); mediaPlayer.start();
|
播音:本地文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| private MediaPlayer mediaPlayer;
mediaPlayer = new MediaPlayer(); mediaPlayer.setDataSource(path); mediaPlayer.prepare(); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { mp.stop(); mp.release(); mp = null; Log.d(TAG, "play: complete"); } }); mediaPlayer.start(); } catch (Exception e) { e.printStackTrace(); Log.d(TAG, "play: file_path is wrong."); }
|
LinearLayout的动态增删控件
增:addView时需要index参数,getChildCount、getChildAt、indexOfChild是很常用的方法。
删:removeView、removeViewAt、removeViewAll
控件嵌套的parent
控件嵌套,同时只能点击到某个子控件,要通过子控件获取父级控件甚至更高级控件,可以使用onClick的参数view,一路getParent获取,同时使用getClass判断类型。多层嵌套时可能要使用多次getParent。
权限框架
easypermissions
参考博客
ImageView 上下空白区域
图片像素大于屏幕像素时,显示有问题。尝试以下设置:android:adjustViewBounds=“true” 。
Glide注入图片,与androidx/android10可能兼容性较差,出现了奇怪错误。
createNewFile()
要在手动建立的文件夹下,才可create
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| String dir_path = Note.this.getFilesDir().getPath() + "/new_dir/"; Log.d(TAG, "onClick: dir: " + dir_path); File dir = new File(dir_path); if(!dir.exists()){ dir.mkdirs(); }
String filename = dir_path + "/hi.txt"; File des = new File(filename); try { if (!des.createNewFile()) { Log.d(TAG, "onClick: file already exists"); } else { Log.d(TAG, "onClick: test file create done."); } } catch (IOException e) { e.printStackTrace(); }
|
数据库升级
1 2
| dbHelper = new MyDBHelper(this, "Course.db", null, 2); db = dbHelper.getWritableDatabase();
|
首行参数指定新的version_code,DBHelper.class的onUpgrade中,针对不同版本的升级做出判断与操作。
Uri.fromFile(new File(path))
报错:java.io.FileNotFoundException: open failed: EACCES (Permission denied)
解决:android:requestLegacyExternalStorage="true"
application
imageView.setImageURI(uri)
从库中读取string,setImageURI(Uri.pairse(string))会报错:
java.lang.SecurityException: Permission Denial: opening provider … that is not exported from UID 10096
无合适解决方法,改用setImageURI(Uri.fromFile(new File(path)))。
但是该方法获得的Uri,调用图库查看图片时又会引起FileUriExposedException异常 😓
intent向前传递的顺序问题
子活动返回result,即setResult()是在被finish()之前。
子活动setResult()之后,主活动的onActivityResult()就会启动。这就会导致onActivityResult()先于子活动的onDestroy()。
同时,onPause(), onStop(), onDestroy()中调用setResult()也是不安全的,这些步骤可能会在finish()之后。
录音设置
编码器、输出文件、文件后缀之间的协调:最终采用文件指定后缀、输出文件Default、编码器与文件后缀匹配
ImageView的修改图片
xml中设置src后,若代码setBackground,则src图片不消失,会重叠。统一使用src或background。
