SharedPreferences en Android

SharedPreferences en Android

Hola amigos 馃憢 Bienvenidos a un nuevo tutorial de Universo Android. Hoy aprenderemos a usar SharedPreferences, el sistema de almacenamiento local m谩s simple y eficiente para guardar datos peque帽os en formato clave-valor.

Al finalizar este tutorial tendr谩s una aplicaci贸n que incluir谩:

  • Guardar y leer datos simples
  • Almacenar diferentes tipos de datos
  • Preferencias de usuario
  • Sistema de login persistente
  • Configuraciones de la app
  • Modo oscuro/claro
  • Limpiar datos almacenados

馃煩 1. ¿Qu茅 es SharedPreferences?

SharedPreferences es un sistema de almacenamiento que guarda datos en formato clave-valor en archivos XML. Es ideal para configuraciones, preferencias de usuario y datos peque帽os que necesitan persistir.

馃煩 2. Tipos de Datos Soportados

  • String
  • int
  • long
  • float
  • boolean
  • Set<String>

馃煩 3. Crear el Proyecto

Creamos un nuevo proyecto en Android Studio con Empty Activity.

馃煩 4. Clase Helper para SharedPreferences

馃搫 SharedPreferencesManager.java

import android.content.Context;
import android.content.SharedPreferences;

public class SharedPreferencesManager {

    private static final String PREF_NAME = "MyAppPreferences";
    private static final String KEY_IS_LOGGED_IN = "isLoggedIn";
    private static final String KEY_USER_NAME = "userName";
    private static final String KEY_USER_EMAIL = "userEmail";
    private static final String KEY_USER_ID = "userId";
    private static final String KEY_DARK_MODE = "darkMode";
    private static final String KEY_NOTIFICATIONS = "notifications";
    private static final String KEY_LANGUAGE = "language";
    private static final String KEY_FIRST_TIME = "firstTime";

    private SharedPreferences prefs;
    private SharedPreferences.Editor editor;

    public SharedPreferencesManager(Context context) {
        prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
        editor = prefs.edit();
    }

    // Guardar login
    public void saveLoginData(String userName, String email, int userId) {
        editor.putBoolean(KEY_IS_LOGGED_IN, true);
        editor.putString(KEY_USER_NAME, userName);
        editor.putString(KEY_USER_EMAIL, email);
        editor.putInt(KEY_USER_ID, userId);
        editor.apply();
    }

    // Verificar si est谩 logueado
    public boolean isLoggedIn() {
        return prefs.getBoolean(KEY_IS_LOGGED_IN, false);
    }

    // Obtener nombre de usuario
    public String getUserName() {
        return prefs.getString(KEY_USER_NAME, "");
    }

    // Obtener email
    public String getUserEmail() {
        return prefs.getString(KEY_USER_EMAIL, "");
    }

    // Obtener ID de usuario
    public int getUserId() {
        return prefs.getInt(KEY_USER_ID, -1);
    }

    // Guardar modo oscuro
    public void setDarkMode(boolean enabled) {
        editor.putBoolean(KEY_DARK_MODE, enabled);
        editor.apply();
    }

    // Obtener estado modo oscuro
    public boolean isDarkMode() {
        return prefs.getBoolean(KEY_DARK_MODE, false);
    }

    // Guardar notificaciones
    public void setNotifications(boolean enabled) {
        editor.putBoolean(KEY_NOTIFICATIONS, enabled);
        editor.apply();
    }

    // Obtener estado notificaciones
    public boolean isNotificationsEnabled() {
        return prefs.getBoolean(KEY_NOTIFICATIONS, true);
    }

    // Guardar idioma
    public void setLanguage(String language) {
        editor.putString(KEY_LANGUAGE, language);
        editor.apply();
    }

    // Obtener idioma
    public String getLanguage() {
        return prefs.getString(KEY_LANGUAGE, "es");
    }

    // Primera vez
    public boolean isFirstTime() {
        return prefs.getBoolean(KEY_FIRST_TIME, true);
    }

    // Marcar como no primera vez
    public void setFirstTime(boolean firstTime) {
        editor.putBoolean(KEY_FIRST_TIME, firstTime);
        editor.apply();
    }

    // Cerrar sesi贸n
    public void logout() {
        editor.putBoolean(KEY_IS_LOGGED_IN, false);
        editor.remove(KEY_USER_NAME);
        editor.remove(KEY_USER_EMAIL);
        editor.remove(KEY_USER_ID);
        editor.apply();
    }

    // Limpiar todas las preferencias
    public void clearAll() {
        editor.clear();
        editor.apply();
    }
}

馃煩 5. Dise帽o XML Principal

馃搫 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="SharedPreferences Demo"
            android:textSize="24sp"
            android:textStyle="bold"
            android:textColor="#2196F3"
            android:layout_marginBottom="24dp" />

        <!-- Secci贸n de Login -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Datos de Usuario"
            android:textSize="18sp"
            android:textStyle="bold"
            android:layout_marginBottom="16dp" />

        <EditText
            android:id="@+id/txtUserName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Nombre de usuario"
            android:padding="12dp"
            android:background="@drawable/edittext_background" />

        <EditText
            android:id="@+id/txtEmail"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Email"
            android:inputType="textEmailAddress"
            android:padding="12dp"
            android:background="@drawable/edittext_background"
            android:layout_marginTop="12dp" />

        <Button
            android:id="@+id/btnSave"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Guardar Datos"
            android:layout_marginTop="16dp" />

        <Button
            android:id="@+id/btnLoad"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Cargar Datos"
            android:layout_marginTop="8dp" />

        <!-- Separador -->
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#CCCCCC"
            android:layout_marginTop="24dp"
            android:layout_marginBottom="24dp" />

        <!-- Configuraciones -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Configuraciones"
            android:textSize="18sp"
            android:textStyle="bold"
            android:layout_marginBottom="16dp" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_vertical">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Modo Oscuro"
                android:textSize="16sp" />

            <Switch
                android:id="@+id/switchDarkMode"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            android:layout_marginTop="16dp">

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Notificaciones"
                android:textSize="16sp" />

            <Switch
                android:id="@+id/switchNotifications"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

        </LinearLayout>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Idioma"
            android:textSize="16sp"
            android:layout_marginTop="24dp"
            android:layout_marginBottom="8dp" />

        <Spinner
            android:id="@+id/spinnerLanguage"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="12dp" />

        <!-- Separador -->
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#CCCCCC"
            android:layout_marginTop="24dp"
            android:layout_marginBottom="24dp" />

        <!-- Informaci贸n almacenada -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Datos Almacenados"
            android:textSize="18sp"
            android:textStyle="bold"
            android:layout_marginBottom="16dp" />

        <TextView
            android:id="@+id/txtStoredData"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="No hay datos almacenados"
            android:padding="16dp"
            android:background="#F5F5F5"
            android:textSize="14sp" />

        <!-- Botones de acci贸n -->
        <Button
            android:id="@+id/btnLogout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Cerrar Sesi贸n"
            android:backgroundTint="#FF9800"
            android:layout_marginTop="24dp" />

        <Button
            android:id="@+id/btnClearAll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Limpiar Todo"
            android:backgroundTint="#F44336"
            android:layout_marginTop="8dp" />

    </LinearLayout>

</ScrollView>

馃煩 6. Crear Drawable para EditText

馃搫 res/drawable/edittext_background.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#FFFFFF" />
    <corners android:radius="8dp" />
    <stroke android:width="1dp" android:color="#CCCCCC" />
    <padding android:left="12dp" android:top="12dp" 
             android:right="12dp" android:bottom="12dp" />
</shape>

馃煩 7. MainActivity

馃搫 MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    EditText txtUserName, txtEmail;
    Switch switchDarkMode, switchNotifications;
    Spinner spinnerLanguage;
    TextView txtStoredData;
    Button btnSave, btnLoad, btnLogout, btnClearAll;

    SharedPreferencesManager prefsManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        prefsManager = new SharedPreferencesManager(this);

        initializeViews();
        setupLanguageSpinner();
        loadStoredData();
        setupListeners();
    }

    private void initializeViews() {
        txtUserName = findViewById(R.id.txtUserName);
        txtEmail = findViewById(R.id.txtEmail);
        switchDarkMode = findViewById(R.id.switchDarkMode);
        switchNotifications = findViewById(R.id.switchNotifications);
        spinnerLanguage = findViewById(R.id.spinnerLanguage);
        txtStoredData = findViewById(R.id.txtStoredData);
        btnSave = findViewById(R.id.btnSave);
        btnLoad = findViewById(R.id.btnLoad);
        btnLogout = findViewById(R.id.btnLogout);
        btnClearAll = findViewById(R.id.btnClearAll);
    }

    private void setupLanguageSpinner() {
        String[] languages = {"Espa帽ol", "English", "Portugu锚s", "Fran莽ais"};
        ArrayAdapter<String> adapter = new ArrayAdapter<>(
            this, 
            android.R.layout.simple_spinner_item, 
            languages
        );
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinnerLanguage.setAdapter(adapter);

        // Cargar idioma guardado
        String savedLanguage = prefsManager.getLanguage();
        int position = getLanguagePosition(savedLanguage);
        spinnerLanguage.setSelection(position);
    }

    private int getLanguagePosition(String language) {
        switch (language) {
            case "es": return 0;
            case "en": return 1;
            case "pt": return 2;
            case "fr": return 3;
            default: return 0;
        }
    }

    private String getLanguageCode(int position) {
        switch (position) {
            case 0: return "es";
            case 1: return "en";
            case 2: return "pt";
            case 3: return "fr";
            default: return "es";
        }
    }

    private void loadStoredData() {
        // Cargar switches
        switchDarkMode.setChecked(prefsManager.isDarkMode());
        switchNotifications.setChecked(prefsManager.isNotificationsEnabled());

        // Mostrar datos almacenados
        displayStoredData();
    }

    private void setupListeners() {
        // Bot贸n Guardar
        btnSave.setOnClickListener(v -> saveUserData());

        // Bot贸n Cargar
        btnLoad.setOnClickListener(v -> loadUserData());

        // Switch Modo Oscuro
        switchDarkMode.setOnCheckedChangeListener((buttonView, isChecked) -> {
            prefsManager.setDarkMode(isChecked);
            Toast.makeText(this, 
                "Modo oscuro: " + (isChecked ? "Activado" : "Desactivado"), 
                Toast.LENGTH_SHORT).show();
            displayStoredData();
        });

        // Switch Notificaciones
        switchNotifications.setOnCheckedChangeListener((buttonView, isChecked) -> {
            prefsManager.setNotifications(isChecked);
            Toast.makeText(this, 
                "Notificaciones: " + (isChecked ? "Activadas" : "Desactivadas"), 
                Toast.LENGTH_SHORT).show();
            displayStoredData();
        });

        // Spinner Idioma
        spinnerLanguage.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                String languageCode = getLanguageCode(position);
                prefsManager.setLanguage(languageCode);
                displayStoredData();
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {}
        });

        // Bot贸n Cerrar Sesi贸n
        btnLogout.setOnClickListener(v -> logout());

        // Bot贸n Limpiar Todo
        btnClearAll.setOnClickListener(v -> clearAllData());
    }

    private void saveUserData() {
        String userName = txtUserName.getText().toString().trim();
        String email = txtEmail.getText().toString().trim();

        if (userName.isEmpty() || email.isEmpty()) {
            Toast.makeText(this, "Completa todos los campos", Toast.LENGTH_SHORT).show();
            return;
        }

        // Guardar datos (simulando ID de usuario)
        int userId = (int) (Math.random() * 1000);
        prefsManager.saveLoginData(userName, email, userId);

        Toast.makeText(this, "Datos guardados exitosamente", Toast.LENGTH_SHORT).show();
        
        // Limpiar campos
        txtUserName.setText("");
        txtEmail.setText("");

        displayStoredData();
    }

    private void loadUserData() {
        if (!prefsManager.isLoggedIn()) {
            Toast.makeText(this, "No hay datos de usuario guardados", Toast.LENGTH_SHORT).show();
            return;
        }

        String userName = prefsManager.getUserName();
        String email = prefsManager.getUserEmail();

        txtUserName.setText(userName);
        txtEmail.setText(email);

        Toast.makeText(this, "Datos cargados", Toast.LENGTH_SHORT).show();
    }

    private void displayStoredData() {
        StringBuilder data = new StringBuilder();

        if (prefsManager.isLoggedIn()) {
            data.append("馃懁 Usuario Logueado\n\n");
            data.append("Nombre: ").append(prefsManager.getUserName()).append("\n");
            data.append("Email: ").append(prefsManager.getUserEmail()).append("\n");
            data.append("ID: ").append(prefsManager.getUserId()).append("\n\n");
        } else {
            data.append("❌ No hay sesi贸n activa\n\n");
        }

        data.append("⚙️ Configuraciones:\n\n");
        data.append("Modo Oscuro: ").append(prefsManager.isDarkMode() ? "✓" : "✗").append("\n");
        data.append("Notificaciones: ").append(prefsManager.isNotificationsEnabled() ? "✓" : "✗").append("\n");
        data.append("Idioma: ").append(prefsManager.getLanguage()).append("\n");
        data.append("Primera Vez: ").append(prefsManager.isFirstTime() ? "S铆" : "No").append("\n");

        txtStoredData.setText(data.toString());
    }

    private void logout() {
        prefsManager.logout();
        txtUserName.setText("");
        txtEmail.setText("");
        displayStoredData();
        Toast.makeText(this, "Sesi贸n cerrada", Toast.LENGTH_SHORT).show();
    }

    private void clearAllData() {
        prefsManager.clearAll();
        txtUserName.setText("");
        txtEmail.setText("");
        switchDarkMode.setChecked(false);
        switchNotifications.setChecked(true);
        spinnerLanguage.setSelection(0);
        displayStoredData();
        Toast.makeText(this, "Todos los datos eliminados", Toast.LENGTH_SHORT).show();
    }
}

馃煩 8. Ejemplo de Uso Directo

// Guardar datos
SharedPreferences prefs = getSharedPreferences("MiApp", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("nombre", "Juan");
editor.putInt("edad", 25);
editor.putBoolean("premium", true);
editor.apply(); // o editor.commit()

// Leer datos
String nombre = prefs.getString("nombre", "");
int edad = prefs.getInt("edad", 0);
boolean premium = prefs.getBoolean("premium", false);

// Eliminar un dato
editor.remove("nombre");
editor.apply();

// Limpiar todo
editor.clear();
editor.apply();

馃煩 9. Diferencia entre apply() y commit()

M茅todoDescripci贸n
apply()As铆ncrono, m谩s r谩pido, no retorna valor
commit()S铆ncrono, bloquea thread, retorna boolean

馃煩 10. M茅todos Principales

M茅todoDescripci贸n
putString()Guarda String
putInt()Guarda int
putBoolean()Guarda boolean
getString()Lee String
getInt()Lee int
getBoolean()Lee boolean
remove()Elimina clave
clear()Elimina todo
contains()Verifica si existe

▶️ C贸mo Ejecutar

  1. Abre Android Studio
  2. Presiona Run
  3. Guarda datos y cierra la app
  4. 脕brela nuevamente y verifica que los datos persisten

馃И Resultado Final

Aplicaci贸n que guarda preferencias, configuraciones de usuario y mantiene sesi贸n activa incluso despu茅s de cerrar la app.

馃摜 Descargar Proyecto

馃憠 

馃檶 Gracias por Visitar mi Blog

✔️ Comp谩rtelo
✔️ D茅jame un comentario
✔️ S铆gueme para m谩s contenido

❓ Preguntas Frecuentes

1. ¿Qu茅 es SharedPreferences?
Sistema de almacenamiento local en formato clave-valor para datos peque帽os y configuraciones.

2. ¿Es seguro para contrase帽as?
No. Para datos sensibles usa EncryptedSharedPreferences o Room Database.

3. ¿Cu谩ndo usar apply() o commit()?
Usa apply() generalmente (m谩s r谩pido). Usa commit() solo si necesitas el resultado booleano.

4. ¿D贸nde se guardan los datos?
En archivos XML en /data/data/paquete/shared_prefs/

No hay comentarios:

Publicar un comentario