I have a "DialogHelper" class wherein a bunch of static methods are used in various contexts to make using Dialogs easier. One such method is a "three choice dialog" where the user has three buttons to choose from to go forward:
public static AlertDialog createThreeChoiceDialog(final MyActivity activity, String title, String firstChoiceText,
String secondChoiceText, String thirdChoiceText, View.OnClickListener firstChoiceListener, View.OnClickListener secondChoiceListener,
View.OnClickListener thirdChoiceListener) {
final View dView = activity.getLayoutInflater().inflate(R.layout.three_choice_dialog, null);
final TextView explanatoryTV = (TextView) dView.findViewById(R.id.explanatoryTV);
final TextView firstChoiceTV = (TextView) dView.findViewById(R.id.firstChoiceTV);
final TextView secondChoiceTV = (TextView) dView.findViewById(R.id.secondChoiceTV);
final TextView thirdChoiceTV = (TextView) dView.findViewById(R.id.thirdChoiceTV);
explanatoryTV.setText(title);
firstChoiceTV.setText(firstChoiceText);
secondChoiceTV.setText(secondChoiceText);
thirdChoiceTV.setText(thirdChoiceText);
firstChoiceTV.setOnClickListener(firstChoiceListener);
secondChoiceTV.setOnClickListener(secondChoiceListener);
thirdChoiceTV.setOnClickListener(thirdChoiceListener);
AlertDialog = etc...
return alertDialog;
}
And I call it like this:
private void doSomething() {
final AlertDialog alert = DialogHelper.createThreeChoiceDialog(activity, "title", "choice1", "choice2", "choice3",
new View.OnClickListener() {
#Override
public void onClick(View v) {
//do something 1
alert.dismiss();
}
}, new View.OnClickListener() {
#Override
public void onClick(View v) {
//do something 2
alert.dismiss();
}
}, new View.OnClickListener() {
#Override
public void onClick(View v) {
//do something 3
alert.dismiss();
}
});
alert.show();
}
However, the "alert.show()" method rings up the error:
variable 'alert' might not have been initialized yet
My question is, what is the best way to handle this situation? I want to dismiss the dialog when the user selects a choice.
This is my current workaround:
private void doSomething() {
final ArrayList<AlertDialog> alerts = new ArrayList<>(); //<-- added ArrayList of AlertDialogs
final AlertDialog alert = DialogHelper.createThreeChoiceDialog(activity, "title", "choice1", "choice2", "choice3",
new View.OnClickListener() {
#Override
public void onClick(View v) {
//do something 1
alerts.get(0).dismiss(); //<-- accessed via ArrayList
}
}, new View.OnClickListener() {
#Override
public void onClick(View v) {
//do something 2
alerts.get(0).dismiss(); //<-- accessed via ArrayList
}
}, new View.OnClickListener() {
#Override
public void onClick(View v) {
//do something 3
alerts.get(0).dismiss(); //<-- accessed via ArrayList
}
});
alerts.add(alert); //<-- add alert to ArrayList
alert.show();
}
It works, but there's no way that this can be a best practice. I've run into this problem a few times, so I finally decided to ask what the best way to handle it is.
You are basically trying to reference an instance of a class while declaring and creating that instance - this is not possible.
I see your options as the following:
1. Wrap AlertDialog
This is basically your work-around which uses an ArrayList, but you can create you own class for this purpose also.
2. Make AlertDialog a member
Declare alert be a private member of the class which contains the doSomething method, instead of declaring it in the method itself.
3. Replace your DialogHelper with a Builder
There are several advantages (and 1 disadvantage) to this approach.
The first advantage is that it will solve your problem. The second is because it's good coding practice: in general, having methods with take many parameters is considered dirty. In the case of them being constructor methods, Clean Code conventions recommend replacing them with builders.
The disadvantage of the implementation I am about to suggest is that the Dialog behaviour is that clicking an option will always dismiss the dialog.
public class MyDialogBuilder {
private AlertDialog alert;
public MyDialogBuilder withActivity(Activity activity){
final View dView = activity.getLayoutInflater().inflate(R.layout.three_choice_dialog, null);
alert = ...;
return this;
}
public MyDialogBuilder withFirstChoice(String choiceText, final ChoiceAction action){
final TextView firstChoiceTV = (TextView) alert.findViewById(R.id.firstChoiceTV);
firstChoiceTV.setText(choiceText);
firstChoiceTV.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
action.perform();
alert.dismiss();
}
});
return this;
}
// Similar implementations for the other methods here...
public AlertDialog create() {
return alert;
}
interface ChoiceAction {
void perform();
}
}
Your calling code would be like
MyDialogBuilder builder = new MyDialogBuilder();
AlertDialog alert = builder.withActivity(activity)
.withTitle("Dialog title")
.withFirstChoice("choice 1", new MyDialogBuilder.ChoiceAction() {
#Override
public void perform() {
//do something 1
}
})
.withSecondChoice("choice 2", new MyDialogBuilder.ChoiceAction() {
#Override
public void perform() {
//do something 2
}
})
.withThirdChoice("choice 3", new MyDialogBuilder.ChoiceAction() {
#Override
public void perform() {
//do something 3
}
})
.create();
I would recommend the third approach, as I think in most cases you want to close the Dialog when the user selects an option. If you want to show some progress bar in the dialog, you can create additional methods on MyDialogBuilder which would call alert.dismiss() in a callback. Hope this helps.
Related
I have this code separate class which makes a Snackbar to be displayed within my application, But with my current implementation I am getting a 'java.lang.NullPointerException'. How do I implement it in my main class properly?
here is my snack bar class:
public class SnackBarUtils
{
private static SnackBarUtils mInstance = null;
private Snackbar mSnackBar;
private SnackBarUtils()
{
}
public static SnackBarUtils getInstance()
{
if (mInstance == null)
{
mInstance = new SnackBarUtils();
}
return mInstance;
}
public void hideSnackBar()
{
if (mSnackBar != null)
{
mSnackBar.dismiss();
}
}
public void showProblemSnackBar(final Activity activity, final String message)
{
mSnackBar = Snackbar.make(activity.findViewById(android.R.id.content), message,
Snackbar.LENGTH_INDEFINITE);
// Changing action button text color
View sbView = mSnackBar.getView();
TextView textView = sbView.findViewById(com.google.android.material.R.id.snackbar_text);
mSnackBar.setAction("x", new View.OnClickListener()
{
#Override
public void onClick(View v)
{
//Call your action method here
mSnackBar.dismiss();
}
});
textView.setTextColor(Color.WHITE);
sbView.setBackgroundColor(Color.RED);
textView.setMaxLines(3);
mSnackBar.show();
}
}
This is my current implementation within main activity, I have already Initialized the snackbar class like this:
SnackBarUtils snackBarUtils;
and then called it like this:
snackBarUtils.showProblemSnackBar(MainActivity.this, mPlainTextResponse);
what am I doing wrong? Or what is the correct way to do this?
First of all, you would share the stacktrace of the NPE for more context.
For the snackbar utility:
If you are using callbacks, then you can use the utility for displaying a snackbar with that callback as parameter:
interface onProblemSnackbarClickedListener {
void onActionClicked(View view);
}
...
/* inside SnackBarUtils.java */
...
public static void showProblemSnackbar(View view, #StringRes int message, onProblemSnackbarClickedListener listener){
Snackbar mSnackBar = Snackbar.make(view,message,Snackbar.LENGTH_INDEFINITE)
.setAction("x", new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.onActionClicked(v);
mSnackBar.dismiss();
}
})
mSnackbar.show();
}
The callback could work for the need to listen to it in the activity/fragment.
For the styling of the Snackbar, you can see this related question:
Style SnackBar in theme app.
Keep in mind the migration from "Support design" to MDC (Material design components), that facilitates the global styling of the snackbar with theme attributes.
This question already has answers here:
How do I print my Java object without getting "SomeType#2f92e0f4"?
(13 answers)
Closed 1 year ago.
I'm making an application, where I have a central recyclerview where items that the user will type are listed, below 2 buttons, one for adding items and the other for finishing. The problem I am having is that when the user finishes the task and clicks on finish I want to get all the data that was listed on his screen that is inside a List and store it in firebase, but I am not able to get this data in shape array, it always returns the object reference.
Searching the internet I found how to use the .toString in the array, but it returns me the same thing.
Here is the example of the return I am receiving.
return
the code of my object
public class ListItem {
private float measure;
public ListItem(float measure) {
this.measure = measure;
}
public float getMeasure() {
return measure;
}
public void setMeasure(float measure) {
this.measure = measure;
}
}
the code of my button add
private View.OnClickListener addItem = new View.OnClickListener() {
#Override
public void onClick(View v) {
DialogForm();
mAdapter.notifyItemInserted(listItems.size());
}
};
void DialogForm() {
AlertDialog.Builder alert = new AlertDialog.Builder(MainActivity.this);
final View customLayout = getLayoutInflater().inflate(R.layout.layout_dialog, null);
alert.setView(customLayout);
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
EditText measure = customLayout.findViewById(R.id.edit_measure);
String response = measure.getText().toString();
if (response.isEmpty() || response.equals("0")) {
Toast.makeText(MainActivity.this, "A medida não pode ser vazia",
Toast.LENGTH_SHORT).show();
} else {
listItems.add(new ListItem(Float.parseFloat(response)));
btnFinish.setEnabled(true);
btnFinish.setBackgroundResource(R.drawable.bg_button_enabled);
}
}
});
AlertDialog dialog = alert.create();
dialog.show();
}
the code of my button finish, when I'm listing
private View.OnClickListener finishListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
for (int i=0; i<listItems.size(); i++){
Log.d("teste", listItems.get(i).toString());
}
}
};
toString() will always return the hash of the object if not override as it is an inherited method from java.lang.Object and not specific to your object
If you want toString to return the measure variable then add the following to your ListItem class
#Override
public String toString() {
return Integer.toString(measure);
}
In order to add more entries to the method, just add another + myData but perhaps ensure there is some sort of divider to make it moire readable - such as + " || " + or something
Good luck with the project
this is probably simple but i am still a beginner
i have a button in class MainActivity and i want to use the onclick method in another class(another activity) i am aware that it could be easily achieved by making it public static and accessing it as instance but in my case i can not make it public static for some good reason
button xml:
<Button
android:id="#+id/googledrivemain"
android:layout_width="78dp"
android:layout_height="77dp"
android:layout_marginTop="149dp"
android:background="#drawable/google"
android:onClick="onClickOpenFile"
android:paddingTop="6dp"
android:textColor="#fff"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageView3"
app:layout_constraintHorizontal_bias="0.51" />
MainActivity button:
public void onClickCreateFile(View view) {
fileOperation = true;
// create new contents resource
Drive.DriveApi.newDriveContents(mGoogleApiClient)
.setResultCallback(driveContentsCallback);
}
second class button:
btn3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity main = new MainActivity();
use onclick from mainactivity...??
}
});
Do it like this
btn3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((MainActivity) getActivity()).onClickCreateFile(v);
}
});
i think it will work.
Simply wrap the code you invoke from the MainActivity.onClickCreateFile() into another class and call it from wherever you want
And never, NEVER, do something like new MainActivity(). Activities are managed by the system
You can create a handler class, something like this:
public static class ClickHandler {
private ClickHandler mInstance;
public static ClickHandler getInstance(){
if(mInstance == null){
mInstance = new ClickHandler();
}
return mInstance;
}
public void onClickCreateFile(View view) {
fileOperation = true;
// create new contents resource
Drive.DriveApi.newDriveContents(mGoogleApiClient)
.setResultCallback(driveContentsCallback);
}
}
Then wherever you need it:
btn3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ClickHandler.getInstance().onClickCreateFile(view);
}
});
The reason you can't make a click method static is that 1) The android:onClick XML action requires public void and 2) The view that you are clicking belong to the instance of the activity, not the class.
You could make a static method like this.
public static void createDriveContents(GoogleApiClient apiClient, ResultCallback callback) {
Drive.DriveApi.newDriveContents(apiClient)
.setResultCallback(callback);
}
But, then you still need a reference to the GoogleApiClient object.
Since no client is bound to any one Activity, you could move it into a singleton in order to access one client instance from everywhere.
For example,
btn3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity.createDriveContents(
GoogleClientWrapper.getInstance().getClient(), // A GoogleApiClient instance
new ResultCallback() {
// TODO: Implement
});
}
});
If you have access to the GoogleClient in the second class, just pass in the API client object directly.
Or, abstracting up one level, you can instead provide a Context, and re-build the client.
public static void createDriveContents(Context context, ResultCallback callback) {
GoogleApiClient client = new GoogleApiClient.Builder(context);
// TODO: add scopes to client
Drive.DriveApi.newDriveContents(apiClient)
.setResultCallback(callback);
}
With this method, we assume the second class is another activity without it's own GoogleClient
btn3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity.createDriveContents(
SecondClass.this, // A Context instance
new ResultCallback() {
// TODO: Implement
});
}
});
I have created my own class to mimick the Snackbar, lets call it CustomSnackbar. What I am trying to achieve is to customize the snackbar and be able to call CustomSnackbar from my main activity and for the usage to be very similar to calling the standard Snackbar. For the purpose of demonstrating my example without all the bulk code, here is my CustomSnackbar class:
package com.wizzkidd.myapp.helpers;
import android.content.Context;
import android.support.design.widget.Snackbar;
import android.util.Log;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class CustomSnackbar {
Context _context;
Snackbar snackbar;
public CustomSnackbar(Context context) {
this._context = context;
}
public void make(View view, CharSequence text, int duration) {
snackbar = Snackbar.make(view, "", duration);
Snackbar.SnackbarLayout snackbarLayout = (Snackbar.SnackbarLayout) snackbar.getView();
TextView textView = (TextView) snackbarLayout.findViewById(android.support.design.R.id.snackbar_text);
textView.setVisibility(View.INVISIBLE); //hide the default snackbar textview
//Create my own textview instead
TextView myTextView = new TextView(_context);
myTextView.setText(text);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); //Create layout params for some text
myTextView.setLayoutParams(params); //Apply the text layout params
snackbarLayout.addView(myTextView); //Add my text to the main snackbar layout. (Other widgets will also be added)
}
public void setAction(CharSequence text) {
snackbar.setAction(text, new View.OnClickListener() {
#Override
public void onClick(View view) {
//do something
Log.v("TAG", "You clicked the action");
}
});
}
public void show() {
snackbar.show();
}
}
In my MainActivity, I am using the class like this:
CustomSnackbar customSnackbar = new CustomSnackbar(activity);
customSnackbar.make(view, "This is my snackbar", Snackbar.LENGTH_INDEFINITE);
customSnackbar.setAction("HIDE");
customSnackbar.show();
You can see that I am using my .setAction method to pass a string/charsequence but I am unsure how to handle the onClickListener in the same call instead of handling the onClickListener inside the class
Please ignore the fact that the class may appear pointless (but this is because I have simplified it for the purpose of this question). I'm not sure that I am creating this class correctly, so any additional advice or suggestions would be appreciated. Thanks.
declare somewhere else your OnClickListener, e.g. in Activity in which you are calling your methods, and pass it to your class
final View.OnClickListener ocl = new View.OnClickListener() {
#Override
public void onClick(View view) {
//do something
Log.v("TAG", "You clicked the action");
}
}
CustomSnackbar customSnackbar = new CustomSnackbar(activity);
customSnackbar.make(view, "This is my snackbar", Snackbar.LENGTH_INDEFINITE);
customSnackbar.setAction("HIDE", ocl);
customSnackbar.show();
and inside your custom class:
public void setAction(CharSequence text, final View.OnClickListener ocl) {
snackbar.setAction(text, ocl);
}
now inside OnClickListener you may call methods from Activity
you have to provide it as parameter to your setAction method. E.g.
public void setAction(CharSequence text, final View.OnClickListener listener) {
and either pass the provided instance, or proxy the call to the other object
Do a class extends Snackbar.
Then addMouseListener(classWithExtendedSnackbar)
I am exactly not clear about your question. But,if you want to make a custom Snackbar which will display all your message, and give functionality on click of it. Then you can try this code.
just call this method to make a snackbar.
//Here I am sending one as code
showMessage("Snackbar Opened","Close",1)
//send different code based on which you can set something to be done, or identify a button click
private void showMessage(String msg, String action_name, final int code) {
progress.cancel();
final Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), msg, Snackbar.LENGTH_INDEFINITE);
snackbar.setAction(action_name, new View.OnClickListener() {
#Override
public void onClick(View v) {
clearDataForListView();
if (code == 1) {
//do something if code is one/if a particular button is clicked
//you can dismiss anwhere else also.
snackbar.dismiss();
} else if (code == 2) {
//do something if another button is pressed
snackbar.dismiss();
}
}
});
snackbar.show();
}
I'm using setOnClickListener for listening on the click event on imageButton in two methods, but it's does not fire in my another method,my first listener firing but my second listener does not fire please see my codes :
Class FirstActivity extends BaseActivity
{
#Override
public void onCreate()
{
super.onCreate();
this.methodA();
this.methodB();
}
public void methodA()
{
ImageButton imageButton = (ImageButton) RContextHelper.getActivity().findViewById(R.id.my_location_button);
imageButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
//event firing when image button touched
}
});
}
public void methodB()
{
Test test = new Test(this);
test.methodA();
}
}
class Test
{
Context con;
public Test(Context con)
{
this.con = con;
}
public void methodA()
{
ImageButton imageButton = (ImageButton) getActivity().findViewById(R.id.my_location_button);
imageButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
//event does not fire when image button touched
}
});
}
protected ActionBarActivity getActivity()
{
return (ActionBarActivity) con;
}
}
As you can guess from the name setOnClickListener sets the new listener and replaces the old one. That is the case with all the set* listeners in Java. If it was addOnClickListener then you could expect that both listeners should be called.
If you want both of them to be called, you can write a composite on click listener and add both of the listeners to it and set the composite listener to the target.
class CompositeListener implements OnEventListener {
private List<OnEventListener> registeredListeners = new ArrayList<OnEventListener>();
public void registerListener (OnEventListener listener) {
registeredListeners.add(listener);
}
public void onEvent(Event e) {
for(OnEventListener listener:registeredListeners) {
listener.onEvent(e);
}
}
}
And then:
CompositeListener composite = new CompositeListener();
composite.registerListener(listener1);
composite.registerListener(listener2);
imageButton.setOnEventListener(composite);
Source
Very confusing to code with two methodA functions. You never call the second one. At least you are not showing code for that. Moreover - as has been said already - there can only be one listener.