Vamos a continuar con la creación de la aplicación libreta usando AndroidStudio.
Pueden ver la publicación anterior (http://progrmrmalasmanas.blogspot.mx/2013/07/creando-una-aplicacion-de-libreta-en.html). En la ultima publicación se logro la creación de un archivo de texto
así como la lectura del mismo mostrando su contenido en una actividad.
Vamos a crear un método para crear el archivo en la tarjeta sd en lugar
de la memoria del teléfono y cambiar el nombre de los métodos que hemos creado;
para esto vamos a modificar la clase TextFile agregando esas nuevos metodos.
/*
* Copyright 2013 Roque Rueda.
*
* Licensed under the Apache License, Version
2.0 (the "License");
* you may not use this file except in
compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed
to in writing, software
* distributed under the License is distributed
on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.
* See the License for the specific language
governing permissions and
* limitations under the License.
*/
package
com.roque.rueda.notepad;
import
android.content.Context;
import
java.io.BufferedReader;
import java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.InputStreamReader;
import
java.io.OutputStreamWriter;
/**
* Save a file using a text input also gets the
text of a file
* and presents as a string.
* Created by Roque Rueda on 25/06/13.
*/
public
class TextFile {
private Context mContext;
private static final String ENCODING =
"utf8";
public TextFile(Context ctx) {
mContext = ctx;
}
/**
* Create a file in the device file system.
* @param path Path where a new text file
is going to be created.
* @param content Text content of the file.
* @return True if the file can be created.
* @throws IOException if there's a problem
creating the new file.
*/
public boolean createFile(String path,
String content)
throws IOException {
FileOutputStream fileOutputStream =
mContext.openFileOutput(path, Context.MODE_PRIVATE);
OutputStreamWriter osw = new
OutputStreamWriter(fileOutputStream, ENCODING);
osw.write(content);
osw.flush();
osw.close();
return true;
}
/**
* Read the contents of the file and
returns as a string.
* @param path Path of the file that is
going to be read.
* @return Content of the file.
* @throws IOException if the file can't be
found or if there's a problem
* reading the content of he file.
*/
public String getFileContent(String path)
throws IOException{
StringBuilder content = new
StringBuilder(350);
FileInputStream fileInputStream =
mContext.openFileInput(path);
InputStreamReader isr = new
InputStreamReader(fileInputStream, ENCODING);
BufferedReader br = new
BufferedReader(isr);
String line;
while ((line = br.readLine()) != null){
content.append(line).append("\n");
}
fileInputStream.close();
return content.toString();
}
}
Vamos a editar nuestra clase para conseguir algo como lo
siguiente:
/*
* Copyright 2013 Roque Rueda.
*
* Licensed under the Apache License, Version
2.0 (the "License");
* you may not use this file except in
compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed
to in writing, software
* distributed under the License is distributed
on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.
* See the License for the specific language
governing permissions and
* limitations under the License.
*/
package
com.roque.rueda.notepad;
import
android.content.Context;
import
android.os.Environment;
import
android.util.Log;
import
java.io.BufferedReader;
import
java.io.File;
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.FileReader;
import
java.io.IOException;
import
java.io.InputStreamReader;
import
java.io.OutputStreamWriter;
/**
* Save a file using a text input also gets the
text of a file
* and presents as a string.
* Created by Roque Rueda on 25/06/13.
*/
public
class TextFile {
private Context mContext;
private static final String ENCODING =
"utf8";
private static final String TAG =
"TextFile";
private static final String DIR =
"/textFiles";
private boolean mExternalStorageAvailable;
private boolean mExternalStorageWriteable;
public TextFile(Context ctx) {
mContext = ctx;
String state =
Environment.getExternalStorageState();
// We need to know if we can write to
external storage
if
(Environment.MEDIA_MOUNTED.equals(state)) {
// We can read and write the media
mExternalStorageAvailable =
mExternalStorageWriteable = true;
} else if
(Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// We can only read the media
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
// Something else is wrong. It may
be one of many other states, but all we need
//
to know is we can neither read nor write
mExternalStorageAvailable =
mExternalStorageWriteable = false;
}
}
/**
* Create a file in the device file system.
* @param name Path where a new text file
is going to be created.
* @param content Text content of the file.
* @return True if the file can be created.
* @throws IOException if there's a problem
creating the new file.
*/
public boolean createFile(String name,
String content)
throws IOException {
OutputStreamWriter osw = null;
try
{
FileOutputStream fileOutputStream =
mContext.openFileOutput(name, Context.MODE_PRIVATE);
osw = new
OutputStreamWriter(fileOutputStream, ENCODING);
osw.write(content);
osw.flush();
Log.w(TAG, String.format("A
new file is created named:%s", name));
return true;
} finally {
osw.close();
}
}
/**
* Creates a file if can in the sd card of
this device, otherwise
* creates the file in the the internal
storage.
* @param name name of the file.
* @param content content of the file.
* @return true if the file can be created.
* @throws IOException if the file can't be
created.
*/
public boolean createSDFile(String name,
String content)
throws IOException {
// We can write in the sd storage.
if (mExternalStorageAvailable
&& mExternalStorageWriteable) {
OutputStreamWriter osw = null;
try
{
File sdCard =
Environment.getExternalStorageDirectory();
File dir = new File
(sdCard.getAbsolutePath() + DIR);
// Creates a new folder.
dir.mkdir();
File file = new File(dir,
name);
FileOutputStream
fileOutputStream = new FileOutputStream(file);
osw = new
OutputStreamWriter(fileOutputStream, ENCODING);
osw.write(content);
osw.flush();
Log.w(TAG,
String.format("A new file is created named:%s", name));
return true;
}
finally {
if (osw != null)
osw.close();
}
} else {
return createFile(name, content);
}
}
/**
* Reads the contents of a sd card file.
* @param name name of the file that is
going to be read.
* @return Contents of the file as string.
* @throws IOException if the file can't be
found.
*/
public String getSDFileContent(String name)
throws IOException
{
if (mExternalStorageAvailable) {
BufferedReader br = null;
try {
File sdCard =
Environment.getExternalStorageDirectory();
File dir = new File
(sdCard.getAbsolutePath() + DIR);
File file = new File(dir,
name);
// This object contains the
text will store the content of the file.
StringBuilder text = new
StringBuilder(400);
br = new BufferedReader(new
FileReader(file));
String line;
while((line = br.readLine()) !=
null) {
text.append(line).append('\n');
}
Log.w(TAG,
String.format("File %s is read", name));
return text.toString();
} finally {
if (br != null)
br.close();
}
}
else {
return getFileContent(name);
}
}
/**
* Read the contents of the file and
returns as a string.
* @param name Path of the file that is
going to be read.
* @return Content of the file.
* @throws IOException if the file can't be
found or if there's a problem
* reading the content of he file.
*/
public String getFileContent(String name)
throws IOException{
BufferedReader br = null;
try {
StringBuilder content = new
StringBuilder(350);
FileInputStream fileInputStream =
mContext.openFileInput(name);
InputStreamReader isr = new
InputStreamReader(fileInputStream, ENCODING);
br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) !=
null){
content.append(line).append("\n");
}
Log.w(TAG, String.format("File
%s is read", name));
return content.toString();
} finally {
if (br != null)
br.close();
}
}
}
Ahora vamos a editar nuestro
diseño de interfaz de usuario para permitir crear un nuevo archivo nombrarlo y
guardarlo. Una cuestión extra seria poder abrir y visualizar el contenido para
su edición si se desea. Vamos a crear un nuevo archivo layout.
Vamos a crear un nuevo elemento
al cual vamos a nombrar edit_text.xml en donde vamos a colocar el diseño básico
para editar y visualizar el texto.
<?xml
version="1.0" encoding="utf-8"?>
<GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/text"
android:inputType="textMultiLine"
android:layout_gravity="center"
android:text="@string/app_name"
android:gravity="left|top"/>
</GridLayout>
Así es como definimos el
fragmento. Ahora vamos a indicar las funciones para ese fragmento el cual solo
contiene el Widget EditText para que se vea el texto y este pueda ser editado.
Ahora ya tenemos la parte visual tenemos que generar la clase que será la que
dé el funcionamiento del fragmento de nuestra aplicación.
/*
* Copyright 2013 Roque Rueda.
*
* Licensed under the Apache License, Version
2.0 (the "License");
* you may not use this file except in
compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed
to in writing, software
* distributed under the License is distributed
on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.
* See the License for the specific language
governing permissions and
* limitations under the License.
*/
package
com.roque.rueda.notepad;
import
android.os.Bundle;
import
android.view.LayoutInflater;
import
android.view.View;
import android.view.ViewGroup;
import
android.widget.EditText;
/**
* This fragment is used to show, edit and save
text into a txt file.
*
* Created by Roque on 28/07/13.
*/
public
class EditTextFragment extends android.support.v4.app.Fragment {
public static final String ARG_TEXT_FILE =
"myTextFile";
@Override
public View onCreateView(LayoutInflater
inflater, ViewGroup container,
Bundle
savedInstanceState) {
View rootView =
inflater.inflate(R.layout.edit_text, container, false);
EditText editText = (EditText)
rootView.findViewById(R.id.text);
if
(getArguments().containsKey(ARG_TEXT_FILE)){
editText.setText(getArguments().getString(ARG_TEXT_FILE));
}
return
rootView;
}
}
Vamos a modificar el método onNavigationItemSelected
de nuestra actividad principal NoteActivity.java para que sea algo como lo
siguiente:
@Override
public boolean onNavigationItemSelected(int
position, long id) {
// When the given dropdown item is
selected, show its contents in the
// container view.
Fragment fragment;
Bundle args = new Bundle();
switch (position){
case 0:{
fragment = new EditTextFragment();
String content = null;
try {
content =
mTextFile.getFileContent("test.txt");
} catch (IOException e) {
Log.w(TAG, e.getMessage());
}
args.putString(DummySectionFragment.ARG_TEXT_FILE, content);
break;
}
default:{
fragment = new
DummySectionFragment();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER,
position + 1);
break;
}
}
fragment.setArguments(args);
getSupportFragmentManager().beginTransaction()
.replace(R.id.container,
fragment)
.commit();
return
true;
}
Ejecutamos y en nuestro emulador veremos lo siguiente:
Como pueden ver ya se muestra un
Widget para editar el texto del documento ahora en la siguiente parte vamos a
ver como almacenar los cambios a nuestro documento de texto y la invocación de
los métodos para la escritura del archivo en la tarjeta sd.
En primer lugar vamos a generar
una clase con las constantes que estamos utilizando esto con el fin de evitar
tener dos veces la misma constante como en el caso de la clase DummySectionFragment
y EditTextFragment para evitar acoplamiento entre las clases. Vamos modificar
las llamadas a la constante con el siguiente codigo Constants.ARG_TEXT_FILE y nuestra clase quedara como la siguiente:
/*
* Copyright 2013 Roque Rueda.
*
* Licensed under the Apache License, Version
2.0 (the "License");
* you may not use this file except in
compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed
to in writing, software
* distributed under the License is distributed
on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.
* See the License for the specific language
governing permissions and
* limitations under the License.
*/
package
com.roque.rueda.notepad;
/**
* Contains the constants used in this
application.
*
* Created by Roque on 19/09/13.
*/
public
final class Constants {
/**
* This is the constant used to send data
between the fragments.
*/
public static final String ARG_TEXT_FILE =
"myTextFile";
private
Constants ()
{
// This is empty.
}
}
Ahora vamos a proceder a almacenar el texto que edite el
usuario cuando presione el menú deseamos que aparezca la opción de guardar.
Para esto vamos a navegar a nuestra NoteActivity para poder agregar el elemento
al menú. Nuestro menu se encuentra en el archivo res\menu\menu.xml, en este archivo vamos a agregar los elementos
que deseamos se muestren cuando se presione el botón de menú. Primero vamos a
agregar un nuevo string en nuestro archivo strings.xml
<string
name="action_save">Save</string>
Y posteriormente en nuestro archivo menu.xml
vamos a cambiar el texto de nuestro elemento en el menu como lo siguiente:
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_save"
android:title="@string/action_save"
android:orderInCategory="100"
android:showAsAction="never"
/>
</menu>
En nuestra activity vamos a manejar el evento que se dispara
cuando un elemento del menu es seleccionado para esto vamos a agregar el
siguiente método a nuestra clase:
@Override
public boolean onOptionsItemSelected(MenuItem
item){
switch(item.getItemId()){
}
return true;
}
Lo que vamos a hacer en este
metodo es del elemento del menu que es seleccionado vamos a obtener su id y
dependiendo del id es que vamos a realizar alguna acción en este caso guarda
los cambios de texto en el archivo. Para esto vamos a generar una interfaz la
cual va a ser nuestro contrato para recibir la información desde nuestros
fragmentos de la interfaz. Esto implica algunos cambios en nuestras clases DummySectionFragment
y EditTextFragment.
/*
* Copyright 2013 Roque Rueda.
*
* Licensed under the Apache License, Version
2.0 (the "License");
* you may not use this file except in
compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed
to in writing, software
* distributed under the License is distributed
on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.
* See the License for the specific language
governing permissions and
* limitations under the License.
*/
package
com.roque.rueda.notepad;
/**
* This interface defines behaviour to get user
text input.
*
* Created by Roque on 19/09/13.
*/
public
interface TextInput {
/**
* Returns the input of the user as text.
* @return user input.
*/
CharSequence getText();
}
En este caso la única
funcionalidad que se define es un método que retorna el texto que ingresa el
usuario. Este método debe ser implementado por los fragmentos de los que
deseamos obtener texto para almacenarlo en el archivo de texto. Quedando
nuestras clases de la siguiente manera:
/**
* A dummy
fragment representing a section of the app, but that simply
* displays
dummy text.
*/
public static class DummySectionFragment
extends Fragment implements TextInput {
/**
* The fragment argument representing
the section number for this
* fragment.
*/
public static final String
ARG_SECTION_NUMBER = "section_number";
private TextView mDummyTextView;
public DummySectionFragment() {
}
@Override
public View onCreateView(LayoutInflater
inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView =
inflater.inflate(R.layout.fragment_note_dummy, container, false);
mDummyTextView = (TextView)
rootView.findViewById(R.id.section_label);
if
(getArguments().containsKey(Constants.ARG_TEXT_FILE)){
mDummyTextView.setText(getArguments().getString(Constants.ARG_TEXT_FILE));
}
else {
mDummyTextView.setText(Integer.toString(getArguments().getInt(ARG_SECTION_NUMBER)));
}
return rootView;
}
@Override
public CharSequence getText() {
return mDummyTextView.getText();
}
}
/*
* Copyright 2013 Roque Rueda.
*
* Licensed under the Apache License, Version
2.0 (the "License");
* you may not use this file except in
compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed
to in writing, software
* distributed under the License is distributed
on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.
* See the License for the specific language
governing permissions and
* limitations under the License.
*/
package
com.roque.rueda.notepad;
import
android.os.Bundle;
import
android.view.LayoutInflater;
import
android.view.View;
import android.view.ViewGroup;
import
android.widget.EditText;
/**
* This fragment is used to show, edit and save
text into a txt file.
*
* Created by Roque on 28/07/13.
*/
public
class EditTextFragment extends android.support.v4.app.Fragment implements TextInput
{
private EditText mEditText;
@Override
public View onCreateView(LayoutInflater
inflater, ViewGroup container,
Bundle
savedInstanceState) {
View rootView =
inflater.inflate(R.layout.edit_text, container, false);
mEditText = (EditText)
rootView.findViewById(R.id.text);
if
(getArguments().containsKey(Constants.ARG_TEXT_FILE)){
mEditText.setText(getArguments().getString(Constants.ARG_TEXT_FILE));
}
return rootView;
}
@Override
public CharSequence getText() {
return mEditText.toString();
}
}
Y nuestra actividad quedara como se muestra a continuación,
noten que se llama a la interfaz para obtener el texto del usuario no se llama
directamente a la actividad lo cual es posible pero no optamos seguir ese
camino.
public
class NoteActivity extends FragmentActivity implements
ActionBar.OnNavigationListener {
/**
* The serialization (saved instance state)
Bundle key representing the
* current dropdown position.
*/
private static final String
STATE_SELECTED_NAVIGATION_ITEM = "selected_navigation_item";
private TextFile mTextFile;
private TextInput mTextInput;
private final static String TAG =
"NotePadRoque";
@Override
protected void onCreate(Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_note);
// Set up the action bar to show a
dropdown list.
final ActionBar actionBar =
getActionBar();
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
// Set up the dropdown list navigation
in the action bar.
actionBar.setListNavigationCallbacks(
// Specify a SpinnerAdapter to
populate the dropdown list.
new ArrayAdapter<String>(
getActionBarThemedContextCompat(),
android.R.layout.simple_list_item_1,
android.R.id.text1,
new String[] {
getString(R.string.title_section1),
getString(R.string.title_section2),
getString(R.string.title_section3),
}),
this);
mTextFile = new TextFile(this);
try {
mTextFile.createSDFile("test.txt", "Hello World");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Backward-compatible version of {@link
ActionBar#getThemedContext()} that
* simply returns the {@link
android.app.Activity} if
*
<code>getThemedContext</code> is unavailable.
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private Context
getActionBarThemedContextCompat() {
if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return
getActionBar().getThemedContext();
} else {
return this;
}
}
@Override
public void onRestoreInstanceState(Bundle
savedInstanceState) {
// Restore the previously serialized
current dropdown position.
if
(savedInstanceState.containsKey(STATE_SELECTED_NAVIGATION_ITEM)) {
getActionBar().setSelectedNavigationItem(
savedInstanceState.getInt(STATE_SELECTED_NAVIGATION_ITEM));
}
}
@Override
public void onSaveInstanceState(Bundle
outState) {
// Serialize the current dropdown
position.
outState.putInt(STATE_SELECTED_NAVIGATION_ITEM,
getActionBar().getSelectedNavigationIndex());
}
@Override
public boolean onCreateOptionsMenu(Menu
menu) {
// Inflate the menu; this adds items to
the action bar if it is present.
getMenuInflater().inflate(R.menu.note,
menu);
return true;
}
@Override
public boolean
onOptionsItemSelected(MenuItem item){
switch(item.getItemId()){
case R.id.action_save:
try
{
mTextFile.createSDFile("test.txt",
mTextInput.getText().toString());
}
catch (IOException ioex)
{
Toast.makeText(this,
ioex.getMessage(), Toast.LENGTH_SHORT).show();
}
return true;
}
return false;
}
@Override
public boolean onNavigationItemSelected(int
position, long id) {
// When the given dropdown item is
selected, show its contents in the
// container view.
Fragment fragment;
Bundle args = new Bundle();
switch (position){
case 0:{
fragment = new EditTextFragment();
String content = null;
try {
content =
mTextFile.getSDFileContent("test.txt");
} catch (IOException e) {
Log.w(TAG, e.getMessage());
}
args.putString(Constants.ARG_TEXT_FILE, content);
break;
}
default:{
fragment = new
DummySectionFragment();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
break;
}
}
fragment.setArguments(args);
getSupportFragmentManager().beginTransaction()
.replace(R.id.container,
fragment)
.commit();
mTextInput = (TextInput) fragment;
return true;
}
Asi mismo hay que agregar el permiso de escritura externa en
nuestro Android manifest
<?xml
version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.roque.rueda.notepad"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="14"
/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".NoteActivity"
android:label="@string/app_name" >
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Y ahora vamos a ejecutar nuestra aplicacion.
No hay comentarios:
Publicar un comentario