I used the code posted in this answer to try and set my preferences a different style (namely the font typeface and size), and it does indeed set the font properly. The problem with this code is, it extends the base Preference class, so I cannot create specific preferences (ListPreference, CheckboxPreference, etc.) and I'm stuck with basic preference objects, which I don't even know whether they have any proper use in terms of user interaction.
Now I could extend every Preference class I use to include the code in CustomPreference, but that seems like bad practice to me. Since there's no multiple inheritance in Java, is there any solution (perhaps any OO workaround) that can add this styling functionality to my CheckBoxPreferences, ListPreferences, PreferenceScreen, etc.?
You could use a Util-type class with public static methods. You'd have to extend each preference class you use still, but you could defer things like setStyleAlarmed to the Utils class instead of having the same code in each custom preference class.
public class PreferenceUtils {
public static void onBindView(View view) {
switch (style) {
case STYLE_ALARMED:
setStyleAlarmed(view);
break;
case STYLE_NORMAL:
setStyleNormal(view);
break;
case STYLE_WARNING:
setStyleWarning(view);
break;
//...
}
}
// Move other methods here, e.g. setStyleAlarmed().
}
public class CustomPreference extends Preference implements PreferenceStyle {
#Override
protected void onBindView(View view) {
super.onBindView(view);
PreferenceUtils.onBindView(view);
}
}
Related
We are currently migrating to Androidx namespace with our Android app project. However I noticed that not only the namespace seems to have changed. For DialogPreference also some interfaces which were using before are now missing
new interfaces: https://developer.android.com/reference/androidx/preference/DialogPreference
old interfaces: https://developer.android.com/reference/kotlin/android/preference/DialogPreference
For example the following methods seem to be missing: onBindDialogView, showDialog, onDialogClosed.
Since we use some of these methods to influence the default behavior of the dialog, it is unclear to me how I should realize this functionality now. For example we are validating the input before closing the dialog, we are saving the value in a database instead of the sharedpreferences and adding some dynamic elements to the dialog.
Has anyone else already encountered this problem and found a solution? Did I miss anything in the documentation? Is there another concept that we can / should use?
It would be possible to use Fragments instead of DialogPreference but for small amounts of content (e.g. a list of tree items, where the user can choose from) this seems to be a lot of overhead for me...
Starting from androidx source files, I've migrated custom classes based on old DialogPreference to new androidx.preference.DialogPreference with the following procedure:
Step 1
The old custom dialog class (e.g. CustomDialogPreference) based on legacy DialogPreference should be split into two separate classes:
One class (e.g. CustomPreference) should extend androidx.preference.DialogPreference and will contain only the code related to preference handling (data management).
Another class (e.g. CustomDialog) should extend androidx.preference.PreferenceDialogFragmentCompat and will contain only the code related to dialog handling (user interface), including onDialogClosed. This class should expose a static method newInstance to return an instance of this class.
Step 2
In the main fragment handling preferences based on PreferenceFragmentCompat the onDisplayPreferenceDialog method should be overridden to show the custom dialog, e.g.:
private static final String DIALOG_FRAGMENT_TAG = "CustomPreference";
#Override
public void onDisplayPreferenceDialog(Preference preference) {
if (getParentFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
return;
}
if (preference instanceof CustomPreference) {
final DialogFragment f = CustomDialog.newInstance(preference.getKey());
f.setTargetFragment(this, 0);
f.show(getParentFragmentManager(), DIALOG_FRAGMENT_TAG);
} else {
super.onDisplayPreferenceDialog(preference);
}
}
Instead of using DialogPreference, you can write your own custom Preference with an AlertDialog.
This may be a workaround for those who don't want to deal with the DialogPreference and PreferenceDialogFragmentCompat.
import android.content.Context;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
public class CustomDialogPreference extends Preference {
private final Context context;
public CustomDialogPreference(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
#Override
protected void onClick() { //what happens when clicked on the preference
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("TITLE")
.setMessage("message")
.setPositiveButton("OK", (dialog, which) -> {
String preferenceKey = getKey(); //You can update the SharedPreference with the key
//....
})
.setNegativeButton("CANCEL", (dialog, which) -> {
//....
})
.create().show();
}
}
onClick() and getKey() methods belong to the Preference class. The context object comes with the constructor and so on..
The key can be defined, as other preferences, in xml file or programmatically in the PreferenceFragment.
<com.myApp.CustomDialogPreference
android:key="my_preference_key"
android:summary="..."
android:title="..." />
A little hack for everyone who (like me) do not completely understand how we should combine androidx.preference.DialogPreference and androidx.preference.PreferenceDialogFragmentCompat:
Step 1:
In your DialogFragment's onAttach() method get the value of your desired SharedPreference (get the key either from newInstance() method or just hardcore it inside) and save it as a variable. On the other hand, save your new value in SharedPreference before closing your DialogFragment. By doing so, you have created your "custom Preference".
Step 2:
Create empty androidx.preference.DialogPreference and use it inside your PreferenceScreen. Then combine it with your DialogFragment as described in 2nd step by #Livio:
private static final String DIALOG_FRAGMENT_TAG = "CustomPreference";
#Override
public void onDisplayPreferenceDialog(Preference preference) {
if (getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
return;
}
if (preference instanceof CustomPreference) {
final DialogFragment f = CustomDialog.newInstance(preference.getKey());
f.setTargetFragment(this, 0);
f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
} else {
super.onDisplayPreferenceDialog(preference);
}
}
By doing so, you will get the same result with only difference that you need to deal with SharedPreference yourself inside your DialogFragment.
I seem to be stuck with a problem with an object communicating with my activity class. The object is a view object with an onClick method that when called I would like it to notify my activity class so that it can perform said action. Below is some example code of my situation (assume all conventional setup operations have already been made):
public class MainActivity extends AppCompatActivity{
//...other global methods and objects
//Does not have access to instantiated Entry object(s)
public void entryObjectWasClicked(){
//perform said action
}
}
public class Entry extends View implements View.OnClickListener{
//...other global methods and objects
//Does not have access to the MainActivity object
#Override
public void onClick(View v){
//send a message to the MainActivity to
//somehow call the entryObjectWasClicked() method
}
}
The only way (off the top of my head) that I could think about dealing with this problem is by creating a static method in MainActivity and then calling it from an anonymous MainActivity object in the onClick method of Entry. The problem with the static method approach is that any subsequent method/object/primitive usages in the static method force those methods/objects/primitives to be static. This defeats the purpose of then being able to have two different instances of the MainActivity object.
After some looking I came across using Broadcast messages, specifically using the LocalBroadcastManager to send an intent to the activity. This code example works for my model, but I want to know: is this the best way for me to go about sending messages to my MainActivity from my Entry object?
If there is a more effective way of doing all this, what would it be?
You're overcomplicating things. Don't override onClick for this. Instead, have your activity call setOnClickHandler on your view, which sets a callback that's called when the view is clicked. Then use the default implementation.
Since you extend view, i guess you want to use it inside a layout. That means you may want to create a Listener for that. Example:
public class Entry extends View implements View.OnClickListener{
private OnClickListener listener;
public void setListener(OnClickListener listener) {
this.listener = listener;
}
#Override
public void onClick(){
if (this.listener != null) this.listener.onClick(this);
}
}
How you can inflate your layout in your Activity and access your custom view.
public class MainActivity extends AppCompatActivity{
public void onCreate( ...) {
Entry entry = findViewById(R.id.entry);
entry.setListener(new OnClickListener(...));
}
}
Edit: No, this is not a duplicate for the given link is asking for the comparison of setOnClickListener and android:onClick. I'm not even asking for a comparison, but I'm asking about the advantage of having an implementation of View.OnClickListener.
Please be free to vote to re-open.
Many people, by preference, use
public class TrumpLocator extends Clinton implements View.onClickListener{
#Override
public void onClick(View v){
//...
}
}
However, if I'm not mistaken, either way, on your Button you have to do:
android:onClick="onClick"
But, again if I'm not mistaken, if we don't override onClick and implement View.onClickListener, we will achieve the same effect:
//no override and no "implements onClickListener"
public void onClick(View v){
//...
}
and
android:onClick="onClick"
So, is there any advantage of implementing the method over simply applying the click listener? Isn't it just a waste of code?
either way, on your Button you have to do:
android:onClick="onClick"
No, this isn't required.
I think you have it backwards maybe. By having android:onClick="onClick", you need a public void method with that name in the quotes.
public void onClick(View v){
//...
}
This is similar to implementing the interface, but not the exact same. In other words, it could just as well be android:onClick="handleClick", then you need
public void handleClick(View v){
//...
}
People may not prefer this because it can lead to typos and uncertainty where a click listener is attached.
Now, the Activity does not need to implement the interface itself, you can attach anonymous class listeners to the views individually.
I'm using common code in my Activity like this:
abstract class CommonCode extends Activity {
//Common Code here...
}
then in my "Activity" I extend CommonCode instead of Activity and it all works fine.
My problem arise when I try to use commoncode in a PreferenceActivity, I tried:
abstract class CommonCode extends Activity {
class CommonCodePreferences extends PreferenceActivity {
}
//Common Code here...
}
but it isn't right.
How can I do it?
May I suggest that you prefer composition over inheritance and do something like this:
abstract class CommonCode {
Activity parent;
public CommonCode(Activity activity) {
parent = activity;
}
}
class MyActivity extends Activity {
CommonCode commonCode;
public MyActivity() {
commonCode = new CommonCode(this);
}
}
This is a little more code to write in each activity, but it has a lot of advantages:
It can also easily handle PreferenceActivity and other classes
It is easier to test and mock
I usually have one each since you can't mess with the existing hierarchy of the base classes.
For example, I have an ActivityBase, ServiceBase, ListActivityBase, etc. If you want to have common code that they all use, I would suggest using composition - each of your base classes has a single instance of your CommonCode class or something to that effect. Another possibility is to use static methods and/or use a custom Application class (requires declaring the custom Application class in the manifest in the name attribute of the application element)
For every Activity I add to my app I'm noticing a lot of similar code being used in the initialization of the Activity. A helper class with a static method to wrap this similar code seems the way to go.
I first thought of a singleton class. I could add static methods/variables and use them across the application. I haven't really tried to see how would this work in an Android application. Searching a little bit more I saw something about creating a class extending Application. For this I did a simple test:
public class MyApp extends Application {
public static String DEMOTEXT = "WORKING!";
public static void ShowToast(Context context, String text) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
}
}
MyApp.ShowToast(this, MyApp.DEMOTEXT); // Placed on onCreate of some Activity
This works exactly as I expected. Is this the way to go on Android or is there a better convention? Anything else I should consider when doing this?
By the way, should I use the final keyword on the string? What about the method?
EDIT: I just read this:
There is normally no need to subclass Application. In most situation,
static singletons can provide the same functionality in a more modular
way. If your singleton needs a global context (for example to register
broadcast receivers), the function to retrieve it can be given a
Context which internally uses Context.getApplicationContext() when
first constructing the singleton.
http://developer.android.com/reference/android/app/Application.html
Should I use a singleton then?
Application is primarily used for a global application initialization. You would create your own class, override Application.onCreate() and initialize your static application data there.
Dont forget to declare it in the AndroidMainfest.xml:
<application
android:icon="#drawable/icon"
android:label="#string/app_name"
android:name="your.package.path.to.MyApp">
A static helper class is made the way you did.
The convention is to use lower case letter at first position, so MyApp.showToast(...).
You would use final for the String if you would want to avoid madifications on other places (since it should be a contant).
// this would allow ...
public static String DEMOTEXT = "WORKING!";
// ... to do this somewhere else
MyApp.DEMOTEXT = "NOT WORKING!"
I haven't tried this but I think you should be able to do something like this as well.
public class MyActivity extends Activity {
private static final String DEMOTEXT = "WORKING!";
#Override
public void onCreate(Bundle bundle)
{
super.onCreate(bundle);
Toast.makeText(this, DEMOTEXT, Toast.LENGTH_SHORT).show();
}
}
Now for all activities that need to use that initialization could just extend your base activity class.
public class SomeActivity extends MyActivity {
...
// Should display the toast on create
...
}
Yes just use a singleton. Well in this case if your methods are static, you don't even need a singleton. Just a class with static methods.