I have an ArrayList of Buttons where my OCL needs to know which index I has been pressed.
The plan is something like this:
MyOnClickListener onClickListener = new MyOnClickListener() {
#Override
public void onClick(View v) {
Intent returnIntent = new Intent();
returnIntent.putExtra("deleteAtIndex",idx);
setResult(RESULT_OK, returnIntent);
finish();
}
};
for (int i =0;i<buttonList.size();i++) {
buttonList.get(i).setText("Remove");
buttonList.get(i).setOnClickListener(onClickListener);
}
How does my implementation of the OCL need to look like ?
Currently I have this:
public class MyOnClickListener implements OnClickListener{
int index;
public MyOnClickListener(int index)
{
this.index = index;
}
#Override
public void onClick(View arg0) {
}
}
However, I am unsure of what I need to do within the constructor of my OCL, aswell as the overriden onClick function.
set setOnClickListener to Button as :
buttonList.get(i).setOnClickListener(new MyOnClickListener(i));
EDIT :
I need to finish an activity in myOCL, how would I do that?
for finishing Activity on Button Click from non Activity class you will need to pass Activity Context to your custom OnClickListener as :
buttonList.get(i).setOnClickListener(new MyOnClickListener(i, Your_Current_Activity.this));
and change the Constructor of your custom OnClickListener class to :
int index;
Context context;
public MyOnClickListener(int index,Context context)
{
this.index = index;
this.context=context;
}
#Override
public void onClick(View arg0) {
// now finish Activity as\
context.finish();
// OR
// ((Activity)context).finish();
}
View.OnClickListener myListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("Button",v.getText().tostring);
}
});
you will get your button value in view so, you will find it which index is.
change your for loop and create new Object every time and pass the index in constructor.
for (int i =0;i<buttonList.size();i++) {
buttonList.get(i).setText("Remove");
buttonList.get(i).setOnClickListener(new MyOnClickListener(i));
}
You can get index in your onClick Method.
I'm not sure this applies specifically to your case, but I had the desire to create a custom listener for clickable elements to prevent them being clicked twice (if the user taps quickly).
My solution was to create a listener class that I pass a custom Runnable to and then handle that Runnable if the button is being clicked the first time, but obviously you can pass any custom behavior in, this is just one very simple illustration of how to use it...
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import android.view.View;
public class MyCustomOnClickListener implements View.OnClickListener {
public static final String TAG = MyCustomOnClickListener.class.getSimpleName();
private Runnable doOnClick;
private Context mContext;
private int isClicked = 0;
public MyCustomOnClickListener(Context c, Runnable doOnClick) throws Exception{
if(!(c instanceof Context) || !(doOnClick instanceof Runnable))
throw new Exception("MyCustomOnClickListener needs to be invoked with Context and Runnable params");
this.doOnClick = doOnClick;
this.mContext = c;
}
#Override
public void onClick(View view) {
Log.v(TAG,"onClick() - detected");
if(isClicked++ > 0)return; //this prevents multiple clicks
Log.d(TAG, String.format("onClick() - being processed. View.Tag = \"%s\"", view.getTag().toString()));
new Handler(mContext.getMainLooper()).post(doOnClick);
}
}
and then to use it (assuming you're in an Activity for the this context)...
try{
Button aButton = findViewById(R.id.someButton);
aButton.setOnClickListener(new MyCustomOnClickListener(/*context*/this, new Runnable(){
public void run(){
//here you would put whatever you would have otherwise put in the onClick handler. If you need the View that's being clicked you can replace the Runnable with a custom Runnable that passes the view along
}
}));
}catch(...){
}
Related
Aim:
I'm looking for a way to append functionality to a button's onClickListener.
Illustration
Button trigger = new Button(getActivity());
trigger.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
method1();
}
});
Button runMethod2Button = new Button(getActivity());
runMethod2Button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
method1();
method2();
}
});
Button runMethod3Button = new Button(getActivity());
runMethod3Button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
method1();
method3();
method4();
}
});
I know we can do this with inheritance by calling
#Override
public void method(){
super.method();
// Do appended stuff
}
Or we can do it inline
new Object(){
#Override
public void method(){
super();
// Do appended stuff
}
}
Things I've Tried
Extending the button to contain a list of Runnable Objects.
Then set the on click listener to trigger all of the runnable objects.
Is there a different/more efficient way of doing this?
Since we don't no much about the background why you want to do so, it is hard to what is the best. If you want to have the original listener unchanged / untouched, you could use a decorator / wrapper pattern.
Wikipedia Decorator Pattern
In the concrete case this means, it is quite comparable to your Runnable approach, but you do not depend on another Interface. Everthing is handled via the View.OnClickListener, which has the following advantages:
It is a generic approach with which you can even "extend" listeners to which you have no source access or which you do not want to modify.
You can create the extension behaviour at runtime and you can extend already instantiated listeners (in contrast to the use of inheritance)
The extensions do not have to know that they are extensions, they are just normal OnClickListeners. In your Runnable approach the extensions are "special" and for example they do not get the View paramter of the onClick method passed.
public class OriginalOnClickListener implements View.OnClickListener{
#Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"Original Click Listener",Toast.LENGTH_LONG).show();
}
}
public class ExtensionOnClickListener implements View.OnClickListener{
#Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"Extension Click Listener",Toast.LENGTH_LONG).show();
}
}
public class DecoratorOnClickListener implements View.OnClickListener {
private final List<View.OnClickListener> listeners = new ArrayList<>();
public void add(View.OnClickListener l) {
listeners.add(l);
}
#Override
public void onClick(View v) {
for(View.OnClickListener l : listeners){
l.onClick(v);
}
}
}
And the usage is like this:
DecoratorOnClickListener dl = new DecoratorOnClickListener();
dl.add(new OriginalOnClickListener());
dl.add(new ExtensionOnClickListener());
editText.setOnClickListener(dl);
I think the Runnable idea is okay, based on what you've said here. Seeing as I don't really know why you need dynamic click handlers, I think a possible solution would look something like this:
private class DynamicOnClickListener implements View.OnClickListener {
private final List<Runnable> mRunnables = new ArrayList<>();
public void add(Runnable r) {
mRunnables.add(r);
}
#Override
public void onClick(View v) {
for (Runnable r : mRunnables) {
r.run();
}
}
}
And you'd use it like this:
DynamicOnClickListener listener = new DynamicOnClickListener();
listener.add(new Runnable() {
#Override
public void run() {
doSomething();
}
});
listener.add(new Runnable() {
#Override
public void run() {
doSomethingElse();
}
});
mButton.setOnClickListener(listener);
what about something like
Button trigger = new Button(getActivity());
trigger.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
method1();
if (someVar) method2();
if (someVar2) method3();
}
})
I want to create by code an array of objects that are subclasses of Button.
public class MyButton extends Button {
private Context ctx;
private int status;
public MyButton(Context context) {
super(context);
ctx = context;
status = 0;
}
private click() {
status = 1;
// OTHER CODE THAT NEEDS TO STAY HERE
}
}
In the main activity I do this:
public class myActivity extends Activity {
private MyButton[] myButtons = new MyButton[100];
#Override
public onCreate(Bundle si) {
super.onCreate(si);
createButtons();
}
private void createButtons() {
for (int w=0; w<100; w++) {
myButtons[w] = new MyButton(myActivity.this);
myButtons[w].setOnClickListener(new View.onClickListener() {
public void onClick(View v) {
// ... (A)
}
});
}
}
}
Now I want the click() method inside MyButton to be run each time the button is clicked.
Seems obvious but it is not at my eyes.
If I make the click() method public and run it directly from (A), I get an error because myButtons[w].click() is not static and cannot be run from there.
In the meantime, I an not able to understand where to put the code in the MyButton class to intercept a click and run click() from there. Should I override onClick? Or should I override onClickListener? Or what else should I do?
How can I run click() whenever one of myButtons[] object is clicked?
Thanks for the help.
You can cast View v you got in listener to MyButton and call click on it:
private void createButtons() {
View.OnClickListener listener = new View.onClickListener() {
public void onClick(View v) {
((MyButton) v).click();
}
};
for (int w=0; w<100; w++) {
myButtons[w] = new MyButton(myActivity.this);
myButtons[w].setOnClickListener(listener);
}
}
you can add:
View.onClickListener onclick = new View.onClickListener() {
public void onClick(View v) {
((MyButton)v).click();
//since v should be instance of MyButton
}
};
to your Activity
then use:
myButtons[w].setOnClickListener(onclick);
//one instance of onclick is enough, there is no need to create it for every button
in createButtons()
but ... why, oh why array of buttons we have ListView in android ...
My Activity has multiple lists so I have defined MyClickListener as below:
My question is how I should instantiate this class:
MyClickListener mMyClickListener = new MyClickListener();
Or maybe it is better to instantiate inside the onCreate(Bundle) and just define above. Whats considered the better way? I don't want too much in onCreate() its already full of stuff. Any thoughts on the declaration and instatiation? Whats the best way?
private class MyClickListener implements OnClickListener
{
#Override
public void onClick(View view) {
}
}
I use same kind of class mechanism as you mentioned in the question.
this is the way i use,
public class myActivity extends Activity
{
private MyListener listener = null;
private Button cmdButton = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
cmdButton = (Button) findViewById(R.id.cmdButton);
cmdButton.setOnClickListener(getListener());
}
// method to fetch the listener object
private MyListener getListener()
{
if (listener == null)
{
listener = new MyListener();
}
return listener;
}
private class MyListener implements Button.OnClickListener
{
public void onClick(View v)
{
}
}
}
Why are you instantiating a listener like that in the first place? Just create a new one when you assign it to your listView.
listView.setOnClickListener( new MyListener());
I have made a custom component like Mybutton.java
and I have set an onclick listener in Mybutton.java.
Now, in my new activity, I have to call a Mybutton
and add content in onclick listener.
However, if I use OnClickListener mClickListener = new OnClickListener(){......
it will replace the old content.
I hope it can do the old and new listener together.
I have searched for some information, found out i can implement this method.
After many attempts, I'm still getting errors.
Can anyone give me a simple example
that i can learn to modify it?
I don't think there's an API in the Android API that allows multiple onClick listeners. You'd need some custom class that handles a single onClick() and pass in handlers for it to call. Something like this:
private class CompositeOnClickListener implements View.OnClickListener{
List<View.OnClickListener> listeners;
public CompositeOnClickListener(){
listeners = new ArrayList<View.OnClickListener>();
}
public void addOnClickListener(View.OnClickListener listener){
listeners.add(listener);
}
#Override
public void onClick(View v){
for(View.OnClickListener listener : listeners){
listener.onClick(v);
}
}
}
When your setting your buttons, do:
CompositeOnClickListener groupListener = new CompositeOnClickListener();
myButton.setOnClickListener(groupListener);
Then, whenever you want to add another listener, just call
groupListener.addOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
**** Custom implementation ****
}
});
You could create your custom Button class something like this :
public class MyButton extends Button {
private CustomOnClickListener mCustomOnClickListener;
public interface CustomOnClickListener {
void onClick(View v);
}
public MyButton(Context context) {
super(context);
// Set your own onClickListener
View.OnClickListener ocl = new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do whatever you want to do
// Invoke the other added onclick listener
if(mCustomOnClickListener != null) {
mCustomOnClickListener.onClick(v);
}
}
};
setOnClickListener(ocl);
}
// use this function to set the other onclick listener
public void setCustomOnClickListener(CustomOnClickListener cl) {
mCustomOnClickListener = cl;
}
}
and, use it like this :
// create your button
MyButton button = new MyButton(context);
// add your custom onClickListener
button.setCustomOnClickListener(new MyButton.CustomOnClickListener() {
#Override
public void onClick(View v) {
// Do whatever you intend to do after the actual onClickListener
// from MyButton class has been invoked.
}
});
If you want to execute some internal logic in your custom view's onClick and want to execute the externally set up OnClickListener's logic, I think a simple way is overriding setOnClickListener as below.
In Kotlin:
override fun setOnClickListener(externalOnClickListener: View.OnClickListener?) {
val internalOnClickListener = View.OnClickListener { view ->
//Your awesome internal logic
externalOnClickListener?.onClick(view)
}
super.setOnClickListener(internalOnClickListener)
}
Same in Java:
#Override
public void setOnClickListener(#Nullable final View.OnClickListener externalOnClickListener) {
View.OnClickListener internalOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View view) {
//Your awesome internal logic
if (externalOnClickListener != null) {
externalOnClickListener.onClick(view);
}
}
};
super.setOnClickListener(internalOnClickListener);
}
On the event invocation of an activity, I opened an AlertDialog.Builder which lists an array of single choice items. When the user clicks any item, I want to set the same to a text view in the activity.
I tried this:
Activity class:
public MyActivity extends Activity implements onClickListener {
TextView item;
public void onCreate(Bundle state) {
super.onCreate(savedState);
setContentView(R.layout.main);
item = (TextView) findViewById(R.id.id_item);
item .setOnClickListener(this);
}
public void onClick(View v) {
new MyBuilder(this).show();
updateUI();
}
private void updateUI() {
item.setText(ItemMap.item);
}
}
Builder class:
public class MyBuilder extends AlertDialog.Builder implements OnClickListener{
Context context;
String[] items = {"pen", "pencil", "ruler"};
public MyBuilder(Context context) {
super(context);
super.setTitle("Select Item");
this.context = context;
super.setSingleChoiceItems(items, 0, this);
}
#Override
public void onClick(DialogInterface dialog, int position) {
ItemMap.item = items[position];
dialog.dismiss();
}
}
Mapping class:
public class ItemMap {
public static String item;
}
Here, MyBuilder is a subclass extending AlertDialog.Builder
updateUI() tries to set the value which user chooses from the list of items. But it did not work! updateUI() is called soon after the dialog is shown.
Could anyone help me out?
Thanks in advance!
With updateUI() in the current location, you are trying to access ItemMap.item before it is set in the AlertDialog.Builder. You're going to need some way to call back from the onClick in the AlertDialog.Builder to your main class - I would do it by adding an interface and then passing that in to your builder - like this:
Activity class:
public MyActivity extends Activity implements onClickListener, AlertBuilderCallback {
TextView item;
public void onCreate(Bundle state) {
super.onCreate(savedState);
setContentView(R.layout.main);
item = (TextView) findViewById(R.id.id_item);
item .setOnClickListener(this);
}
public void onClick(View v) {
new MyBuilder(this).addCallback(this).show();
updateUI();
}
public void updateUI() {
item.setText(ItemMap.item);
}
}
AlertBuilderCallback interface:
public interface AlertBuilderCallback {
public void updateUI();
}
Builder class:
public class MyBuilder extends AlertDialog.Builder implements OnClickListener{
Context context;
String[] items = {"pen", "pencil", "ruler"};
public MyBuilder(Context context) {
super(context);
super.setTitle("Select Item");
this.context = context;
super.setSingleChoiceItems(items, 0, this);
}
public MyBuilder addCallback(AlertBuilderCallback callBack) {
this.callBack = callBack;
return this;
}
#Override
public void onClick(DialogInterface dialog, int position) {
ItemMap.item = items[position];
if(this.callBack != null) {
this.callBack.updateUI();
}
dialog.dismiss();
}
}
Mapping class:
public class ItemMap {
public static String item;
}
move the updateUI() from MyActivity onClick(), to onClick for Dialog.
#Override
public void onClick(DialogInterface dialog, int position) {
ItemMap.item = items[position];
updateUI();
dialog.dismiss();
}
You're doing a load of things wrong here. You could move the updateUI() in to the onClick in your Activity, which should work, but here's another few things to think about:
Why is your AlertDialog.Builder in a different class? this is alright if you are going to extend it with some extra behaviour and use it in other places in your application - if if you are only using it here then you should declare it inside your activity.
Why is your ItemMap.item static? Is that a design decision?