Do stworzenia tego poradnika użyłem
1. Android Studio 3.1.3
2. Java w wersji 8
3. Min SKD 22
4. Kotlin
5. Kotlin Android Extensions
Pokazuje on w jaki sposób użyć SharedPreferences aby przekazać dane z activity do klasy konfigurującej widget.
Na początku mamy stworzony projekt z pustą aktywnością. Klikamy prawym przyciskiem na nazwę naszego pakietu i wybieramy: New -> Widget -> App Widget
W nowym oknie wybieramy nazwę widgetu i podstawowe informacje na jego temat.
Zaznaczamy Configuration Screen, a resztę opcji możemy pozostawić bez zmian. Po kliknięciu Finish doda kilka nowych plików do projektu. W tym przypadku będą to klasy: NewAppWidget.kt i NewAppWidgetConfigureActivity.kt oraz dwa odpowiadające im pliki w layout: new_app_widget.xml i new_app_widget_configure.xml
Struktura projektu w tej chwili wygląda następująco:
Jeżeli wybraliśmy język kotlin to w klasie NewAppWidgetConfigureActivity będzie pokazywało nam błąd Property must be initialized or be abstract:
Jest to problem z konwersją kodu javy na kotlin. Niestety to narzędzie nie działa idealnie. Aby szybko poradzić sobie z tym błędem tą linię zmieniamy na:
internal lateinit var mAppWidgetText: EditText
W tej chwili powinniśmy być w stanie uruchomić nasz widget na emulatorze lub na prawdziwym urządzeniu.
Po instalacji apk otworzy nam się aplikacja z napisem "Hello World" (jeżeli wybraliśmy empty activity przy tworzeniu projektu) - możemy ją zamknąć. Przechodzimy na ekran główny. Przytrzymujemy palec lub kliknięcie (w przypadku emulatora ) na głównym ekranie. Pojawia nam się menu do wyboru tapety, widgetu, lub ustawień.
Wybieramy Widgets, zjeżdżamy na sam dół i widzimy nasz widget:
Po przytrzymaniu możemy go przenieść na ekran główny i skonfigurować za pomocą activity NewAppWidgetConfigureActivity.
No i właśnie, aby przekazać dane z naszej aplikacji, a konkretnie z MainActivity musimy:
W activity_main.xml utworzyć layout z EditText i Button:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Dodaj do konfiguracji" />
<EditText
android:id="@+id/config_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:inputType="text"
app:layout_constraintTop_toBottomOf="@+id/textView2"/>
<Button
android:id="@+id/add_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Dodaj"
app:layout_constraintTop_toBottomOf="@+id/config_text" />
</android.support.constraint.ConstraintLayout>
Następnie w samej MainActivity:
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
add_button.setOnClickListener { save(this) }
}
val PREFS_FROM_MAIN = "OPEN_IN_CONFIG_WIDGET"
val PREFS_MAIN_KEY = "PREFS_String"
fun save(context: Context) {
val configText = config_text.text
val settings = context.getSharedPreferences(PREFS_FROM_MAIN, Context.MODE_PRIVATE)
val editor: SharedPreferences.Editor
editor = settings.edit()
editor.putString(PREFS_MAIN_KEY, configText.toString())
editor.apply()
}
}
za pomocą metody save(context: Context) uruchamianej po kliknięciu na add_button (ma ustawionego listenera w onCreate: add_button.setOnClickListener { save(this) } ) do SharedPreferences dodawany jest text z EditText o nazwie config_text.
Kolejnym krokiem jest odebranie tego tekstu w NewAppWidgetConfigureActivity. Tutaj wystarczy zmodyfikować metodę OnCreate:
//pola pozwalające dobrać się do zapisanego stringa
val PREFS_FROM_MAIN = "OPEN_IN_CONFIG_WIDGET"
val PREFS_MAIN_KEY = "PREFS_String"
public override fun onCreate(icicle: Bundle?) {
super.onCreate(icicle)
// Set the result to CANCELED. This will cause the widget host to cancel
// out of the widget placement if the user presses the back button.
setResult(Activity.RESULT_CANCELED)
setContentView(R.layout.new_app_widget_configure)
mAppWidgetText = findViewById<View>(R.id.appwidget_text) as EditText
findViewById<View>(R.id.add_button).setOnClickListener(mOnClickListener)
// Find the widget id from the intent.
val intent = intent
val extras = intent.extras
if (extras != null) {
mAppWidgetId = extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID)
}
// If this activity was started with an intent without an app widget ID, finish with an error.
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
finish()
return
}
//Pobranie zapisanej wartości
val settings: SharedPreferences = this.getSharedPreferences(PREFS_FROM_MAIN, Context.MODE_PRIVATE)
val textFromMain: String?
textFromMain = settings.getString(PREFS_MAIN_KEY, null)
if (textFromMain.isNullOrEmpty())
mAppWidgetText.setText(loadTitlePref(this@NewAppWidgetConfigureActivity, mAppWidgetId))
else
mAppWidgetText.setText(textFromMain)
}
Gotowe! Teraz po odpaleniu aplikacji widzimy (wpisałem już tekst: ANDROID.COM.PL ?
Po kliknięciu dodaj przechodzimy do wyboru widgetu. Przeciągamy go na ekran główny i widzimy:
Nasz tekst został przekazany do activity konfigurującej widget! Klikamy ADD WIDGET i na ekranie głównym dodaje nam się widget z naszym tekstem:
dodana zawartość
Ten tutorial pokazał w jaki sposób przekazywać za pomocą SharedPreferences typ prosty jakim jest String. Aby przekazywać bardziej złożone obiekty należy posłużyć się biblioteką Gson aby zamienić object na json:
val settings = context.getSharedPreferences(PREFS_FROM_MAIN, Context.MODE_PRIVATE)
val editor: SharedPreferences.Editor
val myObject = MyObject()
editor = settings.edit()
val gson = Gson()
val json = gson.toJson(MyObject)
editor.putString(PREFS_MAIN_KEY, json)
editor.apply()
Odbieranie obiektu w analogiczny sposób:
val gson = Gson()
val json = editor.getString(PREFS_MAIN_KEY, "")
val obj = gson.fromJson(json, MyObject::class.java)