static inner class and non static outer method - android

I have a Problem with an ColorPickerDialog http://www.yougli.net/android/a-photoshop-like-color-picker-for-your-android-application/
This ColorPickerDialog has an inner static class...
in this inner static class i need to use "close()" or "dismiss()" on the ColorPickerDialog to close it...
My problem is
public class ColorPickerDialog extends Dialog
The close() and dismiss() methods are non static in Dialog. How can i use this Methods in the inner static class private static class ColorPickerView extends View ?
edit...
Here are the important sections from the Code..
public class ColorPickerDialog extends Dialog {
public interface OnColorChangedListener {
void colorChanged(String key, int color);
}
private static class ColorPickerView extends View {
#Override
public boolean onTouchEvent(MotionEvent event) {
if (x > 266 && x < 394 && y > 316 && y < 356){
savedDialog();
}
return true;
}
private void savedDialog() {
new AlertDialog.Builder(getContext())
.setTitle("Save to profile?")
.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
}
})
.setNegativeButton("No",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
}
}).show();
}
}
public ColorPickerDialog(Context context, OnColorChangedListener listener,
String key, int initialColor, int defaultColor) {
super(context);
mListener = listener;
mKey = key;
mInitialColor = initialColor;
mDefaultColor = defaultColor;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OnColorChangedListener l = new OnColorChangedListener() {
public void colorChanged(String key, int color) {
mListener.colorChanged(mKey, color);
dismiss();
}
};
setContentView(new ColorPickerView(getContext(), l, mInitialColor,
mDefaultColor));
setTitle(R.string.pick_a_color);
}
}
and here i intatiate the ColorPickerDialog...
public class LampsFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
OnColorChangedListener listener = new OnColorChangedListener() {
#Override
public void colorChanged(String key, int color) {
}
};
ColorPickerDialog cp = new ColorPickerDialog(getActivity(), listener, key, arg2, arg2);
cp.show();
return false;
}
});
lv.setAdapter(files);
return view;
}
}
I want to close the ColorPickerDialog after pressing "YES" on the AlertDialog from the the inner static class.

You can't, unless you can get an instance of the ColorPickerDialog somehow. Is the static modifier on the inner class is strictly required? static inner classes do not have access to the instances of the surrounding class. You can either make ColorPickerView a member class (non-static inner class), or pass a reference to the surrounding class to it (either in constructor or via a setter method call).
Member classes have an implicit reference to the surrounding instance, and you can make call the methods of the surrounding classes directly. If there is name-hiding; for ex. suppose the ColorPickerView also declares a close() method, you can call the outer-class method with ColorPickerDialog.this.close().

Related

Dialog fragment used for two different dialogs

So I implemened a custom class that inherits DialogFragment. I want to use this dialog for two types of operations: delete and edit. For that I customized it so for each one I set the title, description and button text. What I want to do is to create an interface and have two presenters: DeletePresenter and EditPresenter that implements that interface. The interface shoul have the click action and the setting of texts for dialog. Being very new to this I can't figure it out how do I connect all of them (dialogFragment, presenters and interface)? If anyone could give me an example that would be great.
MyDialogFragment
public class MyDialogFragment extends DialogFragment implements View.OnClickListener {
private DialogPresenterContract dialogPresenterContract;
private String title, description, buttonTxt;
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.dialog_btn:
dialogPresenterContract.onActionClick(editText.getText().toString().trim(), onDialogActionListener);
break;
}
}
DialogPresenterContract
public interface DialogPresenterContract {
void onActionClick(String reason, MyDialogFragment.OnDialogActionListener onDialogActionListener);
String getDialogTitle();
String getDialogDescription();
String getDialogButtonTxt();
}
DeletePresenter
public class DeletePresenter implements DialogPresenterContract {
//implement all methods
}
EditPresenter
public class EditPresenter implements DialogPresenterContract {
//implement all methods
}
If you want MVP try my sample below, but if you have not match logic I recommend write her right in your view(MyDialogFragment).
public class MyDialogFragment extends DialogFragment implements View.OnClickListener, MyView {
public static MyDialogFragment newInstance(int num) {
MyDialogFragment f = new MyDialogFragment();
Bundle args = new Bundle();
args.putInt("type", type);
f.setArguments(args);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenter.setType(getArguments().getInt("type"));
...
}
private Presenter presenter;
private String title, description, buttonTxt;
#override
void setStrings(String title, String description, String buttonTitle) {
......
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.dialog_btn:
dialogPresenterContract.onActionClick(editText.getText().toString().trim());
break;
}
}
}
MyView interface
interface MyView {
void setStrings(String title, String description, String buttonTitle);
}
Presenter
class Presenter {
private MyView view;
private int type;
public Presenter(MyView view) {
this.view = view;
}
void setType(int type) {
this.type = type;
}
void onClick(String text) {
if (type == delete_type) {
.....
} else {
......
}
}
}

I got java.lang.IllegalStateException: No activity error when I update androidX libraries

I got this error while I am going to show a dialog box
Error:-
This error coming when I update below androidx's Libraries
1. implementation 'androidx.appcompat:appcompat:1.1.0-rc01
2. implementation 'com.google.android.material:material:1.1.0-alpha09'
----- This is my Confirm Dialog ----
public class ConfirmDialog extends BaseDialogFragment {
public static ConfirmDialog newInstance() {
return new ConfirmDialog();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Error Comes
}
#Override
protected Dialog createDialog(Context activityContext) {
AlertDialog.Builder builder;
builder = new AlertDialog.Builder(activityContext, android.R.style.Theme_Material_Light_Dialog_Alert);
AlertDialog alertDialog = builder.create();
alertDialog.setCanceledOnTouchOutside(false);
return alertDialog;
}
#Override
public void onClick(DialogInterface dialog, int which) {
super.onDialogClick(dialog, which, which);
}
}
----- This is my BaseDialog ----
public abstract class BaseDialogFragment extends DialogFragment implements DialogInterface.OnClickListener {
protected OnDialogClickListener onClickListener;
protected BaseDialogFragment() {
}
protected BaseDialogFragment(OnDialogClickListener onClickListener) {
this.onClickListener = onClickListener;
}
public static void show(BaseDialogFragment dialogFragment, Context context) {
dialogFragment.onCreate(null);
Dialog dialog = dialogFragment.createDialog(context);
dialog.show();
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return createDialog(getActivity());
}
protected abstract Dialog createDialog(Context activityContext);
public void setOnClickListener(OnDialogClickListener onClickListener) {
this.onClickListener = onClickListener;
}
public void removeOnClickListener() {
this.onClickListener = null;
}
public void onDialogClick(DialogInterface dialog, int which, Object o) {
if (this.onClickListener != null) {
this.onClickListener.onDialogClick(dialog, which, o);
}
}
}
--- And Last, This is when I call my Confirmdialog ---
ConfirmDialog confirmDialog = ConfirmDialog.newInstance();
confirmDialog.setOnClickListener(new OnDialogClickListener() {
#Override
public void onDialogClick(DialogInterface dialog, int which, Object o) {
}
});
ConfirmDialog.show(confirmDialog, activity); // When I call this
You can try:
ConfirmDialog.show(confirmDialog, activity);
instead
ConfirmDialog.show(confirmDialog, this);
Pass context or instance of the activity
Dialog Fragment should handle click events inside, not in activity/fragment where you are initializing it.
And for comunication between dialog fragment and activity create interface implemented by activity.
For example
interface DialogFragmentResultListener {
fun onDialogResultReceived(requestCode: Int, isPositive: Boolean)
}
and pass result from DialogFragment like this:
if (activity is DialogFragmentResultListener) {
......
}
You Can Try like this method -
BaseDialogFragment -
abstract class BaseDialogFragment : DialogFragment(), View.OnClickListener, BaseView {
override fun onClick(v: View?) {
}
abstract fun initObjects()
abstract fun registerListeners()
abstract fun initWidgets()
protected fun setFullScreen() {
val width = ViewGroup.LayoutParams.MATCH_PARENT
val height = ViewGroup.LayoutParams.MATCH_PARENT
dialog?.window?.setLayout(width, height)
}
override fun onNetworkFailure(errorCode: Int, errorMessage: String?) {
(activity!! as BaseActivity).onNetworkFailure(errorCode, errorMessage)
}
override fun onResponseFailure(error: CloudError?) {
(activity!! as BaseActivity).onResponseFailure(error)
}
override fun showLoader(msg: String?) {
(activity!! as BaseActivity).showLoader(msg)
}
override fun showLoader(msg: String?, isCancellable: Boolean?) {
(activity!! as BaseActivity).showLoader(msg, isCancellable)
}
override fun dismissLoader() {
(activity!! as BaseActivity).dismissLoader()
}
}
Create your confirm Dialog like this -
public class AddEmailDialogFragment extends BaseDialogFragment implements View.OnClickListener {
public static final String TAG = AddEmailDialogFragment.class.getSimpleName();
private Button btnSubmit;
private EditText etEmail;
private GetEmailCallback getEmailCallBack;
public static AddEmailDialogFragment newInstance() {
return new AddEmailDialogFragment();
}
public void setListener(GetEmailCallback listener) {
getEmailCallBack = listener;
}
#Nullable
#Override
public View onCreateView(#NotNull LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_askemail, container, false);
}
#Override
public void onViewCreated(#NotNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initObjects();
initWidgets();
registerListeners();
}
#Override
public void initObjects() {
}
#Override
public void registerListeners() {
btnSubmit.setOnClickListener(this);
}
#Override
public void initWidgets() {
View view = getView();
btnSubmit = view.findViewById(R.id.btn_submit);
etEmail = view.findViewById(R.id.et_email);
}
#Override
public void onClick(View view) {
if (view.getId() == R.id.btn_submit) {
saveEmail();
}
}
private boolean isValidEmail(String target) {
return (!TextUtils.isEmpty(target) && Patterns.EMAIL_ADDRESS.matcher(target).matches());
}
private void saveEmail() {
if (isValidEmail(etEmail.getText().toString()))
getEmailCallBack.gotEMail(etEmail.getText().toString());
else
etEmail.setError(getString(R.string.err_invalidEmail));
}
}
And call the dialog fragment using this-
val activity = activity
if (null != activity) {
addEmailDialogFragment = AddEmailDialogFragment.newInstance()
addEmailDialogFragment!!.show(getActivity()!!.supportFragmentManager, AddEmailDialogFragment.TAG)
addEmailDialogFragment!!.isCancelable = false
addEmailDialogFragment!!.setListener(this)
}
I got this issue after updating the version of androidx appcompat dependency.
For Temporary I have resolved this issue by the below solution.
Use
implementation 'androidx.appcompat:appcompat:1.0.2'
instead of
implementation 'androidx.appcompat:appcompat:1.1.0'

Should ViewModel class contain Android elements?

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/

Calling functions such as startActivity() inside of an enum

I wanted to centralise the creation of DialogFragments used to report errors to the user in order to just have a class to which I pass the error code and have the dialog spawned automagically.
In order to handle multiple errors I am using an enum in which I define the error propreties.
public enum DialogError {
TTS_NOT_INSTALLED {
#Override
public int getTitleResource() {
return R.string.error_tts_not_installed_title;
}
#Override
public int getMessageResource() {
return R.string.error_tts_not_installed_message;
}
#Override
public int getPositiveButtonResource() {
return R.string.error_tts_not_installed_button_positive;
}
#Override
public void onPositiveButtonClick() {
// TODO
}
#Override
public int getNegativeButtonResource() {
return R.string.error_tts_not_installed_button_negative;
}
#Override
public void onNegativeButtonClick() {
// TODO
}
};
public abstract int getTitleResource();
public abstract int getMessageResource();
public abstract int getPositiveButtonResource();
public abstract void onPositiveButtonClick();
public abstract int getNegativeButtonResource();
public abstract void onNegativeButtonClick();
}
Then I have my FragmentDialogError class that I call to create a new Dialog.
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
public class FragmentDialogError
extends DialogFragment {
Context context;
DialogError error;
public FragmentDialogError(Context context, DialogError error) {
this.context = context;
this.error = error;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder
.setTitle(error.getTitleResource())
.setMessage(error.getMessageResource())
.setPositiveButton(error.getPositiveButtonResource(),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
error.onPositiveButtonClick();
}
})
.setNegativeButton(error.getNegativeButtonResource(),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
error.onNegativeButtonClick();
}
});
return builder.create();
}
}
My problem now is that I can't obviously call functions such as startActivity inside of my enum's onPositiveButtonClick() or onNegativeButtonClick().
One soluction would be using a switch() in FragmentDialogError but this way I would split the code between the enum and the class. Another one would be to define in some way the actions that a button press could trigger and let handle them to another class, but I'm looking for a clean and elegant soluction.
How can I implement this in Java keeping the code tidy?
Why not just add a Context to the onClick handlers that you can use to do startActivity?
Also, instead of overriding all the methods in your enum why not use members and a constructor?
public enum DialogError {
TTS_NOT_INSTALLED(
R.string.error_tts_not_installed_title,
R.string.error_tts_not_installed_message,
R.string.error_tts_not_installed_button_positive,
R.string.error_tts_not_installed_button_negative) {
public void onPositiveButtonClick(Context context) {
context.startActivity...
}
#Override
public void onNegativeButtonClick(Context context) {
// TODO
}
};
private final int mTitle;
private final int mMessage;
private final int mPositive;
private final int mNegative;
private DialogError(int title, int message, int positive, int negative) {
mTitle = title;
mMessage = message;
mPositive = positive;
mNegative = negative;
}
public final int getTitleResource() {
return mTitle;
}
public final int getMessageResource() {
return mMessage;
}
public final int getPositiveButtonResource() {
return mPositive;
}
public final int getNegativeButtonResource() {
return mNegative;
}
public abstract void onPositiveButtonClick();
public abstract void onNegativeButtonClick();
}

Android communicate custom View and DialogFragment with listener

I am trying to create a communication between a custom View and a DialogFragment with an interface/callback.
Custom View:
public MyDraw extends View implements ColorPickerListener
{
public MyDraw(Context context)
{
super(context);
// ...
MyDialogFragment.setColorPickerListener(this);
}
#Override
public void onColorChanged(int color)
{
// ...
}
}
DialogFragment
public MyDialogFragment extends DialogFragment
{
public interface ColorPickerListener
{
public void onColorChanged(int color);
}
ColorPickerListener colorPickerListener;
public static void setColorPickerListener(ColorPickerListener listener)
{
colorPickerListener = listener;
}
// ....
private void colorSelected(int color)
{
colorPickerListener.onColorChanged(color);
}
}
This is working, but I'm not sure if this is Ok. I am afraid of memory leaks, because I am referencing a static method from View to the dialog fragment.
Is there any alternative solution, like getting the activity, the instance or casting to something?
You don't need to call the static setColorPickerListener method. You can find your DialogFragment instance using findFragmentByTag method and then simply call your setColorPickerListener (non-static method).
public void showPickerDialog() {
DialogFragment newFragment = new PickerFragment();
newFragment.show(this.getSupportFragmentManager(), "dialogfrag1");
getSupportFragmentManager().executePendingTransactions();
// getting the fragment
PickerFragment df1 = (PickerFragment) getSupportFragmentManager().findFragmentByTag("dialogfrag1");
if (df1 != null) {
df1.registerListener(this);
}
}

Categories

Resources