0

I have only one activity in my app. Before I just stored my views and dialogs static in the activity, so I could access them from anywhere. But I know that this is bad practice because it leads to memory leaks.

So I made them non-static, but now I need to have a reference to my activity deep down in my view hierarchy, to access the views and dialogs stored in the activity.

Example:

My MainActivity has a dialog called a and a custom view called b. How can the onClick method of b show the dialog a? enter image description here

or in code:

public class MainActivity extends Activity {
    private CustomDialog a;
    private CustomView b;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        a = new CustomDialog(this);
        b = new CustomView(this);
    }
}


public class CustomView extends Button implements OnClickListener {
    public CustomView(Context context) {
        super(context);
        setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        //wants to show dialog a
        MainActivity.a.show(); //Not possible -> a is not static
        mainActivity.a.show(); //<-- needs a reference of the activity
                               //    but where from?
    }
}

MainActivity mainActivity = (MainActivity) getContext(); won't work because getContext() is not always an activity context.

UPDATE:

I posted an answer below! For some reasons StackOverflow only lets me accept my own answer in two days

Mano176
  • 168
  • 13

3 Answers3

0

I do not know what exactly your view hierarchy looks like.

I picture your problem for example as: Activity A has a recyclerview R, now every viewholder H in R should be able to trigger some method in A.

In such a scenario it would be feasable to pass a reference of your activity to your recyclerview adapter and then the adapter passes it to the ViewHolder. Which then uses it in the onClick method of your (viewholder's) view. Here, you could use the "callback" pattern. There are many posts about this on stackoverflow, e.g. here.

So the implementation steps would be:

  1. define interface
  2. let your activity implement that interface
  3. let your adapter take the interface as a constructor parameter and pass your activity. (in this example: you have to repeat the step with your viewHolder, pass the interface from the adapter)
  4. use this interfaces method in the onClick method -> this will then trigger your activities method

The implementation depends on the actual hierarchy. If your other view is in a fragment, then you could also use a (shared) ViewModel. According to your picture I was thinking of the callback-pattern approach first.

cewaphi
  • 410
  • 2
  • 7
  • Also a good suggestion from Sid. It really depends on the relation of your views with each other. If you do not want to define the onClick method inside the activity, then you can do it similar to his approach with the callback. Then besides `this` you only need to pass the `interface` (implemented by your activity) – cewaphi Aug 11 '20 at 15:43
  • @Mano176 you asked for a "best" way. Unwrapping the context does not seem like it should be done. The android documentation recommends to use an interface when communicating with an activity. In the example the activity communicates with a fragment, but it could be any other class as well. https://developer.android.com/training/basics/fragments/communicating#DefineInterface – cewaphi Aug 11 '20 at 19:42
0

You could override onClick in MainActivity; there is probably no need to do it in the class definition itself.

public class MainActivity extends Activity {
    private CustomDialog a;
    private CustomView b;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        a = new CustomDialog(this);
        b = new CustomView(this);
        
        b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                a.show();
            }
        });

    }
}

This is a very common pattern in android and I don't know what your view hierarchy looks like but it should work in most cases. I am having trouble understanding why any class extending Button would need to implement View.OnClickListener. It makes much more sense to create listeners in activities or have MainActivity implement OnClickListener.

Sid
  • 66
  • 8
  • I am doing this in some cases. But since all of my CustomViews are supposed to do the same thing, I don't want to override the onClicks in my activity. Also my activity class would become very long then. But Thanks for suggesting it! :) (Btw, the view extending a button and implementing the onclicklistener was just for the example) – Mano176 Aug 11 '20 at 15:49
0

A few minutes a go there was an answer here that turned out to be correct. I don't know why the author deleted it, but it had a link to this answer:

private static Activity unwrap(Context context) {
    while (!(context instanceof Activity) && context instanceof ContextWrapper) {
        context = ((ContextWrapper) context).getBaseContext();
    }
    return (Activity) context;
}

So everytime you need the activity you just can call Activity activity = unwrap(getContext());.

I don't know if it is really intended to do it that way or if it is a workaround, but it does its job (atleast in my case).

Mano176
  • 168
  • 13