Android Preference - replace preference function - android

I have an EditPreference in a PreferenceActivity and I have a variable that tells me if I should allow the user to access this preference or to show some alert.
My problem is that I couldn't find how to cancel the preference dialog before it's displayed and to show my alert (according to the variable).
I tried to return true/false in the preference onClick or in onTreeClick but that didn't do anything, the dialog still popped.
On Android 2.1+ .
Thanks.

The DialogPreference.onClick(), which handles clicks to the preference itself, is protected, so you can't override it in your own PreferenceActivity class members.
However, you can extend the class to achieve what you need. Below is a very minimalist example:
package com.example.test;
import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
public class MyEditTextPreference extends EditTextPreference {
private Runnable alternative = null;
public MyDatePickerDialog(Context context,
AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyDatePickerDialog(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyDatePickerDialog(Context context) {
super(context);
}
public void setAlternativeRunnable(Runnable runnable) {
alternative = runnable;
}
// this will probably handle your needs
#Override
protected void onClick() {
if (alternative == null) super.onClick();
else alternative.run();
}
}
In your XML file:
<com.example.test.MyEditTextPreference
android:key="myCustom"
android:title="Click me!" />
In your PreferenceActivity:
MyEditTextPreference pref = (MyEditTextPreference) this.findPreference("myCustom");
pref.setAlternativeRunnable(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplication(), "Canceled!", Toast.LENGTH_SHORT)
.show();
}
});
As a final note, let me say that whenever you can't find a way to do what you want, think about taking a look at how the Android classes themselves work. Most of the times, they will give you good insights to achieve what you want.
In this case, it's the DialogInterface.onClick() method, as described above. So you know you need to override it somehow to achieve that. In this case, the solution is extending the EditTextPreference class itself.

Related

Preference screen update summary for preference

I implemented a custom preference by extending RingtonePrefernce. I did override getSummary method which dynamically returns the value of pref when pref is updated.
But the problem is on main pref screen the updated value is not reflected when pref is closed.
The only time the pref summay is updated in main screen if i scroll down the main pref screen down/up and when the pref goes out of screen and comes back on screen. So basically when its redrawn.
How do i solve this?
Code for main perf screen which lists all prefs
public class MainActivity extends PreferenceActivity implements
OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
#SuppressLint("Deprecation")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SoundUtils.stopPlaying();
ringtoneManager = new RingtoneManager(getApplicationContext());
featureManager = FeatureManager.getInstance(this);
beepActionManager= new BeepActionManager();
addPreferencesFromResource(R.xml.preferences_settings);
//other code
}
}
And in preferences_settings my pref is added as
<PreferenceCategory android:title="#string/sound_settings">
<com.mindedges.beephourly.utils.CustomRintonePreference
android:defaultValue="content://settings/system/notification_sound"
android:key="ringtone_pref"
android:ringtoneType="all"
android:title="#string/hr_beep_tone_title"
android:summary="#string/hr_beep_tone_summary"/>
<com.mindedges.beephourly.utils.CustomRintonePreference
</PreferenceCategory>
I checked your code and I created test project, found solution.
I think you should notify your custom preference that you changed it.
'RingtonePreference' has callback method called 'onSaveRingtone' and you need to call 'notifyChnaged' function at overriding of 'onSaveRingtone'.
Here is the code I tested.
public class CustomPreference extends RingtonePreference {
public CustomPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomPreference(Context context) {
super(context);
}
#Override
protected void onSaveRingtone(Uri ringtoneUri) {
super.onSaveRingtone(ringtoneUri);
notifyChanged();
}
#Override
public CharSequence getSummary() {
return "Date " + System.currentTimeMillis();
}
}
As far as I can tell, the ListView in the PreferenceScreen needs to be invalidated using the ListView.invalidate() function in when onResume() of PreferenceScreen is called, after the RingtonePreference has closed.

Android YesNoDialogPreference

You may think this is a duplicate question but i looked up almost every existing answer and i still did not get it right. Here is my question:
I want to create a default YesNoDialogPreference by extending the DialogPreferenceclass
Creating a preference using YesNoDialogPreference in prefs.xml
In the MainActivity i want to set an onClickListener for Yes and No options
I have tried doing this using AlertDialog.Builder but it didn't work, i've also tried to use com.android.internal.preference.YesNoPreference and it did work cause of R.attr error
Can somebody please give me a full answer...PLEASE!!, i have been struggling with this for weeks now.
Here's my code:
YesNoDialogPreference.java
import android.content.Context;
import android.preference.DialogPreference;
import android.util.AttributeSet;
public class YesNoDialogPreference extends DialogPreference {
public YesNoDialogPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
persistBoolean(positiveResult);
}
}
The preference from prefs.xml
<com.me.myapp.YesNoDialogPreference
android:key="KEY"
android:dialogMessage="You sure ?"
android:title="Do something"
android:summary="will do something"
/>
I do not know how to link them in the MainActivity.
What you are trying to achieve does not make sense. The MainActivity is not active so can't be the target of the dialog.
You need an onClick handler in your YesNoDialogPreference which then does what you want. You typically safe the value in your settings and read that value in all other places - like your MainActivity.
Here is a code sample:
How to get the DialogPreference POSITIVE_BUTTON to work on OnClick?
Just use onClick method and implement listener to provide handle actions where you want
#Override
public void onClick(DialogInterface dialog, int which){
if(which == DialogInterface.BUTTON_POSITIVE) {
// do your stuff to handle positive button
}else if(which == DialogInterface.BUTTON_NEGATIVE){
// do your stuff to handle negative button
}
}

Android intercept paste\copy\cut on editText

How can I intercept this kind of events ?
I need to add some logic when the user trying to paste some text into my EditText i know i can use TextWatcher but this entrypoint not good for me becuase i only need to intercept in case of paste and not every time the user press my EditText,
Seems there isn't much you can do by using the API: android paste event
Source reading to the rescue!
I dug into the Android Source of the TextView (EditText is a TextView with some different configuration) and found out that the menu used to offer the cut/copy/paste options is just a modified ContextMenu (source).
As for a normal context-menu, the View must create the menu (source) and then handle the interaction in a callback-method (source).
Because the handling method is public, we can simply hook into it by extending EditText and overwriting the method to react on the different actions. Here is an example-implementation:
import android.content.Context;
import android.util.AttributeSet;
import android.widget.EditText;
import android.widget.Toast;
/**
* An EditText, which notifies when something was cut/copied/pasted inside it.
* #author Lukas Knuth
* #version 1.0
*/
public class MonitoringEditText extends EditText {
private final Context context;
/*
Just the constructors to create a new EditText...
*/
public MonitoringEditText(Context context) {
super(context);
this.context = context;
}
public MonitoringEditText(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public MonitoringEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
/**
* <p>This is where the "magic" happens.</p>
* <p>The menu used to cut/copy/paste is a normal ContextMenu, which allows us to
* overwrite the consuming method and react on the different events.</p>
* #see Original Implementation
*/
#Override
public boolean onTextContextMenuItem(int id) {
// Do your thing:
boolean consumed = super.onTextContextMenuItem(id);
// React:
switch (id){
case android.R.id.cut:
onTextCut();
break;
case android.R.id.paste:
onTextPaste();
break;
case android.R.id.copy:
onTextCopy();
}
return consumed;
}
/**
* Text was cut from this EditText.
*/
public void onTextCut(){
Toast.makeText(context, "Cut!", Toast.LENGTH_SHORT).show();
}
/**
* Text was copied from this EditText.
*/
public void onTextCopy(){
Toast.makeText(context, "Copy!", Toast.LENGTH_SHORT).show();
}
/**
* Text was pasted into the EditText.
*/
public void onTextPaste(){
Toast.makeText(context, "Paste!", Toast.LENGTH_SHORT).show();
}
}
Now, when the user uses cut/copy/paste, a Toast is shown (of course you could do other things, too).
The neat thing is that this works down to Android 1.5 and you don't need to re-create the context-menu (like suggested in the above linked question), which will keep the constant look of the platform (for example with HTC Sense).
There is a much simpler way, although not 100% reliable.
Add TextChangedListener to your editbox:
EditText et = (EditText) mView.findViewById(R.id.yourEditText);
et.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (count > 2) toast("text was pasted");
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void afterTextChanged(Editable s) {
}
});
If the text changes for more than 2 characters, you can assume it was pasted (some smileys take up two characters).
Of course it will not detect pasting when the user pastes 1 or 2 characters, and it will falsely report pasting if the change in text was triggered by something else.
But for most purposes, it gets the job done 👍

Switch Preference - Handling both onPreferenceChange and onPreferenceClick

I've been trying to get a switch preference working in Android whereby I can intercept and handle differently, in certain cases, when they switch it on/off vs when they click the whole preference.
This is what I'm trying to accomplish:
User goes into preferences tags are off and no tags are stored (ie: tag preference is empty)
User turns on preference for tags, and since no tags are stored currently it launches a tag search activity for user to find the tag. - works fine.
If tag already exists, and they change the state ONLY then update the value as normal. - works fine
Here's my issue:
If they click the preference though and they already have a tag saved, don't change the state (regardless if it's enabled or disabled), launch the tag search activity. - this DOESN'T work.
What I've found so far is that in the final scenario above, I get a call to onPreferenceChanged, followed by a call to onPreferenceClicked, followed by a subsequent call to onPreferenceChanged. This seems to be my problem. The first call to onPreferenceChanged causes my listener on my SharedPreferences to be called telling it that it's now enabled.
If I didn't receive the first call to onPreferenceChanged then I wouldn't have an issue.
Here is the relevant parts where I'm setting the listeners
SwitchPreference tagPref = (SwitchPreference) findPreference(PreferencesConstants.PREFERENCE_TAG_ENABLED);
tagPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Log.e("BLAH", "onPrefChanged....is it handled by OnClick?" + Boolean.toString(handledByClick));
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
boolean enabled = prefs.getBoolean(PreferencesConstants.PREFERENCE_TAG_ENABLED, false);
Log.e("BLAH", "value stored in prefs? " + Boolean.toString(enabled));
if (newValue instanceof Boolean) {
enabled = (Boolean) newValue;
}
Log.e("BLAH", "New value? " + Boolean.toString(enabled));
if (!handledByClick) {
if (enabled && (currentTag == null || currentTag.isEmpty())) {
Log.e("BLAH", "Enabled and CurrentTag empty!");
Intent intent = new Intent(getActivity(), TagSearchActivity.class);
startActivityForResult(intent, 0);
return false; // always return false, we'll handle
// updating
// this value manually.
} else {
return true;
}
}
Log.e("BLAH", "returning false (AS IN WE HANDLED IT).");
return false;
}
});
tagPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference preference) {
handledByClick = true;
Log.e("BLAH", "onprefClick");
Intent intent = new Intent(getActivity(), TagSearchActivity.class);
startActivityForResult(intent, 0);
return true;
}
});
Here are the relevant log lines after running it with a saved tag, and clicking the preference.
01-18 15:55:05.593: E/BLAH(13261): onPrefChanged....is it handled by OnClick?false
01-18 15:55:05.593: E/BLAH(13261): value stored in prefs? true
01-18 15:55:05.593: E/BLAH(13261): New value? false
01-18 15:55:05.613: E/DifferentClass(13261): On Shared Preferences Changed - tagEnabled
01-18 15:55:05.652: E/DifferentClass(13261): disabled TAG in cancelAlarmService
01-18 15:55:05.662: E/AnotherClass(13261): Updating Feed List. Old Size: 33, New Size: 14
01-18 15:55:05.682: E/BLAH(13261): onprefClick
01-18 15:55:05.812: E/BLAH(13261): onPrefChanged....is it handled by OnClick?true
01-18 15:55:05.812: E/BLAH(13261): value stored in prefs? false
01-18 15:55:05.822: E/BLAH(13261): New value? false
01-18 15:55:05.822: E/BLAH(13261): returning false (AS IN WE HANDLED IT).
I have been working with the same issue for ages now and you can go about it two ways.
Implementing a switchpreference with custom actions for every event:
forevercrashed made some good points. I tried follow them, but for me they didn't do it. I bet they work, but I needed more functionallity in an easier way. Xgouchet (second Link) uses Headers and custom xml layouts which uses custom placements and measurements (height, witdth, padding etc.). I needed a solution without altering Googles built in auto-generated layout.
The super easy and powerful way: implement your own SwitchPreference!
Just make a class extend SwitchPreference and then implement/override like so:
public class AutoUploadSwitchPreference extends SwitchPreference {
public AutoUploadSwitchPreference(Context context) {
super(context);
}
public AutoUploadSwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AutoUploadSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onClick() {
//super.onClick(); THIS IS THE IMPORTANT PART!
}
By overriding onClick() and commenting out / deleting super.onClick() makes the SwitchPreference NOT call callChangeListener(Object newValue). Now you can click the preference and nothing happens, not until you want it to.
(One bug that would occur otherwise was having multiple calls to onPreferenceChange in the fragment)
Now! To make things happen: Here is the structure I have used.
Create a SettingsActivity
In it make sure you fetch preferences, resources etc.
in onCreate() in your Activity - launch a PreferenceFragment
This needs to be a custom class extending PreferenceFragment, see how here : PreferenceFragment
In your custom Fragment, get hold of your custom-preference. You can use findPreference("custom_switch_key").
add an OnPreferenceChangeListener on the preference
I personally make my fragment implement the listener and pass this as argument.
The return statement is important. This is what makes the actual change in the switch. If you return true the switch will change into the newValue. If you return false, it will not. If you use return false; you can change the value with setChecked(true|false) on the switchpreference.
when you implement onPreferenceChange(Preference preference, Object newValue) you can add whatever functionality you want from pressing the switch-slider only
the functionality from clicking the preference can be done in three ways:
Implement the onClick() further in the custom SwitchPreference class
Implement the method onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) in the fragment
Implement an onPreferenceClickListener like you did for the ChangeListener.
Sorry if this is a long post. It is my first and I have been through so many stackoverflow-pages about this and no one was accurate, just wanted to get it right ;)
After searching for hours more I came across a couple posts that will be helpful to others in this situation.
This one was the solution I opted for given my problem: How do I create one Preference with an EditTextPreference and a Togglebutton?
It's a very detailed answer and is very helpful in understanding preferences.
The other post I came across was this one: http://xgouchet.fr/android/index.php?article4/master-on-off-preferences-with-ice-cream-sandwich
It will give you pretty much the same look and feel as the one above, but requires more work and because of my requirements wouldn't work for me.
i think you are asking about a feature that doesn't exist.
however , since the preference activity uses a listView , you can use some tricks to customize it and handle it however you wish .
here's a post i've made about customizing it , based on this website . what i've asked there is how to add a listView , but i didn't know that a preference activity actually uses a listview .
This took me ages, and none of the answers here worked. I finally found the simplest answer, no custom layout required, in a Github Gist, which I'll preserve here, but I have modified it to work with SwitchPreferenceCompat instead of SwitchPreference.
First, create the custom preference.
package com.mendhak.gpslogger.ui.components;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import androidx.appcompat.widget.SwitchCompat;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreferenceCompat;
/**
* Custom preference for handling a switch with a clickable preference area as well
*/
public class SwitchPlusClickPreference extends SwitchPreferenceCompat {
//
// Public interface
//
/**
* Sets listeners for the switch and the background container preference view cell
* #param listener A valid SwitchPlusClickListener
*/
public void setSwitchClickListener(SwitchPlusClickListener listener){
this.listener = listener;
}
private SwitchPlusClickListener listener = null;
/**
* Interface gives callbacks in to both parts of the preference
*/
public interface SwitchPlusClickListener {
/**
* Called when the switch is switched
* #param buttonView
* #param isChecked
*/
public void onCheckedChanged(SwitchCompat buttonView, boolean isChecked);
/**
* Called when the preference view is clicked
* #param view
*/
public void onClick(View view);
}
public SwitchPlusClickPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public SwitchPlusClickPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SwitchPlusClickPreference(Context context) {
super(context);
}
//
// Internal Functions
//
/**
* Recursively go through view tree until we find an android.widget.Switch
* #param view Root view to start searching
* #return A Switch class or null
*/
private SwitchCompat findSwitchWidget(View view){
if (view instanceof SwitchCompat){
return (SwitchCompat)view;
}
if (view instanceof ViewGroup){
ViewGroup viewGroup = (ViewGroup)view;
for (int i = 0; i < viewGroup.getChildCount();i++){
View child = viewGroup.getChildAt(i);
if (child instanceof ViewGroup){
SwitchCompat result = findSwitchWidget(child);
if (result!=null) return result;
}
if (child instanceof SwitchCompat){
return (SwitchCompat)child;
}
}
}
return null;
}
#Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final SwitchCompat switchView = findSwitchWidget(holder.itemView);
if (switchView!=null){
switchView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null)
listener.onCheckedChanged((SwitchCompat) v, ((SwitchCompat)v).isChecked());
}
});
switchView.setChecked(getSharedPreferences().getBoolean(getKey(),false));
switchView.setFocusable(true);
switchView.setEnabled(true);
//Set the thumb drawable here if you need to. Seems like this code makes it not respect thumb_drawable in the xml.
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener!=null) listener.onClick(v);
}
});
}
// //Get a handle on the 2 parts of the switch preference and assign handlers to them
// #Override
// protected void onBindView (View view){
// super.onBindView(view);
//
// final Switch switchView = findSwitchWidget(view);
// if (switchView!=null){
// switchView.setOnClickListener(new View.OnClickListener() {
// #Override
// public void onClick(View v) {
// if (listener != null)
// listener.onCheckedChanged((Switch) v, ((Switch)v).isChecked());
// }
// });
// switchView.setChecked(getSharedPreferences().getBoolean(getKey(),false));
// switchView.setFocusable(true);
// switchView.setEnabled(true);
// //Set the thumb drawable here if you need to. Seems like this code makes it not respect thumb_drawable in the xml.
// }
//
// view.setOnClickListener(new View.OnClickListener() {
// #Override
// public void onClick(View v) {
// if (listener!=null) listener.onClick(v);
// }
// });
// }
}
Next in your preference XML, add an entry:
<com.mendhak.gpslogger.ui.components.SwitchPlusClickPreference
android:key="google_drive_enabled"
android:title="#string/google_drive_setup_title"
android:icon="#drawable/googledrive"/>
And then in the code for your Preference Activity class, the real magic is that you use the built-in callbacks for changed, and clicked.
((SwitchPlusClickPreference)findPreference(PreferenceNames.AUTOSEND_GOOGLE_DRIVE_ENABLED)).setSwitchClickListener(new SwitchPlusClickPreference.SwitchPlusClickListener() {
#Override
public void onCheckedChanged(SwitchCompat buttonView, boolean isChecked) {
//The switch bit changed. handle it.
}
#Override
public void onClick(View view) {
// The text bit was clicked on, handle it
}
});

Getting activity from context in android

This one has me stumped.
I need to call an activity method from within a custom layout class. The problem with this is that I don't know how to access the activity from within the layout.
ProfileView
public class ProfileView extends LinearLayout
{
TextView profileTitleTextView;
ImageView profileScreenImageButton;
boolean isEmpty;
ProfileData data;
String name;
public ProfileView(Context context, AttributeSet attrs, String name, final ProfileData profileData)
{
super(context, attrs);
......
......
}
//Heres where things get complicated
public void onClick(View v)
{
//Need to get the parent activity and call its method.
ProfileActivity x = (ProfileActivity) context;
x.activityMethod();
}
}
ProfileActivity
public class ProfileActivityActivity extends Activity
{
//In here I am creating multiple ProfileViews and adding them to the activity dynamically.
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.profile_activity_main);
}
public void addProfilesToThisView()
{
ProfileData tempPd = new tempPd(.....)
Context actvitiyContext = this.getApplicationContext();
//Profile view needs context, null, name and a profileData
ProfileView pv = new ProfileView(actvitiyContext, null, temp, tempPd);
profileLayout.addView(pv);
}
}
As you can see above, I am instantiating the profileView programatically and passing in the activityContext with it. 2 questions:
Am i passing the correct context into the Profileview?
How do I get the containing activity from the context?
From your Activity, just pass in this as the Context for your layout:
ProfileView pv = new ProfileView(this, null, temp, tempPd);
Afterwards you will have a Context in the layout, but you will know it is actually your Activity and you can cast it so that you have what you need:
Activity activity = (Activity) context;
This is something that I have used successfully to convert Context to Activity when operating within the UI in fragments or custom views. It will unpack ContextWrapper recursively or return null if it fails.
public Activity getActivity(Context context)
{
if (context == null)
{
return null;
}
else if (context instanceof ContextWrapper)
{
if (context instanceof Activity)
{
return (Activity) context;
}
else
{
return getActivity(((ContextWrapper) context).getBaseContext());
}
}
return null;
}
No
You can't
There are two different contexts in Android. One for your application (Let's call it the BIG one) and one for each view (let's call it the activity context).
A linearLayout is a view, so you have to call the activity context. To call it from an activity, simply call "this". So easy isn't it?
When you use
this.getApplicationContext();
You call the BIG context, the one that describes your application and cannot manage your view.
A big problem with Android is that a context cannot call your activity. That's a big deal to avoid this when someone begins with the Android development. You have to find a better way to code your class (or replace "Context context" by "Activity activity" and cast it to "Context" when needed).
Regards.
Just to update my answer. The easiest way to get your Activity context is to define a static instance in your Activity. For example
public class DummyActivity extends Activity
{
public static DummyActivity instance = null;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Do some operations here
}
#Override
public void onResume()
{
super.onResume();
instance = this;
}
#Override
public void onPause()
{
super.onPause();
instance = null;
}
}
And then, in your Task, Dialog, View, you could use that kind of code to get your Activity context:
if (DummyActivity.instance != null)
{
// Do your operations with DummyActivity.instance
}
And in Kotlin:
tailrec fun Context.activity(): Activity? = when {
this is Activity -> this
else -> (this as? ContextWrapper)?.baseContext?.activity()
}
If you like to call an activity method from within a custom layout class(non-Activity Class).You should create a delegate using interface.
It is untested and i coded it right . but i am conveying a way to achieve what you want.
First of all create and Interface
interface TaskCompleteListener<T> {
public void onProfileClicked(T result);
}
public class ProfileView extends LinearLayout
{
private TaskCompleteListener<String> callback;
TextView profileTitleTextView;
ImageView profileScreenImageButton;
boolean isEmpty;
ProfileData data;
String name;
public ProfileView(Context context, AttributeSet attrs, String name, final ProfileData profileData)
{
super(context, attrs);
......
......
}
public setCallBack( TaskCompleteListener<String> cb)
{
this.callback = cb;
}
//Heres where things get complicated
public void onClick(View v)
{
callback.onProfileClicked("Pass your result or any type");
}
}
And implement this to any Activity.
and call it like
ProfileView pv = new ProfileView(actvitiyContext, null, temp, tempPd);
pv.setCallBack(new TaskCompleteListener
{
public void onProfileClicked(String resultStringFromProfileView){}
});
Context may be an Application, a Service, an Activity, and more.
Normally the context of Views in an Activity is the Activity itself so you may think you can just cast this Context to Activity but actually you can't always do it, because the context can also be a ContextThemeWrapper in this case.
ContextThemeWrapper is used heavily in the recent versions of AppCompat and Android (thanks to the android:theme attribute in layouts) so I would personally never perform this cast.
So short answer is: you can't reliably retrieve an Activity from a Context in a View. Pass the Activity to the view by calling a method on it which takes the Activity as parameter.
Never ever use getApplicationContext() with views.
It should always be activity's context, as the view is attached to activity. Also, you may have a custom theme set, and when using application's context, all theming will be lost. Read more about different versions of contexts here.
I used convert Activity
Activity activity = (Activity) context;
For kotlin user -
val activity = context as Activity
an Activity is a specialization of Context so, if you have a Context you already know which activity you intend to use and can simply cast a into c; where a is an Activity and c is a Context.
Activity a = (Activity) c;
This method should be helpful..!
public Activity getActivityByContext(Context context){
if(context == null){
return null;
}
else if((context instanceof ContextWrapper) && (context instanceof Activity)){
return (Activity) context;
}
else if(context instanceof ContextWrapper){
return getActivity(((ContextWrapper) context).getBaseContext());
}
return null;
}
I hope this helps.. Merry coding!
how about some live data callback,
class ProfileView{
private val _profileViewClicked = MutableLiveData<ProfileView>()
val profileViewClicked: LiveData<ProfileView> = _profileViewClicked
}
class ProfileActivity{
override fun onCreateView(...){
profileViewClicked.observe(viewLifecycleOwner, Observer {
activityMethod()
})
}
}
Create an extension function. And call this extension function with your context like context.getActivity().
fun Context.getActivity(): AppCompatActivity? {
var currentContext = this
while (currentContext is ContextWrapper) {
if (currentContext is AppCompatActivity) {
return currentContext
}
currentContext = currentContext.baseContext
}
return null
}
Kotlin android shorthand extension version of Theo's solution
private fun Context?.getParentActivity() : AppCompatActivity? = when {
this is ContextWrapper -> if (this is AppCompatActivity) this else this.baseContext.getParentActivity()
else -> null
}
Usage of above explained here

Categories

Resources