您的位置:首页 > 移动开发 > Android开发

Android之从特定目录读取和写入数据

2017-03-11 11:11 411 查看
Android之访问内外部存储空间
本文链接:http://blog.csdn.net/qq_16628781/article/details/61413556

知识点:
1、权限管理;
2、获取内外部目录的方法;
3、此示例演示如何从特定目录读取和写入数据,同时需要较少的权限

在开发中,我们经常会用到存储空间,这是一个不可避免的问题。那么,我们该如何来获取到我们需要的存储空间呢。下面为大家介绍一个方法,因为我都在代码里头做了解释,所以文字我就不写那么多了,省的大家一开头就看到这么多的文字,就不想看下去了。
关键的代码都做了注释。

这里要注意一点:我的项目至少是24的SDK才能运行的。
compileSdkVersion 24
buildToolsVersion "25.0.2"

defaultConfig {
minSdkVersion 24
targetSdkVersion 24
}


什么?你手头没有24以上的机器?这还不简单,我们可以利用as提供的虚拟机设备啊。只要你勤点更新SDK,你就能启动一个25SDK机器了。
首先上图



我打开的是sd卡的目录,我们可以看到,列出了这么多,但是因为是模拟题,里头都是没有东西的。

下面,我先把这个页面的代码贴出来

1、是主页fragment_scoped_directory_access的布局:



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="@dimen/margin_medium">

<LinearLayout
android:id="@+id/container_volumes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_small"
android:orientation="horizontal">

<TextView
android:id="@+id/textview_primary_volume_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_medium"
android:text="内部存储空间" />

<Button
android:id="@+id/button_open_directory_primary_volume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="打开" />
</LinearLayout>
</LinearLayout>

<Spinner
android:id="@+id/spinner_directories"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_small" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_small"
android:layout_marginStart="@dimen/margin_small"
android:orientation="horizontal">

<TextView
android:id="@+id/label_current_directory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选择的目录" />

<TextView
android:id="@+id/textview_current_directory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:enabled="false"
android:textColor="#000000" />

</LinearLayout>

<TextView
android:id="@+id/textview_nothing_in_directory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_medium"
android:text="选择的文件夹为空"
android:visibility="gone" />

<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview_directory_entries"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/margin_small"
android:layout_marginStart="@dimen/margin_small"
android:drawSelectorOnTop="true"
android:scrollbars="vertical"
app:layoutManager="LinearLayoutManager" />

</LinearLayout>


然后是volume_entry的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_small"
android:orientation="horizontal">

<TextView
android:id="@+id/textview_volume_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_medium" />

<Button
android:id="@+id/button_open_directory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="打开" />
</LinearLayout>


2、是Java代码:
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.provider.DocumentsContract;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

/**
* 访问内外部存储空间的例子
*/
public class ScopedDirectoryAccessFragment extends Fragment {

private static final String DIRECTORY_ENTRIES_KEY = "directory_entries";
private static final String SELECTED_DIRECTORY_KEY = "selected_directory";
private static final int OPEN_DIRECTORY_REQUEST_CODE = 1;

private static final String[] DIRECTORY_SELECTION = new String[]{
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_MIME_TYPE,
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
};

private Activity mActivity;
private StorageManager mStorageManager;
private TextView mCurrentDirectoryTextView;
private TextView mNothingInDirectoryTextView;
private TextView mPrimaryVolumeNameTextView;
private Spinner mDirectoriesSpinner;
private DirectoryEntryAdapter mAdapter;
private ArrayList<DirectoryEntry> mDirectoryEntries;

public static ScopedDirectoryAccessFragment newInstance() {
ScopedDirectoryAccessFragment fragment = new ScopedDirectoryAccessFragment();
return fragment;
}

public ScopedDirectoryAccessFragment() {
}

@Override
public void onAttach(Context context) {
super.onAttach(context);
mActivity = getActivity();
mStorageManager = mActivity.getSystemService(StorageManager.class);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// 回调
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == OPEN_DIRECTORY_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
// 向用户获取权读取内部存储和外部存储的权限
getActivity().getContentResolver().takePersistableUriPermission(data.getData(),
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
updateDirectoryEntries(data.getData());
}
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_scoped_directory_access, container, false);
}

@Override
public void onViewCreated(final View rootView, Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);

mCurrentDirectoryTextView = (TextView) rootView
.findViewById(R.id.textview_current_directory);
mNothingInDirectoryTextView = (TextView) rootView
.findViewById(R.id.textview_nothing_in_directory);
mPrimaryVolumeNameTextView = (TextView) rootView
.findViewById(R.id.textview_primary_volume_name);

// Set onClickListener for the primary volume
Button openPictureButton = (Button) rootView
.findViewById(R.id.button_open_directory_primary_volume);
/* 内部存储空间按钮 */
openPictureButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 得到spinner的选择项
String selected = mDirectoriesSpinner.getSelectedItem().toString();
// 获取到要访问的目录名称
String directoryName = getDirectoryName(selected);
// 获取到外部存储空间的大小
StorageVolume storageVolume = mStorageManager.getPrimaryStorageVolume();
// 创建一个访问的意图,得到用户的允许之后,就可以访问了
Intent intent = storageVolume.createAccessIntent(directoryName);
// 启动
startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE);
}
});

//获取外部存储空间
List<StorageVolume> storageVolumes = mStorageManager.getStorageVolumes();
LinearLayout containerVolumes = (LinearLayout) mActivity
.findViewById(R.id.container_volumes);
//如果内部存储空间(sd卡)存在的话,就加入访问外部存储空间的按钮
for (final StorageVolume volume : storageVolumes) {
String volumeDescription = volume.getDescription(mActivity);
if (volume.isPrimary()) {
// Primary volume area is already added...
if (volumeDescription != null) {
// 设置外部存储的真实名称,如果可用的话
mPrimaryVolumeNameTextView.setText(volumeDescription);
}
continue;
}
// 加载自定义布局
LinearLayout volumeArea = (LinearLayout) mActivity.getLayoutInflater()
.inflate(R.layout.volume_entry, containerVolumes);
TextView volumeName = (TextView) volumeArea.findViewById(R.id.textview_volume_name);
volumeName.setText(volumeDescription);
Button button = (Button) volumeArea.findViewById(R.id.button_open_directory);
// 设置点击事件
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String selected = mDirectoriesSpinner.getSelectedItem().toString();
String directoryName = getDirectoryName(selected);
Intent intent = volume.createAccessIntent(directoryName);
startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE);
}
});
}

RecyclerView recyclerView = (RecyclerView) rootView
.findViewById(R.id.recyclerview_directory_entries);
// 这里做一个判断,是否之前有保存此fragment的状态
if (savedInstanceState != null) {
mDirectoryEntries = savedInstanceState.getParcelableArrayList(DIRECTORY_ENTRIES_KEY);
mCurrentDirectoryTextView.setText(savedInstanceState.getString(SELECTED_DIRECTORY_KEY));
mAdapter = new DirectoryEntryAdapter(mDirectoryEntries);
if (mAdapter.getItemCount() == 0) {
mNothingInDirectoryTextView.setVisibility(View.VISIBLE);
}
} else {
mDirectoryEntries = new ArrayList<>();
mAdapter = new DirectoryEntryAdapter();
}
recyclerView.setAdapter(mAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

// 给spinner设置值
mDirectoriesSpinner = (Spinner) rootView.findViewById(R.id.spinner_directories);
ArrayAdapter<CharSequence> directoriesAdapter = ArrayAdapter
.createFromResource(getActivity(),
R.array.directories, android.R.layout.simple_spinner_item);
directoriesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mDirectoriesSpinner.setAdapter(directoriesAdapter);
}

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 当fragment被系统意外杀死是,会调用此方法
// 如果有不明白的,可以看 这个文章:
// http://blog.csdn.net/qq_16628781/article/details/60877412 outState.putString(SELECTED_DIRECTORY_KEY, mCurrentDirectoryTextView.getText().toString());
outState.putParcelableArrayList(DIRECTORY_ENTRIES_KEY, mDirectoryEntries);
}

private void updateDirectoryEntries(Uri uri) {
mDirectoryEntries.clear();

// 这里涉及到内容提供者,可以搜索内容提供者的相关知识
// 也可以看这篇文章的解释:http://blog.csdn.net/qq_16628781/article/details/61195621
// 获取内容获得者
ContentResolver contentResolver = getActivity().getContentResolver();

// 根据URI和id,建立一个URI代表目标去访问内容提供者
Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri,
DocumentsContract.getTreeDocumentId(uri));
// 访问URI的子目录
Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri,
DocumentsContract.getTreeDocumentId(uri));

// 查询URI提供者
try (Cursor docCursor = contentResolver
.query(docUri, DIRECTORY_SELECTION, null, null, null)) {
while (docCursor != null && docCursor.moveToNext()) {
mCurrentDirectoryTextView.setText(docCursor.getString(docCursor.getColumnIndex(
DocumentsContract.Document.COLUMN_DISPLAY_NAME)));
}
}

// 查询子目录
try (Cursor childCursor = contentResolver
.query(childrenUri, DIRECTORY_SELECTION, null, null, null)) {
while (childCursor != null && childCursor.moveToNext()) {
// 获得子目录下的所有文件夹,最后在recycleview里头展示出来
DirectoryEntry entry = new DirectoryEntry();
entry.fileName = childCursor.getString(childCursor.getColumnIndex(
DocumentsContract.Document.COLUMN_DISPLAY_NAME));
entry.mimeType = childCursor.getString(childCursor.getColumnIndex(
DocumentsContract.Document.COLUMN_MIME_TYPE));
mDirectoryEntries.add(entry);
}

if (mDirectoryEntries.isEmpty()) {
mNothingInDirectoryTextView.setVisibility(View.VISIBLE);
} else {
mNothingInDirectoryTextView.setVisibility(View.GONE);
}
mAdapter.setDirectoryEntries(mDirectoryEntries);
mAdapter.notifyDataSetChanged();
}
}

/**
* 获取目录的名称
*
* @param name name
* @return name,比如有dcim图片目录,下载目录,音乐等等文件类型
*/
private String getDirectoryName(String name) {
switch (name) {
case "ALARMS":
return Environment.DIRECTORY_ALARMS;
case "DCIM":
return Environment.DIRECTORY_DCIM;
case "DOCUMENTS":
return Environment.DIRECTORY_DOCUMENTS;
case "DOWNLOADS":
return Environment.DIRECTORY_DOWNLOADS;
case "MOVIES":
return Environment.DIRECTORY_MOVIES;
case "MUSIC":
return Environment.DIRECTORY_MUSIC;
case "NOTIFICATIONS":
return Environment.DIRECTORY_NOTIFICATIONS;
case "PICTURES":
return Environment.DIRECTORY_PICTURES;
case "PODCASTS":
return Environment.DIRECTORY_PODCASTS;
case "RINGTONES":
return Environment.DIRECTORY_RINGTONES;
default:
throw new IllegalArgumentException("Invalid directory representation: " + name);
}
}
}


里边还有一个数组directories在string.xml文件里头:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string-array name="directories">
<item>ALARMS</item>
<item>DCIM</item>
<item>DOCUMENTS</item>
<item>DOWNLOADS</item>
<item>MOVIES</item>
<item>MUSIC</item>
<item>NOTIFICATIONS</item>
<item>PICTURES</item>
<item>PODCASTS</item>
<item>RINGTONES</item>
</string-array>
</resources>


然后是适配器:DirectoryEntryAdapter.java,适配器的代码比较简单,我就不做解释了,主要使用到了RecyclerView这个新的控件,如果��不懂的,可以自己搜索一下,这里都是最简单的用法,一看就懂了。

package com.example.android.scopeddirectoryaccess;

import android.provider.DocumentsContract;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

/**
* 适配器
*/
public class DirectoryEntryAdapter extends RecyclerView.Adapter<DirectoryEntryAdapter.ViewHolder> {

private List<DirectoryEntry> mDirectoryEntries;

public DirectoryEntryAdapter() {
this(new ArrayList<DirectoryEntry>());
}

public DirectoryEntryAdapter(List<DirectoryEntry> directoryEntries) {
mDirectoryEntries = directoryEntries;
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.directory_entry, viewGroup, false);
return new ViewHolder(v);
}

@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
viewHolder.fileName.setText(mDirectoryEntries.get(position).fileName);
viewHolder.mimeType.setText(mDirectoryEntries.get(position).mimeType);

if (DocumentsContract.Document.MIME_TYPE_DIR
.equals(mDirectoryEntries.get(position).mimeType)) {
viewHolder.imageView.setImageResource(R.drawable.ic_directory_grey600_36dp);
} else {
viewHolder.imageView.setImageResource(R.drawable.ic_description_grey600_36dp);
}
}

@Override
public int getItemCount() {
return mDirectoryEntries.size();
}

public void setDirectoryEntries(List<DirectoryEntry> directoryEntries) {
mDirectoryEntries = directoryEntries;
}

public class ViewHolder extends RecyclerView.ViewHolder {

public TextView fileName;
public TextView mimeType;
public ImageView imageView;

public ViewHolder(View v) {
super(v);
fileName = (TextView) v.findViewById(R.id.textview_filename);
mimeType = (TextView) v.findViewById(R.id.textview_mimetype);
imageView = (ImageView) v.findViewById(R.id.imageview_entry);
}
}
}

然后是适配器的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/directory_item_height"
android:layout_centerVertical="true"
android:gravity="center_vertical"
android:orientation="horizontal">

<ImageView
android:id="@+id/imageview_entry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_directory_grey600_36dp" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_medium"
android:orientation="vertical">

<View
android:id="@+id/divisor"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#aaaaaa" />

<TextView
android:id="@+id/textview_filename"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000" />

<TextView
android:id="@+id/textview_mimetype"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>
</LinearLayout>


代码基本上就是这样子。
下面再给一个运行的图片



GitHub地址:点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息