I am trying to write a Unit Test for a validator of my android Application. The validator accepts as parameter EditText, therefore I need to mock it. However the mocking does not work, forcing the Test to crash on calling the when() method with the exception:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
My code is:
#RunWith(MockitoJUnitRunner.class)
public class MyUnitTest
{
#Mock
Context mMockContext;
#Test
public void validateIsCorrect() {
final EditText input = Mockito.mock(EditText.class);
when(input.getText()).thenReturn(Editable.Factory.getInstance().newEditable("123"));
...
}
}
The dependencies in build.gradle file are:
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
The method getText() of the EditText is not private or final. What am I doing wrong? Is it possible to mock an EditText this way? How?
When you're running a unit test, you're using a standard JVM context, not Android's context and that's why it's crashing: The Editable.Factory class and it's methods (like getInstance()) are not in the classpath. And they have not been mocked either.
What I'd do is to create a class that implements Editable with a private member to hold a string reference and use it to mock the getText() method.
Something like this:
class MockEditable implements Editable {
private String str;
public MockEditable(String str) {
this.str = str;
}
#Override #NonNull
public String toString() {
return str;
}
#Override
public int length() {
return str.length();
}
#Override
public char charAt(int i) {
return str.charAt(i);
}
#Override
public CharSequence subSequence(int i, int i1) {
return str.subSequence(i, i1);
}
#Override
public Editable replace(int i, int i1, CharSequence charSequence, int i2, int i3) {
return this;
}
#Override
public Editable replace(int i, int i1, CharSequence charSequence) {
return this;
}
#Override
public Editable insert(int i, CharSequence charSequence, int i1, int i2) {
return this;
}
#Override
public Editable insert(int i, CharSequence charSequence) {
return this;
}
#Override
public Editable delete(int i, int i1) {
return this;
}
#Override
public Editable append(CharSequence charSequence) {
return this;
}
#Override
public Editable append(CharSequence charSequence, int i, int i1) {
return this;
}
#Override
public Editable append(char c) {
return this;
}
#Override
public void clear() {
}
#Override
public void clearSpans() {
}
#Override
public void setFilters(InputFilter[] inputFilters) {
}
#Override
public InputFilter[] getFilters() {
return new InputFilter[0];
}
#Override
public void getChars(int i, int i1, char[] chars, int i2) {
}
#Override
public void setSpan(Object o, int i, int i1, int i2) {
}
#Override
public void removeSpan(Object o) {
}
#Override
public <T> T[] getSpans(int i, int i1, Class<T> aClass) {
return null;
}
#Override
public int getSpanStart(Object o) {
return 0;
}
#Override
public int getSpanEnd(Object o) {
return 0;
}
#Override
public int getSpanFlags(Object o) {
return 0;
}
#Override
public int nextSpanTransition(int i, int i1, Class aClass) {
return 0;
}
}
You can then make use of this class
Mockito.when(input.getText()).thenReturn(new MockEditable("123"));
Looking at this from a bit further away; I am asking myself: why does your validator need to know anything about Android specific classes?
What I mean is: I assume that your validator (in the end) has to check the properties of maybe a String, or something alike?
I would thus suggest to focus on separating concerns here:
Create a component that fetches a String from your EditText
Create a validator that works with such strings
Then you don't need any specific mocking for your validator in the first place!
final EditText editText = Mockito.mock(EditText.class);
final ArgumentCaptor<Editable> captor = ArgumentCaptor.forClass(Editable.class);
Mockito.doNothing().when(editText).setText(captor.capture());
Mockito.when(editText.getText()).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) {
return captor.getValue();
}
});
What about this guys, it works for me:
Please don't forget to add the MockitoAnnotations.init(this); and also use
#Mock private EditTextView passwordField;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
when(rootView.findViewById(R.id.button_logout)).thenReturn(buttonLogout);
when(rootView.findViewById(R.id.button_unlock)).thenReturn(buttonUnlock);
when(rootView.findViewById(R.id.ScreenLock_PasswordTextField)).thenReturn(passwordField);
when(passwordField.getText()).thenReturn(Editable.Factory.getInstance().newEditable("asd"));
when(application.getPassword()).thenReturn("asd");
sut = new ScreenLockPresenterImpl(application, rootView, screenLockListener,
logoutButtonClickListener);
}
#Test
public void testOnClickWhenOk() {
sut.onClick(null);
verify(passwordField).getText();
verify(screenLockListener).unLock();
}
I think this is what you are looking for:
when(passwordField.getText()).thenReturn(Editable.Factory.getInstance().newEditable("asd"));
Related
I am just learning about RxJava in Android and the (supposed) excellent composition of MVVM, databinding and RxJava. Unfortunately I cannot bind an RxJava Observable directly to a View: need a LiveData.
So, I was wondering is there a way to implement Two-way databinding with RxJava?
So far I've attempted to write a BindingAdapter which adds a listener to the passed View and calls onNext on the Subject.
#BindingAdapter("rxText")
public static void bindReactiveText(EditText view, BehaviorSubject<String> text)
{
view.setText(text.getValue());
view.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) {
}
#Override
public void afterTextChanged(Editable s) {
text.onNext(s.toString());
}
});
}
What this does is updates the Subject/model with any changes in the View, so model always has consistent value with the View. However, the binding is never triggered for any change in the Subject itself (ofcourse we'd have to compare new and old values to stop from looping).
Now, I did try to subscribe to the subject and call setText for each emission, but then I'd have to dispose the Observer. So what I did was also listened for View Attach State change: subscribe in onViewAttachedToWindow and dispose the observer in onViewDetachedFromWindow.
#BindingAdapter("rxText")
public static void bindReactiveText(EditText editText, BehaviorSubject<String> text)
{
setText(editText, text.getValue());
editText.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
private TextWatcher textWatcher = 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) {
}
#Override
public void afterTextChanged(Editable s) {
text.onNext(s.toString());
}
};
private Disposable disposable;
#Override
public void onViewAttachedToWindow(View v) {
editText.addTextChangedListener(textWatcher);
disposable = text.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.filter(s -> !s.equals(editText.getText().toString()))
.subscribe(s -> setText(editText, text.getValue()));
}
#Override
public void onViewDetachedFromWindow(View v) {
disposable.dispose();
editText.removeTextChangedListener(textWatcher);
}
});
}
And while that does work in the intended way, I'm not sure if this is the best approach to implement Two-way binding via RxJava.
One of the immediate drawback that comes into mind is, Activity/Fragment cannot register a for a callback for most android Views. For instance, if I use the same approach for a Button and its click, and set up a listener in my Activity the binding will stop working.
I am still learning RxJava and its various operators and their uses, so maybe I'm missing something obvious or committing a goof, but I've been trying to working out another way to do this for a few days now, so far have not been able to think of one.
So my question: What is the best approach to implementing Two-way data binding with RxJava Observables.
To bind RxJava Observable directly to a View, you need to use ObservableField:
val name = ObservableField<String>()
//ex: name = "john"
Then put this in your xml:
<data>
<import type="android.databinding.ObservableField"/>
<variable
name="name"
type="ObservableField<String>" />
</data>
<TextView
android:text="#{name}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Reference: https://developer.android.com/topic/libraries/data-binding/observability
EDIT:
In case you need to apply it on a model instead of a single field, you can do this:
class User : BaseObservable() {
#get:Bindable
var firstName: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.firstName)
}
#get:Bindable
var lastName: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.lastName)
}
}
BR is the name of a class generated by Android Data Binding.
I would not recommend you to do it like that. While usage of Rx Java with data binding is good(LiveData is better though), your way of implementing it is quite overworked.
This is enough to bind it to the textView.
#BindingAdapter("rxText")
public static void bindReactiveText(EditText view, BehaviorSubject<String> text)
{
view.setText(text.getValue());
view.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) {
}
#Override
public void afterTextChanged(Editable s) {
text.onNext(s.toString());
}
});
}
To subscribe to it and use it you should use a bit different approach. You should use it in view model that should look like this
public class MyViewModel extends ViewModel {
private CompositeDisposable subscriptions = new CompositeDisposable();
public BehaviorSubject<String> text = new BehaviorSubject<String>();
private void bind() {
subscriptions.add(
text.subscribe(text -> {
//do something
})
)
}
#Override
public void onCleared(){
subscriptions.clear()
}
}
and in layout use
...
<TextView
android:id="#+id/text"
style="#style/TextField.Subtitle3.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/small_margin"
app:rxText="#{vm.text}"
/>
...
and in fragment
public class MasterFragment extends Fragment {
private MyViewModel vm;
public void onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
ViewDataBinding binding = DataBindingUtil.inflate(inflater, layoutRes, container, false);
model = new ViewModelProvider(requireActivity()).get(MyViewModel.class);
binding.setVariable(BR.vm, this);
binding.executePendingBindings();
vm.bind();
return binding.root;
}
}
I would do something like this if I would use RxJava for that. I am using LiveData though.
Hope it helps.
Moving from MVP to MVVM and trying to learn from tutorials on web.
Some of the tutorials state that ViewModel classes should not have any reference to Activity or View(android.view.View) classes.
But in some of the tutorials i've seen Views are used in ViewModel class and Activities to start other Activities using ViewModel.
For example:
import android.arch.lifecycle.ViewModel;
import android.support.annotation.NonNull;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import com.journaldev.androidmvvmbasics.interfaces.LoginResultCallback;
import com.journaldev.androidmvvmbasics.model.User;
public class LoginViewModel extends ViewModel {
private User user;
private LoginResultCallback mDataListener;
LoginViewModel(#NonNull final LoginResultCallback loginDataListener) {
mDataListener = loginDataListener;
user = new User("", "");
}
public TextWatcher getEmailTextWatcher() {
return new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
user.setEmail(editable.toString());
}
};
}
public TextWatcher getPasswordTextWatcher() {
return new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
user.setPassword(editable.toString());
}
};
}
public void onLoginClicked(#NonNull final View view) {
checkDataValidity();
}
private void checkDataValidity() {
if (user.isInputDataValid())
mDataListener.onSuccess("Login was successful");
else {
mDataListener.onError("Email or Password not valid");
}
}
}
Another one with View.OnClickListener
public class PostViewModel extends BaseObservable {
private Context context;
private Post post;
private Boolean isUserPosts;
public PostViewModel(Context context, Post post, boolean isUserPosts) {
this.context = context;
this.post = post;
this.isUserPosts = isUserPosts;
}
public String getPostScore() {
return String.valueOf(post.score) + context.getString(R.string.story_points);
}
public String getPostTitle() {
return post.title;
}
public Spannable getPostAuthor() {
String author = context.getString(R.string.text_post_author, post.by);
SpannableString content = new SpannableString(author);
int index = author.indexOf(post.by);
if (!isUserPosts) content.setSpan(new UnderlineSpan(), index, post.by.length() + index, 0);
return content;
}
public int getCommentsVisibility() {
return post.postType == Post.PostType.STORY && post.kids == null ? View.GONE : View.VISIBLE;
}
public View.OnClickListener onClickPost() {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
Post.PostType postType = post.postType;
if (postType == Post.PostType.JOB || postType == Post.PostType.STORY) {
launchStoryActivity();
} else if (postType == Post.PostType.ASK) {
launchCommentsActivity();
}
}
};
}
public View.OnClickListener onClickAuthor() {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
context.startActivity(UserActivity.getStartIntent(context, post.by));
}
};
}
public View.OnClickListener onClickComments() {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
launchCommentsActivity();
}
};
}
private void launchStoryActivity() {
context.startActivity(ViewStoryActivity.getStartIntent(context, post));
}
private void launchCommentsActivity() {
context.startActivity(CommentsActivity.getStartIntent(context, post));
}
}
Another one with Activity Reference
public class UserProfileViewModel {
/* ------------------------------ Constructor */
private Activity activity;
/* ------------------------------ Constructor */
UserProfileViewModel(#NonNull Activity activity) {
this.activity = activity;
}
/* ------------------------------ Main method */
/**
* On profile image clicked
*
* #param userName name of user
*/
public void onProfileImageClicked(#NonNull String userName) {
Bundle bundle = new Bundle();
bundle.putString("USERNAME", userName);
Intent intent = new Intent(activity, UserDetailActivity.class);
intent.putExtras(bundle);
activity.startActivity(intent);
}
/**
* #param editable editable
* #param userProfileModel the model of user profile
*/
public void userNameTextChange(#NonNull Editable editable,
#NonNull UserProfileModel userProfileModel) {
userProfileModel.setUserName(editable.toString());
Log.e("ViewModel", userProfileModel.getUserName());
}
}
Is it okay for ViewModel class to contain Android and View classes,
isn't this bad for unit testing?
Which class should a custom view model class extend? ViewModel or
BaseObservable/Observable?
Is there any tutorial link that shows simple usage of MVVM and with
only focus on architecture without any Dagger2, LiveData, or RxJava
extensions? I'm only looking for MVVM tutorials for now.
From the documentation:
Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.
This is because a ViewModel survives configuration changes. Let's say you have an activity and you rotate the device. The activity is killed and a new instance is created. If you put views in the viewmodel, then the activity won't be garbage collected because the views hold the reference to the previous activity. Also, the views themselves will be recreated but you're keeping old views in the viewmodel. Basically don't put any views, context, activity in the viewmodel.
Here's a sample from google: https://github.com/googlesamples/android-sunflower/
I have read this question
How to disable Button if EditText is empty ?
But there it is only 1 EditText field. What is an elegant solution to use a TextWatcher to enable or disable a Button if both of two EditText fields are either empty or contain text?
This is my approach and it works, but it makes no use of any of the arguments passed in onTextChanged. What do you think?
public class MainActivity extends AppCompatActivity implements TextWatcher {
private EditText editTextUsername;
private EditText editTextPassword;
private Button buttonConfirm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editTextUsername = findViewById(R.id.edit_text);
editTextPassword = findViewById(R.id.edit_password);
buttonConfirm = findViewById(R.id.button_confirm);
editTextUsername.addTextChangedListener(this);
editTextPassword.addTextChangedListener(this);
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String usernameInput = editTextUsername.getText().toString().trim();
String passwordInput = editTextPassword.getText().toString().trim();
buttonConfirm.setEnabled(!usernameInput.isEmpty() && !passwordInput.isEmpty());
}
#Override
public void afterTextChanged(Editable s) {
}
}
You can set listeners to both EditText as in
editTextUsername.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) {
button.setEnabled(editTextUsername.getText().toString().trim().length() > 0
&& editTextPassword .getText().toString().trim().length() > 0 );
}
#Override
public void afterTextChanged(Editable s) {
}
});
editTextPassword.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) {
button.setEnabled(editTextUsername.getText().toString().trim().length() > 0
&& editTextPassword .getText().toString().trim().length() > 0 );
}
#Override
public void afterTextChanged(Editable s) {
}
});
Maybe you can create a wrapper for your editTexts like this.
public class MyEditText extends AppCompatEditText implements TextWatcher {
public interface LoginTextWatcher {
void onTextChanged(String... texts);
}
private static List<MyEditText> myEditTextList = new ArrayList<>();
private static LoginTextWatcher loginTextWatcherListener;
public MyEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyEditText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyEditText(Context context) {
this(context, null, 0);
}
public static void setLoginTextWatcherListener(LoginTextWatcher listener) {
loginTextWatcherListener = listener;
}
public void addLoginTextWatcher() {
super.addTextChangedListener(this);
myEditTextList.add(this);
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (loginTextWatcherListener != null) {
String[] textArray = new String[myEditTextList.size()];
for (int index = 0; index < myEditTextList.size(); index++) {
textArray[index] = myEditTextList.get(index).getText().toString();
}
loginTextWatcherListener.onTextChanged(textArray);
}
}
#Override
public void afterTextChanged(Editable editable) {
}
Then you use it like this,
public class MainActivity extends AppCompatActivity implements LoginTextWatcher {
private MyEditText editTextUsername;
private MyEditText editTextPassword;
private Button buttonConfirm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editTextUsername = findViewById(R.id.edit_text);
editTextPassword = findViewById(R.id.edit_password);
buttonConfirm = findViewById(R.id.button_confirm);
MyEditText.setLoginTextWatcherListener(this);
editTextUsername.addLoginTextWatcher();
editTextPassword.addLoginTextWatcher();
}
#Override
public void onTextChanged(String... texts) {
//do your work
}
}
What I did it here is not a big deal. You have a wrapper, manages callbacks there and pass to activity what you want and as you want. I believe your activity looks more elegant in this way. You can pass editTexts if you don't wanna loop after every text change. You know, you can change things as you wish. It is also nice to add methods into MyEditText class to remove those static references and call them at some point. Maybe when the activity is destroying.
Use textwatcher for both edittext
Add one more condition for checking anotheredittext.getText().toString().trim().isEmpty()
And use this code to enable button in both textwatcher,
buttonConfirm.setEnabled(!(s.toString().trim().isEmpty() || anotheredittext.getText().toString().trim().isEmpty()));
Kotlin version of #anzaidemirzoi's answer (also, for a TextInputEditText as that is what I'm using):
import android.content.Context
import android.support.design.widget.TextInputEditText
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
class TextWatcherInputEditText #JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
TextInputEditText(context, attrs, defStyleAttr),
TextWatcher {
interface IntegratedTextWatcher {
fun onTextChanged(texts: String)
}
init {
super.addTextChangedListener(this)
}
private var integratedTextWatcherListener: IntegratedTextWatcher? = null
fun setLoginTextWatcherListener(listener: IntegratedTextWatcher) {
integratedTextWatcherListener = listener
}
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
if (integratedTextWatcherListener != null) {
integratedTextWatcherListener!!.onTextChanged(charSequence.toString())
}
}
override fun afterTextChanged(editable: Editable) {
}
}
I have 8 fragments, each inflating a layout with among others, an EditText wrapped inside a TextInputLayout. In the onCreateView, am implementing
EditText inputTextFrag1 = (EditText)findViewById(R.id.et_frag1);
inputTextFrag1.addTextChangedListener(new MyTextWatcher(inputTextFrag1));
Am also having to implement MyTextWatcher class in each fragment body as below:
private class MyTextWatcher implements TextWatcher {
private View view;
public MyTextWatcher(View view) {
this.view = view;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
saveButton.getBackground().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
saveButton.setClickable(false);
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
saveButton.getBackground().setColorFilter(null);
switch (view.getId()) {
case R.id.et_frag1:
validateName();
break;
}
}
}
Where validateName();
private boolean validateName() {
if (inputTextFrag1 .getText().toString().trim().isEmpty()) {
mInputLayoutName.setError(getString(R.string.err_msg_name));
requestFocus(inputTextFrag1 );
return false;
} else {
mInputLayoutName.setErrorEnabled(false);
}
return true;
}
Is there a way to have just one MyTextWatcher class somewhere and one validateName() method to be called by each fragment instead of duplicating the same class/method 8 times. Thanks
Is this the correct way to place the TextWatcher class inside a BaseDialogFragment?
public abstract class BaseDialogFragment extends DialogFragment{
private class MyTextWatcher implements TextWatcher {
private View view;
public MyTextWatcher(View view) {
this.view = view;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
}
}
}
What logic goes into the beforeTextChanged and afterTextChanged methods of the TextWatcher?
You can create a BaseFragment that will be extended by your fragments.
Therefore, you can manage your TextWatcher inside this BaseFragment and consequently the fragments which have this heritage, will receive your expected logic.
As in the following example:
BaseFragment.class
public abstract class BaseFragment extends Fragment implements TextWatcher {
EditText editText;
Button button;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//inflate your edit text
...
//inflate your button
...
editText.addTextChangedListener(this);
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
//text watcher listener
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//text watcher listener
}
#Override
public void afterTextChanged(Editable s) {
//text watcher listener
}
}
YourFragment.class
public class YourFragment extends BaseFragment {
...
}
No need for duplication. In your current implementation, it seems your MyTextWatcher class is an inner class of another class (probably fragment class). In this way of implementation you can't share it among all fragment classes.
However if you define your MyTextWatcher class as a standalone class, you can then use it for all fragment classes. To do this, you should only be using the variables and class members that have been declared in scope of the class being defined. In your case saveButton variable doesn't belong to MyTextWatcher class (it's accessible from the outer scope), in such cases, you should import them via the constructor method.
private class MyTextWatcher implements TextWatcher {
private View view;
private Button saveButton;
public MyTextWatcher(View view, Button saveButton) {
this.view = view;
this.saveButton = saveButton;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
saveButton.getBackground().setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
saveButton.setClickable(false);
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
saveButton.getBackground().setColorFilter(null);
switch (view.getId()) {
case R.id.et_frag1:
validateName();
break;
}
}
}
You can now instantiate this class 8 times for your 8 fragments.
However, #Bruno Vieira's solution is better (i.e. using a base fragment class).
I want to create an EditText which accepts passwords. I want to hide the character as soon as it is typed. So, I used a TransformationMethod.
I am new to this so, I tried the following code.
EditText editText = (EditText) findViewById(R.id.editText);
editText.setTransformationMethod(new PasswordTransformationMethod());
private class PasswordTransformationMethod extends Transformation implements TransformationMethod {
#Override
public CharSequence getTransformation(CharSequence source, View view) {
return "/";
}
#Override
public void onFocusChanged(View view, CharSequence source, boolean focused, int direction, Rect previouslyFocusedRect) {
source = getTransformation(source, view);
}
}
However, it throws,
01-03 10:22:35.750: E/AndroidRuntime(2102): java.lang.IndexOutOfBoundsException
I am missing something. Any help will be appreciated.
The above method has lots of errors.
So, I am sharing the code that I use to convert passwords into dots.
Create a separate class in the same Java file like this,
public class MyPasswordTransformationMethod extends PasswordTransformationMethod {
#Override
public CharSequence getTransformation(CharSequence source, View view) {
return new PasswordCharSequence(source);
}
private class PasswordCharSequence implements CharSequence {
private CharSequence mSource;
public PasswordCharSequence(CharSequence source) {
mSource = source;
}
public char charAt(int index) {
return '.';
}
public int length() {
return mSource.length();
}
public CharSequence subSequence(int start, int end) {
return mSource.subSequence(start, end); // Return default
}
}
};
The implementation goes like this,
passwordEditText = (EditText) findViewById(R.id.passwordEditText);
passwordEditText.setTransformationMethod(new MyPasswordTransformationMethod());