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 👍
Related
I need to create a login screen and a simple first screen (with a log out button). When a user logs in, for convenience, it does not require to log in again (only when the log out button is hit). To do this, i need to store a boolean variable whether the users is logged in or not.
When I hit the home button and open the app again, the app remembers that I already logged in. But when I hi the back button, it does not remember it.
Here is my code of the login screen:
package com.example.a20172425.login;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
public class LoginActivity extends AppCompatActivity {
EditText usernameEditText;
EditText passwordEditText;
TextView falseLoginTextView;
SharedPreferences pref;
boolean validCredentials = false;
public static Boolean login = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
usernameEditText = (EditText)findViewById(R.id.usernameField);
passwordEditText = (EditText)findViewById(R.id.passwordField);
falseLoginTextView = (TextView)findViewById(R.id.falseLoginText);
pref = getPreferences(MODE_PRIVATE);
login = getLoginStatus();
if(login) {
toMainActivity();
}
}
public void checkCredentials(View v){
//clear possible previous content
falseLoginTextView.setText("");
//retrieve username and password
String username = usernameEditText.getText().toString();
String password = passwordEditText.getText().toString();
if ((username.equals("username")) && (password.equals("password"))) {
validCredentials = true;
setLoginStatus(true);
//setUsername(username);
} else if ((username.equals("a")) && (password.equals("a"))) {
validCredentials = true;
setLoginStatus(true);
//setUsername(username);
}
if (validCredentials){
toMainActivity();
}
else
{
falseLoginTextView.setText("Incorrect username and or password");
}
}
public void toMainActivity(){
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
intent.putExtra("Username", usernameEditText.getText().toString());
this.startActivity(intent);
//makes sure pressing the back button does not send the app back to the login screen
this.finish();
}
// gets the logged_in value from persistent memory.
public Boolean getLoginStatus (){
return pref.getBoolean("Logged_in",false);
}
//sets the logged_in boolean value in persistent memory.
public void setLoginStatus(Boolean loginStatus){
//editor to change values to be stored in memory
SharedPreferences.Editor edit = pref.edit();
edit.putBoolean("Logged_in",loginStatus);
//save changes
edit.commit();
}
}
This is my code of the simple first screen:
package com.example.a20172425.login;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import static com.example.a20172425.login.LoginActivity.login;
public class MainActivity extends AppCompatActivity {
TextView LoginTextView;
LoginActivity loginActivity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LoginTextView = (TextView) findViewById(R.id.LoginTV);
String username = getIntent().getStringExtra("Username");
LoginTextView.setText("Currently logged in as: " + username);
loginActivity = new LoginActivity();
}
public void toLoginActivity(View v) {
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
this.startActivity(intent);
login = false;
//loginActivity.setLogin(false);
//makes sure pressing the back button does not send the app back to the login screen
this.finish();
}
}
Any suggestion will be appreciated.
EDIT: I write Kotlin in my day to day job, I didn't actually compile the Java examples below so there may be some syntactical errors. However, it should be fairly trivial to resolve these with basic working Java knowledge. The concepts in this answer are applicable to the vast majority of OO languages (Kotlin, Java, C# etc.).
I'm updating this answer to give some more detail on what an interface is, why they are useful, how callbacks work and how they are implemented under the hood.
In object oriented programming (OO) and, in this case, Java; there is a type system that allows for polymorphism. Polymorphism, when broken down, means (poly -> many) and (morphism -> behaving like), or, in other words, many similarly behaving different types.
What these means in more concrete coding terms is that you can have many different classes of types which conform to a common behaviour (or interface). The behaviour of a type should be thought of as it's outwardly observable behaviours and not the internal implementation. It is useful to conceptualise this with respect to the type system. For example, a behaviour is defined as a transformation from one type to another (for instance a function that takes a collection of strings and returns a string). There are many functions that could perform this transformation, but the outwardly observable behaviour of all of these functions is the same (in other words, a transformation Collection -> String).
It therefore follows that such a type system can allow for arbitrary swapping of implementations as long as the outwardly observable behaviour is maintained.
Interfaces are a popular language construct to achieve this. An interface merely defines the tranformations between types and gives them names. Other types may then depend on this interface and call methods of this interface without any concern as to the actual implementation of the methods (the only constraint being that the implementors of said interface must conform to the type transformations - this is enforced at compile time in Java).
Here is a very simple interface:
public interface Car {
public Int accelerate(Int force);
public Int steer(Int direction);
}
I use the car example as it's quite intuitive. Here we can see two type transformations, from Int -> Int with a name accelerate, and from Int -> Int with a name of steer.
All cars can accelerate and steer. But not all cars accelerate and steer in the same way. However, all cars steer and accelerate behaviours follow a common pattern (or type transformation). They take some input value and result in some output value.
So, we could provide several implementations of car like so (bear in mind this is a very contrived example, so don't judge):
public class Ford implements Car {
#Override
public Int accelerate(Int force) {
return force * 1;
}
#Override
public Int steer(Int direction) {
return direction * 1;
}
}
public class Ferrari implements Car {
#Override
public Int accelerate(Int force) {
return force * 10;
}
#Override
public Int steer(Int direction) {
return direction * 10;
}
}
As you can see, a Ford and Ferrari both steer and accelerate. But the Ferrari does it differently (but it still conforms to the type transformation just as the Ford does).
Now, here we introduce polymorphism, which is a very powerful tool. Imagine we have the following class:
public class Person {
private Car car;
public Person(Car car) {
this.car = car
}
}
So, a person can be constructed by passing a car as a dependancy to it. Due to polymorphism we can pass any instance of an object that implements (or conforms to) the Car interface. For instance, we could do the following:
public class Main {
public void main([String] args) {
Person poorPerson = new Person(new Ford());
Person richPerson = new Person(new Ferrari());
}
}
Pretty nifty! Now we can create hundreds of different types of cars but our person class never has to change! Our person class can call methods on their respectively owned cars and never have to worry about anything breaking (because all cars can accelerate and steer).
So, how does this relate to the original question? Let us consider this very contrived example of an Android View class and a Callback interface:
public interface ContrivedCallback {
public void onClick();
}
public class ContrivedView {
private ContrivedCallback callback;
public void setOnClickListener(ContrivedCallback: callback) {
this.callback = callback;
}
private void onClick() {
this.callback.invoke();
}
}
Let us assume that the onClick method in the ContrivedView class is magically called by the Android OS when the view is clicked. Now, when the view is clicked, the callback (if set) is invoked. But, as we now know, the callback is just an interface, so whatever implementation was provided to the setCallback method will be invoked.
So, the callbacks single defined method is merely a transformation from Void -> Void (in other words, it takes no arguments and returns no value). It's just some code to run. The implementation may launch rockets, save to a database, print a string or do literally nothing, it's up to the coder providing the implementation).
I hope this makes sense? Now, with respect to the original question:
You can use an onClickListener callback. In this callback implementation you can write some code that updates some state of the Activity or Fragment (or write to preferences or local database, or launch missiles... you get the picture).
You create an implementation and then assign this implementation to the callback listener on the button in question like so (in your onCreate method):
logoutButton = (Button)findViewById(R.id.logoutButton);
logoutButton.addOnClickListener(class callback extends DialogInterface.OnClickListener {
#Override
public void onClick() {
// Do stuff here...
}
});
How do i go to the next text when my snake eat a food? (When the snake eat the food, the text will change from testing to success.) I'm using the snake game provided by eclipse. This is the code i have done so far. I am doing this for my project so i appreciate all the help i can get.
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class Snake extends Activity {
/**
* Constants for desired direction of moving the snake
*/
public static int MOVE_LEFT = 0;
public static int MOVE_UP = 1;
public static int MOVE_DOWN = 2;
public static int MOVE_RIGHT = 3;
private static String ICICLE_KEY = "snake-view";
private SnakeView mSnakeView;
/**
* Called when Activity is first created. Turns off the title bar, sets up the content views,
* and fires up the SnakeView.
*
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.snake_layout);
init();
mSnakeView = (SnakeView) findViewById(R.id.snake);
mSnakeView.setDependentViews((TextView) findViewById(R.id.text),
findViewById(R.id.arrowContainer), findViewById(R.id.background));
if (savedInstanceState == null) {
// We were just launched -- set up a new game
mSnakeView.setMode(SnakeView.READY);
} else {
// We are being restored
Bundle map = savedInstanceState.getBundle(ICICLE_KEY);
if (map != null) {
mSnakeView.restoreState(map);
}
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
// Store the game state
outState.putBundle(ICICLE_KEY, mSnakeView.saveState());
}
private int currentQuestion;
private String [] questions;
private TextView questionView;
public void init() {
questions = new String[]{"testing","success"};
currentQuestion = -1;
questionView = (TextView) findViewById(R.id.QuestionTextView);
showQuestion();
}
public void showQuestion() {
currentQuestion++;
if(currentQuestion == questions.length)
currentQuestion =0;
questionView.setText(questions[currentQuestion]);
}
}
After checking out the source code (which I did here: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/4.2_r1/com/example/android/snake/SnakeView.java), I dont think it's possible to add this functionality without altering the source code...
However, if u want to edit the source to make it work, I would suggest the following:
create an interface to use as a listener
public interface AppleEatenListener{
public void appleEaten(int size);
}
Then add a variable of this interface to the SnakeView class
private AppleEatenListener mAppleEatenListener;
Create a setter in the SnakeView class to set the mAppleEatenListener
public void setAppleEatenListener(AppleEatenListener listener){
this.mAppleEatenListener = listener;
}
After that, make your way down to the updateSnake() method in the SnakeView class and find the following piece of code (consider using ctrl+f (search function)):
This snippet comes from the current source code:
// except if we want the snake to grow
if (!growSnake) {
mSnakeTrail.remove(mSnakeTrail.size() - 1);
}
And here we want to add, that if the snake should grow, we call the AppleEatenListener's function appleEaten(), like so:
// except if we want the snake to grow
if (!growSnake) {
mSnakeTrail.remove(mSnakeTrail.size() - 1);
}
else{
if(mAppleEatenListener != null){
mAppleEatenListener.appleEaten(mSnakeTrail.size());
}
}
Now, we should return to your own Snake class and add the following in onCreate():
mSnakeView.setOnAppleEatenListener(new AppleEatenListener() {
#Override
public void appleEaten(int size) {
showQuestion();
//size is the current size of the snake after the apple was eaten
}
});
Note that I've added that the listener requests a "size" parameter, I'm supposing it could be useful to know the size of the snake after it has eaten an apple, since I didn't see a function for requesting the size of the snake in the API either...
I have not tested this code, but I hope this helps u at least a little
In the code, I have an integer variable. In the GUI, I have a numeric editText field and two buttons which increase/decrease the value of the variable by one.
I need to link the field to the variable so that the field shows the current value of the variable every time it's changed (ie. the buttons are pressed), and set the variable value automatically when the user edits the field using the Android keyboard. Preferably, I'd like it to update the variable automatically without the need to press "Done" (if possible), however I also need to set a limit for the number. Additionally, a method needs to be called each time the value is changed.
I've been able to find how to read the value from the editText on request (to a variable), and how to set a specific text into the field (from a variable). However, this doesn't seem so practical when I need to use the field dynamically as both an input and an output. For example, when pressing the button, it changes the variable, which then changes the field, but this would also trigger the onTextChanged watcher and then again get the field text and set it to the value. I'm assuming there must be a more efficient way to do this.
There's no need to invent a wheel. Android framework provides widget for such purposes - NumberPicker.
EDIT: If you can't or don't want to use NumberPicker, here's the idea:
private class NumberPicker {
private int value=0;
private Button btnIncr;
private Button btnDecr;
private EditText editText;
public NumberPicker(Button btnIncr, Button btnDecr, EditText editText) {
this.btnIncr=btnIncr;
this.btnDecr=btnDecr;
this.editText=editText;
this.btnIncr.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
value++;
NumberPicker.this.editText.setText(String.valueOf(value));
}
});
this.btnDecr.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
value--;
NumberPicker.this.editText.setText(String.valueOf(value));
}
});
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
value=Integer.parseInt(s.toString());
}
#Override
public void afterTextChanged(Editable s) {
}
});
}
public int getValue() {
return value;
}
}
And don't worry about TextWatcher being triggered when value is changed by buttons. This shouldn't cause any problems.
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
}
});
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.