I just noticed the fact that the method addPreferencesFromResource(int preferencesResId) is marked deprecated in Android's documentation (Reference Entry).
Unfortunately, no alternative method is provided in the method's description.
Which method should be used instead in order to connect a preferenceScreen.xml to the matching PreferenceActivity?
No alternative method is provided in the method's description because the preferred approach (as of API level 11) is to instantiate PreferenceFragment objects to load your preferences from a resource file. See the sample code here: PreferenceActivity
To add more information to the correct answer above, after reading an example from Android-er I found you can easily convert your preference activity into a preference fragment. If you have the following activity:
public class MyPreferenceActivity extends PreferenceActivity
{
#Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.my_preference_screen);
}
}
The only changes you have to make is to create an internal fragment class, move the addPreferencesFromResources() into the fragment, and invoke the fragment from the activity, like this:
public class MyPreferenceActivity extends PreferenceActivity
{
#Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
}
public static class MyPreferenceFragment extends PreferenceFragment
{
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.my_preference_screen);
}
}
}
There may be other subtleties to making more complex preferences from fragments; if so, I hope someone notes them here.
#Garret Wilson Thank you so much! As a noob to android coding, I've been stuck with the preferences incompatibility issue for so many hours, and I find it so disappointing they deprecated the use of some methods/approaches for new ones that aren't supported by the older APIs thus having to resort to all sorts of workarounds to make your app work in a wide range of devices. It's really frustrating!
Your class is great, for it allows you to keep working in new APIs wih preferences the way it used to be, but it's not backward compatible. Since I'm trying to reach a wide range of devices I tinkered with it a bit to make it work in pre API 11 devices as well as in newer APIs:
import android.annotation.TargetApi;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
public class MyPrefsActivity extends PreferenceActivity
{
private static int prefs=R.xml.myprefs;
#Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
try {
getClass().getMethod("getFragmentManager");
AddResourceApi11AndGreater();
} catch (NoSuchMethodException e) { //Api < 11
AddResourceApiLessThan11();
}
}
#SuppressWarnings("deprecation")
protected void AddResourceApiLessThan11()
{
addPreferencesFromResource(prefs);
}
#TargetApi(11)
protected void AddResourceApi11AndGreater()
{
getFragmentManager().beginTransaction().replace(android.R.id.content,
new PF()).commit();
}
#TargetApi(11)
public static class PF extends PreferenceFragment
{
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(MyPrefsActivity.prefs); //outer class
// private members seem to be visible for inner class, and
// making it static made things so much easier
}
}
}
Tested in two emulators (2.2 and 4.2) with success.
Why my code looks so crappy:
I'm a noob to android coding, and I'm not the greatest java fan.
In order to avoid the deprecated warning and to force Eclipse to allow me to compile I had to resort to annotations, but these seem to affect only classes or methods, so I had to move the code onto two new methods to take advantage of this.
I wouldn't like having to write my xml resource id twice anytime I copy&paste the class for a new PreferenceActivity, so I created a new variable to store this value.
I hope this will be useful to somebody else.
P.S.: Sorry for my opinionated views, but when you come new and find such handicaps, you can't help it but to get frustrated!
My approach is very close to Garret Wilson's (thanks, I voted you up ;)
In addition it provides downward compatibility with Android < 3.
I just recognized that my solution is even closer to the one by Kevin Remo. It's just a wee bit cleaner (as it does not rely on the "expection" antipattern).
public class MyPreferenceActivity extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
onCreatePreferenceActivity();
} else {
onCreatePreferenceFragment();
}
}
/**
* Wraps legacy {#link #onCreate(Bundle)} code for Android < 3 (i.e. API lvl
* < 11).
*/
#SuppressWarnings("deprecation")
private void onCreatePreferenceActivity() {
addPreferencesFromResource(R.xml.preferences);
}
/**
* Wraps {#link #onCreate(Bundle)} code for Android >= 3 (i.e. API lvl >=
* 11).
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void onCreatePreferenceFragment() {
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new MyPreferenceFragment ())
.commit();
}
}
For a "real" (but more complex) example see NusicPreferencesActivity and NusicPreferencesFragment.
Instead of exceptions, just use:
if (Build.VERSION.SDK_INT >= 11)
and use
#SuppressLint("NewApi")
to suppress the warnings.
Instead of using a PreferenceActivity to directly load preferences, use an AppCompatActivity or equivalent that loads a PreferenceFragmentCompat that loads your preferences. It's part of the support library (now Android Jetpack) and provides compatibility back to API 14.
In your build.gradle, add a dependency for the preference support library:
dependencies {
// ...
implementation "androidx.preference:preference:1.0.0-alpha1"
}
Note: We're going to assume you have your preferences XML already created.
For your activity, create a new activity class. If you're using material themes, you should extend an AppCompatActivity, but you can be flexible with this:
public class MyPreferencesActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_preferences_activity)
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, MyPreferencesFragment())
.commitNow()
}
}
}
Now for the important part: create a fragment that loads your preferences from XML:
public class MyPreferencesFragment extends PreferenceFragmentCompat {
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.my_preferences_fragment); // Your preferences fragment
}
}
For more information, read the Android Developers docs for PreferenceFragmentCompat.
Related
I am beginner student in Java and android studio. I am trying to make a seekbar that gives both negative and positive values. I managed to make a seekbar as shown in the picture [1]: https://i.stack.imgur.com/tJZJN.png). However it start at 0 and finishes at 100. I would like it to start at -50 and finishes at 50. the code I have is right here. Hopfully someone can help .
thank you in advance.
Here is my code
public class MainActivity extends AppCompatActivity {
ArcSeekBar defaultSeekBar, seekBarBackground, gradientSeekBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
defaultSeekBar= (ArcSeekBar) findViewById (R.id.defaultSeekBar );
defaultSeekBar.setOnProgressChangedListener(new ProgressListener() {
#Override
public void invoke(int i) {
Log.d("VALUE",""+i);
}
});
}
}
Wouldn't that be setMin(x) and setMax(x) ? In addition to doing this programmatically you could also apply it within the layout itself.
Refer to (bookmark this site and always explore what APIs exist within the class you are using):
https://developer.android.com/reference/android/widget/AbsSeekBar?hl=en#setMax(int)
PlaylistFragment starts an adapter:
playlistsAdapter = new PlaylistRecyclerAdapter(playlistsListArray, addToPlaylist, mSong, getActivity(), this);
PlaylistRecyclerAdapter binds data to the PlaylistViewHolder, something like this:
((PlaylistViewHolder) viewHolder).bind(this, dataSet.get(position), addToPlaylist, mSong);
User clicks on an item in PlaylistViewHolder:
context.startActivity(PublicPlaylistActivity.createStartIntent(context, playlist));
Now here is the question, how can PublicPlaylistActivity talk back to the initial PlaylistFragment?
I suggest you'd better use Interface from fragment to adapter. So when user clicks anything in adapter, call function realization in fragment. If you need your activity to proceed some operation - ((YourActivity) getActivity()).someMethod() should be called from fragment.
Second trick is using broadcastreceiver to send events. A bit more complicated. You have to launch broadcast in view you need to recive message and send these messages from adapter. This approach is more complexible to debug and support if system is wide spread, so you'd better use interfaces.
There are several ways of doing that. The simplest way should be starting the PublicPlaylistActivity with startActivityForResult. In that way, then the activity finishes, you can set send some data to the caller fragment (which is PlaylistFragment in your case). Here is a nice tutorial about the implementation.
Another way of doing that is by using lifecycle methods. You might have a public static variable which can keep track of some status that you might observe in your onResume function of your PlaylistFragment when you are returning back from your PublicPlaylistActivity. You might consider a sample implementation as follows.
Define a public static variable in your PlaylistFragment. Then in your onResume function check the value of that variable and take actions accordingly.
public static boolean someIndicator = false; // Initialize with a default value
#Override
protected void onResume() {
super.onResume();
if(someIndicator == true) doSomething();
else doSomethingElse();
}
Now you can set the indicator variable from anywhere in your application actually which will have the effect on your PlaylistFragment. For example, from your PublicPlaylistActivity, you might consider doing something like this.
public void someFunctionInYourPublicPlaylistActivity() {
// ...
// Some code and then the following
PlaylistFragment.someIndicator = true;
}
Another way of achieving the same thing is by using a BroadcastReceiver. Here is a tutorial on how you can implement one.
It really depends on how you are structuring your whole activity-fragments communication. Hope that helps!
I would do a common "context" class (ComContext) with an interface. When you create your fragment, you also create this class. And from the activity you can check if it exists or not.
I assume that you already have a helper(AppHelper) class with static variables.
public class AppHelper {
public static ComContext comContext = null;
}
public class MainFragment {
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ConContext comContext = new ComContext();
comContext.listener = this;
AppHelper.comContext = comContext;
}
#Override
public void onDataChanged() {
}
#Override
public void onDestroyView() {
super.onDestroyView();
AppHelper.comContext = null;
}
}
public class MainActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (AppHelper.comContext != null) {
AppHelper.comContext.listener.onDataChanged();
}
}
}
public class ComContext {
public interface HelperListener {
void onDataChanged();
}
public HelperListener listener = null;
}
I'm trying to use a DialogFragment to show a Dialog within my MainActivity. Depending on the user's reaction to the dialog I want to invoke methods defined in my MainActivity.java file (e.g. onActivityResult, but ideally also customized methods).
Following a reply by ashishduh on this question, I defined the DialogFragment as follows (in a seperate java file):
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
public class YesNoDialog extends DialogFragment {
public static final String ARG_TITLE = "YesNoDialog.Title";
public static final String ARG_MESSAGE = "YesNoDialog.Message";
public YesNoDialog() {}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{ Bundle args = getArguments();
String title = args.getString(ARG_TITLE);
String message = args.getString(ARG_MESSAGE);
return new AlertDialog.Builder(getActivity())
.setTitle(title)
.setMessage(message)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null);
}
})
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, null);
}
})
.create();
}
}
Correspondingly, I try to start it from the MainActivity like this:
public void openYesNoDialog (View view) {
DialogFragment dialog = new YesNoDialog();
Bundle args = new Bundle();
args.putString(YesNoDialog.ARG_TITLE, "title");
args.putString(YesNoDialog.ARG_MESSAGE, "message");
dialog.setArguments(args);
dialog.setTargetFragment(this, YES_NO_CALL);
dialog.show(getSupportFragmentManager(), "tag");
}
where openYesNoDialog is triggered by a button in the activity_main.xml layout file.
I am facing the problem that setTargetFragment(this, YES_NO_CALL) is not working, since "this" corresponds to my MainActivity, but setTargetFragment is (naturally)
expecting a Fragment and no Activity. The problem is that I do not know what to reference in the first argument instead because apart from the DialogFragment I
am trying to build I have made absolutely no use of Fragments in my code.
So I am wondering which of the following strategies you would encourage to fix my issue (not even sure if all of them might possibly work):
1.) Use a method similar to setTargetFragment which allows setting a target Activity. (sort of a "setTargetActivity" method; this solution sounds easiest to me if such a thing exists, but I haven't found anything similar yet).
2.) Write everything in terms of Fragments and have something like a "MainFragment" instead of a MainActivity. I could then easily reference this "MainFragment" as a reasonable target fragment with "this".
3.) Use a completely different approach (e.g. not putting the methods in the activity but in an interface both activity and fragment implement, but actually I also want to make use of e.g. TextViews of the activity inside of the DialogFragment, so I think this might be a problem)
I am very thankful for any help.
One final comment: Note that I am using the v4 support libraries in my imports to support backward compatibility as suggested in the Android tutorials on Dialogs.
This is for example why I needed to use getSupportFragmentManager() instead of getFragmentManager() to make work what is already working right now. So that's the reason for my slight modifications of the code I have been referring to with the hyperlink.
getTargetFragment and setTargetFragment both we should use for communication between Fragment to Fragment,
For Activity to Fragment communication, you can use 2 ways
You can use interface for communication
You can use Local broadcast
Interface communication
Create one interface in dialog fragment,
public class YesNoDialog extends DialogFragment {
public interface OnDialogActionListener {
public void onClickDialog();
}
private OnDialogActionListener mListener;
#Override
public void onAttach(Context context) {
mListener = (OnDialogActionListener) context;
}
// Your code
#Override
public void onClick(DialogInterface dialog, int which)
{
mListener.onClickDialog();
}
}
And in Your activity you can implement and override the function, you will get callback in your Activty.
You can simply use interface for the same. Just define interface in a separate class and declare method as onClickEvent/onSuccess according to you and override it in your activity and perform your task in your activity in the method. And call this method from your dialog on yes/no click buttons.
This is my first app and I'm having some trouble.
When I run the app it crashes and I don't know how to fix this error.
public class MainActivity extends AppCompatActivity {\
TextView outputBottom = (TextView)findViewById(R.id.output);
}
public void play_the_big_lie(View view) {
the_big_lie.start();
outputBottom.setText("ObamaCare, the big lie");
}
String literal in setText cannot be translated
This is not an error and you can safely ignore it (unless you need translation in your app).
It is simply a notification by Android Studio that you should be using a string resource file instead of a hard-coded string.
If I had to guess at the source of the issue since you did not post the logcat, it is this.
You can't use findViewById before setContentView because there is no view to "find".
Please try something like the following code
public class MainActivity extends AppCompatActivity {
private TextView outputBottom;
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.activity_main);
outputBottom = (TextView)findViewById(R.id.output);
}
There are two issues here. First, you can't use findViewById until you have "created" things and have a view to find things with, so as the previous answer you want to separate them.
public class MainActivity extends AppCompatActivity {
private TextView outputBottom;
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.activity_main);
outputBottom = (TextView)findViewById(R.id.output);
}
...
}
To set text, it's better not to "hardcode" with a string in quotes, but to create a string file and ask for it, which will look more like this:
outputBottom.setText(R.string.my_words);
Here are the developer notes on strings.
If you're using Android Studio there are tutorials for how to make that happen.
You can ignore these warning by adding
lintOptions {
disable 'MissingTranslation'
}
to the gradle.build file.
I have original code which is given below.But i tried decompiling original apk it gave me this keyword everywhere(shown after this code below) :
public class Aboutt extends Activity {
WebView web;
ProgressBar progressBar;
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aboutt);
web = (WebView) findViewById(R.id.webview01);
if (savedInstanceState != null)
web.restoreState(savedInstanceState);
else
web.loadUrl("http://www.google.com");
progressBar = (ProgressBar) findViewById(R.id.progressBar1);
web.setWebViewClient(new myWebClient());
web.getSettings().setJavaScriptEnabled(true);
web.getSettings().setBuiltInZoomControls(true);
}
But the decompiled code gives me:
public class Aboutt
extends Activity {
ProgressBar progressBar;
WebView web;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_aboutt);
this.web = (WebView)this.findViewById(R.id.webview01));
if (savedInstanceState != null) {
this.web.restoreState(savedInstanceState);
} else {
this.web.loadUrl("http://www.google.com");
}
this.progressBar = (ProgressBar)this.findViewById(R.id.progressBar1);
this.web.setWebViewClient((WebViewClient)new myWebClient());
this.web.getSettings().setJavaScriptEnabled(true);
this.web.getSettings().setBuiltInZoomControls(true);
}
Here it gives this everywhere.
Will it affect working of the app or can i use this everywehere.
The this keyword refers to the current instance of the class i.e. the object that the method is called on.
Usually, this is omitted because if you have something like this:
private int i;
public int getI () { return i}
everyone knows that you are referring to i. If you want to add the this keyword, it's just more wordy and will not affect the compiled code.
Whenever you see your code reference a non-static member, you can add the this suffix. Like in the above example, you can change i to this.i.
However, this does not work in a static method. I mean it will NEVER appear in a static context.
But other than that, this is fine to appear anywhere else.
this is a reference to the current instance of a class. It is mostly used un cases where there are similar names of parameters and local variables (they live as long as the method) and member variables (which live as long as the object).
So while it's not okay to use it everywhere (will not work in static context for instance) you're safe with what the decompiler created.