好久没更新博客了,今天写一篇 我最近遇到的问题,以及我是如何解决的的方法。
概要:本软件主要是运用于展讯平台手机的预装录音管理APP,基本大的功能已经写好了,之前在做的是在录音列表里添加长按删除录音文件、文件夹,本以为很简单,可以很快搞定,没想到搞了整整一个星期,真是走了很多弯路,好了不多说,先上“菜”。
public void scanFiles(String path, boolean clear) {
Log.d(TAG, "zhangcl: scanFiles(): path = " + path);
if(clear) {
list.clear();
}
File dir = new File(path);
File[] subFiles = dir.listFiles();
int loopindex = 0;
if(subFiles != null){
for(File f:subFiles){
list.add(f);
}
}
this.notifyDataSetChanged();
currPath = path;
}
CallRecordAdter的getview方法我就不贴出来了,也没什么好看的,这个方法主要是根据传入的路径遍历文件夹,添加到list中,这个方法的2个参数,第一个是要遍历的路径,第二个是判断是否要清空原本的list。
List.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
alertDialogDelete(arg2);
return true;
}
});
我先说下思路吧,思路很简单,"不就是一个长按删除的功能嘛",首先onItemLongClick已经提供了一个arg2,这个arg2其实就是postion,这个postion就是你长按的是listview中具体哪个item的下标,有了这个下标,你就可以拿到用户点击listview的item的值,之后判断是否为文件,如果是文件,通过File[] childFiles = file.listFiles();通过for循环遍历childFiles[i].getName().equals(item的值),如果相等就删除,如果是文件夹,判断文件夹下是否有文件,如果没有,直接删除。如果有文件的话,通过迭代遍历删除。哦 千万不要忘记了,更新媒体数据库。<pre name="code" class="java"> private void alertDialogDelete(final int index){
AlertDialog.Builder dialog=new Builder(this);
dialog.setTitle("Delete");
dialog.setMessage("Whether to delete?");
dialog.setIcon(R.drawable.ic_launcher);
dialog.setPositiveButton("OK", new OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
Adter = (CallRecordAdter) List.getAdapter();
String txNamePath=Adter.list.get(index).toString();
String txName=txNamePath.substring(txNamePath.lastIndexOf("/")+1);
String suffix = txName.substring(txName.lastIndexOf(".") + 1);
File file = new File(Adter.currPath);
if (!Environment.getExternalStoragePathState().equals(Environment.MEDIA_MOUNTED)) {
if (suffix.equals("m4a") || suffix.equals("mp3")
|| suffix.equals("mid") || suffix.equals("xmf")
|| suffix.equals("ogg") || suffix.equals("wav")
|| suffix.equals("3gpp") || suffix.equals("amr")) {
File[] childFiles = file.listFiles();
for (int i = 0,length=childFiles.length;i < length; i++) {
if (childFiles[i].getName().equals(txName)) {
if(childFiles[i].delete()){
scanDirAsync(CallRecordList.this,m_CellphonerecordingCard);
}
Adter.scanFiles(Adter.currPath, true);
break;
}
}
}else{
if(file.listFiles().length==0){
file.delete();
Adter.scanFiles(Adter.currPath, true);
}else{
File filee = new File(Adter.currPath+"/"+txName);
File[] files=filee.listFiles();
recursionDeleteFile(files);
scanDirAsync(CallRecordList.this,m_CellphonerecordingCard);
filee.delete();
Adter.scanFiles(Adter.currPath, true);
}
}
} else {
if (suffix.equals("m4a") || suffix.equals("mp3")
|| suffix.equals("mid") || suffix.equals("xmf")
|| suffix.equals("ogg") || suffix.equals("wav")
|| suffix.equals("3gpp") || suffix.equals("amr")) {
File[] childFiles = file.listFiles();
for (int i = 0,length=childFiles.length;i < length; i++) {
if (childFiles[i].getName().equals(txName)) {
if(childFiles[i].delete()){
scanDirAsync(CallRecordList.this,m_CellphonerecordingCard);
}
Adter.scanFiles(Adter.currPath, true);
break;
}
}
}else{
if(file.listFiles().length==0 && Adter.list.get(index).toString().equals(m_recordLocattionTcard)){
file.delete();
Adter.scanFiles(Adter.currPath, true);
} else{
File filee = new File(Adter.list.get(index).toString());
File[] files = filee.listFiles();
if(files.length == 0){
filee.delete();
}
recursionDeleteFile(files);
scanDirAsync(CallRecordList.this,m_CellphonerecordingCard);
filee.delete();
}
Adter.scanFiles(m_CellphonerecordingCard, true);
Adter.scanFiles(m_recordLocattionTcard, false);
}
}
}
});
dialog.setNegativeButton("NO", new OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
}
});
dialog.create().show();
}
这个就是我实现的代码了,因为我的录音列表是加载sd卡和内置sd卡的CallToRecout文件里创建的录音文件,所以我进行了sd卡的判断。拿用户点击的Item的值时,我之前是通过这个方法
TextView textView = (TextView) List.getChildAt(index).findViewById(R.id.record_name);
String txName = textView.getText().toString();
这个取值的方法,看起来是没有问题,但是这只是局限于一屏,如果删除一屏后的录音文件,就会异常。为什么呢?安卓其实对listview已经做了优化,当你往下滑动的时候,系统其实已经把你看不到的item给回收了,下滑看到的item其实是上面已经隐藏的item回收利用的,这个postion值这个时候肯定就有问题了。具体的listview自带的优化机制这里就不再详说了。
所以如果要拿item的值,最好是通过这个
Adter = (CallRecordAdter) List.getAdapter();
String txNamePath=Adter.list.get(index).toString();
它这Adter.list.get(index),到的值其实是我CallRecordAdter里的 List<File> list = new LinkedList<File>(); 保存的值,所以你拿到的肯定是 内置sd卡/外置sd卡绝对路径+CallToRecout+你选中的item的名称,然后你截取最后一个 / 之后的值就可以了。
接着说如何更新媒体数据库,以及为何要更新数据媒体库。
首先为什么要更新媒体数据库呢?因为android在启动的时候会启动MediaScannerService扫描系统上的多媒体文件,然后将这些多媒体文件的信息加入到多媒体数据库中,应用程序要取得这些多媒体信息就是从这个多媒体数据库里面去取的,并不是从SD卡中取。也就是说,如果开机后增加或删除了一些多媒体,这个多媒体数据库是不会自动刷新的,所以需要我们手动的发送广播或者直接操作数据库通过_id删除。
如何更新媒体数据库其实网上有很多办法,我也试了很多,但最终我用了这个方法。
public void scanDirAsync(Context ctx, String dir) {
Intent scanIntent = new Intent(ACTION_MEDIA_SCANNER_SCAN_DIR);
scanIntent.setData(Uri.fromFile(new File(dir)));
ctx.sendBroadcast(scanIntent);
}
这个方式是通过发送一个广播android.intent.action.MEDIA_SCANNER_SCAN_DIR,这个广播是扫描指定的目录,这种扫描方式中,由于扫描工作是在MediaScanner服务中进行的,因此不会阻塞当前程序进程。当扫描大量媒体文件且实时性要求不高的情况下,适合使用该扫描方式。当然你也可以扫描指定的文件是通过这个Intent.ACTION_MEDIA_SCANNER_SCAN_FILE。当你发送这个广播后,在framwork层有一个广播接收器接受这个广播,根据你传的file,和本地的媒体数据库比较,执行媒体数据库的insert和update操作。
最后再贴一下迭代遍历删除子文件的代码。
private void recursionDeleteFile(File[] files) {
for (File f : files) {
if (f.isFile()) {
f.delete();
} else {
File[] fileee = f.listFiles();
if (fileee.length == 0) {
f.delete();
} else {
recursionDeleteFile(fileee);
f.delete();
}
}
}
}