last - ¿Cómo creo preferencias personalizadas utilizando la biblioteca android.support.v7.preference?
com.android.support:design 26 (2)
Quiero admitir al menos la API 10, quiero poder ajustar mis preferencias a la perfección, quiero tener encabezados (o mostrar PreferenceScreen
s). Parece que PreferenceActivity
, no totalmente compatible con la AppCompat
de AppCompat
, no se ajustará. Así que estoy tratando de usar AppCompatActivity
y PreferenceFragmentCompat
.
public class Prefs extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null)
getSupportFragmentManager().beginTransaction()
.replace(android.R.id.content, new PreferencesFragment())
.commit();
}
public static class PreferencesFragment extends PreferenceFragmentCompat {
@Override public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
@Override
public void onDisplayPreferenceDialog(Preference preference) {
// the following call results in a dialogue being shown
super.onDisplayPreferenceDialog(preference);
}
@Override public void onNavigateToScreen(PreferenceScreen preferenceScreen) {
// I can probably use this to go to to a nested preference screen
// I''m not sure...
}
}
}
Ahora, quiero crear una preferencia personalizada que proporcione la opción de una fuente. Con PreferenceActivity
, simplemente podría hacer
import android.preference.DialogPreference;
public class FontPreference extends DialogPreference {
public FontPreference(Context context, AttributeSet attrs) {super(context, attrs);}
@Override protected void onPrepareDialogBuilder(Builder builder) {
super.onPrepareDialogBuilder(builder);
// do something with builder and make a nice cute dialogue, for example, like this
builder.setSingleChoiceItems(new FontAdapter(), 0, null);
}
}
y usa un xml como este para mostrarlo
<my.app.FontPreference android:title="Choose font" android:summary="Unnecessary summary" />
Pero ahora, no hay onPrepareDialogBuilder
en android.support.v7.preference.DialogPreference
. En su lugar, se ha movido a PreferenceDialogFragmentCompat
. Encontré poca información sobre cómo usar esa cosa, y no estoy seguro de cómo pasar de xml a mostrarla. El fragmento de preferencia v14 tiene el siguiente código:
public void onDisplayPreferenceDialog(Preference preference) {
...
final DialogFragment f;
if (preference instanceof EditTextPreference)
f = EditTextPreferenceDialogFragment.newInstance(preference.getKey());
...
f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
}
Intenté subclasificar android.support.v7.preference.DialogPreference
y hacer que onDisplayPreferenceDialog
use un fragmento de código similar para crear una instancia de FontPreferenceFragment
ficticio, pero falla con la siguiente excepción.
java.lang.IllegalStateException: Target fragment must implement TargetFragment interface
En este punto, ya estoy demasiado metido en el desorden y no quiero seguir investigando. Google no sabe nada acerca de esta excepción. De todos modos, este método parece ser demasiado complicado. Entonces, ¿cuál es la mejor manera de crear preferencias personalizadas utilizando la biblioteca android.support.v7.preference?
Nota importante: actualmente (v23.0.1 de la biblioteca v7) todavía hay muchos problemas de tema con el ''PreferenceThemeOverlay'' (ver este problema ). En Lollipop, por ejemplo, terminas con encabezados de categoría de estilo Holo.
Después de algunas horas frustrantes, finalmente logré crear una Preferencia v7 personalizada. Crear tu propia Preference
parece ser más difícil de lo que crees que es necesario. Así que asegúrate de tomarte un tiempo.
Al principio puede que se esté preguntando por qué encontrará un DialogPreference
y un PreferenceDialogFragmentCompat
para cada tipo de preferencia. Como resultado, la primera es la preferencia real, la segunda es el DialogFragment
donde se DialogFragment
la preferencia. Lamentablemente, se requiere subclasificar a ambos .
No te preocupes, no necesitarás cambiar ningún código. Solo necesitas reubicar algunos métodos:
- Todos los métodos de edición de preferencias (como
setTitle()
osetTitle()
persist*()
) se pueden encontrar en la claseDialogPreference
. - Todos los métodos de diálogo (edición) (
onBindDialogView(View)
yonDialogClosed(boolean)
) se han movido aPreferenceDialogFragmentCompat
.
Es posible que desee que su clase existente extienda la primera, de esa manera no creo que tenga que cambiar mucho. Autocompletar debería ayudarte a encontrar los métodos que faltan.
Cuando haya completado los pasos anteriores, es hora de unir estas dos clases. En su archivo xml, se referirá a la parte de preferencia. Sin embargo, Android aún no sabe qué Fragment
debe inflar cuando debe ser su preferencia personalizada. Por lo tanto, debe anular onDisplayPreferenceDialog(Preference)
:
@Override
public void onDisplayPreferenceDialog(Preference preference) {
DialogFragment fragment;
if (preference instanceof LocationChooserDialog) {
fragment = LocationChooserFragmentCompat.newInstance(preference);
fragment.setTargetFragment(this, 0);
fragment.show(getFragmentManager(),
"android.support.v7.preference.PreferenceFragment.DIALOG");
} else super.onDisplayPreferenceDialog(preference);
}
y también su DialogFragment
necesita manejar la ''clave'':
public static YourPreferenceDialogFragmentCompat newInstance(Preference preference) {
YourPreferenceDialogFragmentCompat fragment = new YourPreferenceDialogFragmentCompat();
Bundle bundle = new Bundle(1);
bundle.putString("key", preference.getKey());
fragment.setArguments(bundle);
return fragment;
}
Eso debería hacer el truco. Si tiene problemas, intente echar un vistazo a las subclases existentes y ver cómo lo resolvió Android (en Android Studio: escriba el nombre de una clase y presione Ctrl + b para ver la clase descompilada). Espero eso ayude.
La excepción se produce cuando su FontPreferenceFragment
no implementa DialogPreference.TargetFragment . Deberá asegurarse de que su fragmento implemente esa interfaz.