Получение активности из контекста в Android

184

Это меня поставило в тупик.

Мне нужно вызвать метод деятельности из пользовательского класса макета. Проблема в том, что я не знаю, как получить доступ к активности из макета.

ProfileView

public class ProfileView extends LinearLayout
{
    TextView profileTitleTextView;
    ImageView profileScreenImageButton;
    boolean isEmpty;
    ProfileData data;
    String name;

    public ProfileView(Context context, AttributeSet attrs, String name, final ProfileData profileData)
    {
        super(context, attrs);
        ......
        ......
    }

    //Heres where things get complicated
    public void onClick(View v)
    {
        //Need to get the parent activity and call its method.
        ProfileActivity x = (ProfileActivity) context;
        x.activityMethod();
    }
}

ProfileActivity

public class ProfileActivityActivity extends Activity
{
    //In here I am creating multiple ProfileViews and adding them to the activity dynamically.

    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.profile_activity_main);
    }

    public void addProfilesToThisView()
    {
        ProfileData tempPd = new tempPd(.....)
        Context actvitiyContext = this.getApplicationContext();
        //Profile view needs context, null, name and a profileData
        ProfileView pv = new ProfileView(actvitiyContext, null, temp, tempPd);
        profileLayout.addView(pv);
    }
}

Как вы можете видеть выше, я создаю экземпляр profileView программно и передаю им ActivityContext. 2 вопроса:

  1. Я передаю правильный контекст в профиль?
  2. Как я могу получить содержащую деятельность из контекста?
обертон
источник

Ответы:

473

От вашего Activity, просто введите thisкак Contextдля вашего макета:

ProfileView pv = new ProfileView(this, null, temp, tempPd);

После этого вы будете иметь Contextв макете, но вы будете знать, что это на самом деле ваш, Activityи вы можете разыграть его так, чтобы у вас было то, что вам нужно:

Activity activity = (Activity) context;
Борис Странджев
источник
53
Вы не можете быть уверены, что контекст, с которым вы работаете, является контекстом активности или контекстом приложения. Попробуйте передать контекст приложения в DialogView, посмотрите, как он падает, и вы увидите разницу.
Скай Келси
6
Борис, вопрос спрашивает, есть ли способ получить действие из контекста. Это невозможно. Конечно, вы можете сыграть, но это последнее средство. Если вы хотите трактовать контекст как действие, не переходите к действию. Это упрощает код и менее подвержено ошибкам позже, когда ваш код обслуживает другой человек.
Скай Келси
6
Обратите внимание, что getApplicationContext () вместо this не будет работать.
dwbrito
1
@BorisStrandjev Я не совсем понял ваш комментарий. Во всяком случае, я сказал, что после того, как попробовал ваш пример, но вместо «этого» я ​​использовал getApplicationContext (), и приложение попыталось привести само приложение, следовательно, выдало ошибку преобразования вместо действия. После переключения на «это», как вы ответили, все заработало.
dwbrito
1
Ответы, получившие наибольшее количество голосов по вашей ссылке, предлагают поставить вопрос, если он вонючий. Этот вопрос, конечно, вонючий. ОП сначала заявил: «Мне нужно вызвать метод действия из пользовательского класса макета». что вполне достижимо при соответствующем использовании интерфейсов. Затем он говорит: «Проблема в том, что я не знаю, как получить доступ к активности из макета». что является значительным намеком на недоразумение. Люди все время пытаются программировать, и мы не должны закрывать на это глаза.
Сэм
39

Это то , что я успешно используется для преобразования Contextв Activityпри работе в пользовательском интерфейсе в фрагментов или пользовательских представлений. Он будет распаковывать ContextWrapper рекурсивно или вернет ноль в случае сбоя.

public Activity getActivity(Context context)
{
    if (context == null)
    {
        return null;
    }
    else if (context instanceof ContextWrapper)
    {
        if (context instanceof Activity)
        {
            return (Activity) context;
        }
        else
        {
            return getActivity(((ContextWrapper) context).getBaseContext());
        }
    }

    return null;
}
Тео
источник
Это правильный ответ. Другие не учитывают иерархию ContentWrapper.
Сниколас
Это верный ответ :)
lygstate
1
@lygstate: Какой целевой уровень API вы используете в своем приложении? В чем ошибка? Это работает только в пользовательском интерфейсе (действия, фрагменты и т. Д.), Но не в службах.
Тео
31
  1. нет
  2. Ты не можешь

В Android есть два разных контекста. Одна для вашего приложения (назовем это БОЛЬШОЙ) и одна для каждого представления (назовем это контекстом активности).

LinearLayout - это представление, поэтому вы должны вызвать контекст действия. Чтобы вызвать это из действия, просто назовите это. Так просто, не правда ли?

Когда вы используете

this.getApplicationContext();

Вы вызываете БОЛЬШОЙ контекст, который описывает ваше приложение и не может управлять вашим представлением.

Большая проблема с Android состоит в том, что контекст не может вызвать вашу активность. Это большая проблема, чтобы избежать этого, когда кто-то начинает разработку Android. Вы должны найти лучший способ кодирования вашего класса (или заменить «Контекст контекста» на «Активность действия» и при необходимости привести его к «Контексту»).

С уважением.


Просто чтобы обновить мой ответ. Самый простой способ получить ваш Activity context- это определить staticэкземпляр в вашем Activity. Например

public class DummyActivity extends Activity
{
    public static DummyActivity instance = null;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // Do some operations here
    }

    @Override
    public void onResume()
    {
        super.onResume();
        instance = this;
    }

    @Override
    public void onPause()
    {
        super.onPause();
        instance = null;
    }
}

А потом, в вашем Task, Dialog, View, вы можете использовать этот вид кода , чтобы получить ваши Activity context:

if (DummyActivity.instance != null)
{
    // Do your operations with DummyActivity.instance
}
Манитоба
источник
4
+1 за объяснение очень распространенной области путаницы между двумя различными типами контекстов (точно так же, как есть 2 различных типа R). Люди из Google должны пополнить свой словарный запас.
an00b
3
Кстати, @BorisStrandjev это правильно: 2. Да, вы можете . (не могу поспорить с рабочим кодом)
an00b
2
2. Не совсем. Если бы контекст был контекстом приложения, то ваше приложение зависло бы.
Переполнение стека
статический экземпляр ?! У @Nepster есть лучшее решение для этого ИМО
Сэм
14
Создание статической ссылки на Activity - лучший способ утечки памяти.
BladeCoder
8

Если вы хотите вызывать метод действия из пользовательского класса макета (не-класса деятельности). Вам следует создать делегат с использованием интерфейса.

Это не проверено, и я правильно написал. но я передаю способ достичь того, что вы хотите.

Прежде всего создать и интерфейс

interface TaskCompleteListener<T> {
   public void onProfileClicked(T result);
}



public class ProfileView extends LinearLayout
{
    private TaskCompleteListener<String> callback;
    TextView profileTitleTextView;
    ImageView profileScreenImageButton;
    boolean isEmpty;
    ProfileData data;
    String name;

    public ProfileView(Context context, AttributeSet attrs, String name, final ProfileData profileData)
    {
        super(context, attrs);
        ......
        ......
    }
    public setCallBack( TaskCompleteListener<String> cb) 
    {
      this.callback = cb;
    }
    //Heres where things get complicated
    public void onClick(View v)
    {
        callback.onProfileClicked("Pass your result or any type");
    }
}

И реализовать это в любой деятельности.

и называть это как

ProfileView pv = new ProfileView(actvitiyContext, null, temp, tempPd);
pv.setCallBack(new TaskCompleteListener
               {
                   public void onProfileClicked(String resultStringFromProfileView){}
               });
Zar E Ahmer
источник
1
Это правильный ответ и должен быть помечен как правильный ответ. Я знаю, что ответ, помеченный как правильный, на самом деле отвечает на вопрос ОП, но он не должен отвечать на этот вопрос. Дело в том, что не рекомендуется передавать в Activity вид, подобный этому, внутри представления. Ребенок никогда не должен знать о своих родителях в любом случае, кроме как через Context. Как утверждает Непстер, лучше всего передавать обратный вызов, поэтому всякий раз, когда что-то случается, представляющее интерес для родителя, обратный вызов запускается с соответствующими данными.
Дарвинд
6

Контекстом может быть Приложение, Сервис, Активность и многое другое.

Обычно контекст Views в Activity - это сама Activity, поэтому вы можете подумать, что можете просто преобразовать этот Context в Activity, но на самом деле вы не всегда можете это сделать, потому что в этом случае контекст также может быть ContextThemeWrapper.

ContextThemeWrapper интенсивно используется в последних версиях AppCompat и Android (благодаря атрибуту android: theme в макетах), поэтому я бы лично никогда не выполнял это приведение.

Итак, короткий ответ: вы не можете надежно извлечь действие из контекста в представлении. Передайте Activity в представление, вызвав для него метод, который принимает Activity в качестве параметра.

BladeCoder
источник
3

Никогда не используйте getApplicationContext () с представлениями.

Это всегда должен быть контекст деятельности, поскольку представление привязано к деятельности. Кроме того, у вас может быть набор пользовательских тем, и при использовании контекста приложения все темы будут потеряны. Узнайте больше о различных версиях контекстов здесь .

Ломжа
источник
3

А в Котлине

tailrec fun Context.activity(): Activity? = when {
  this is Activity -> this
  else -> (this as? ContextWrapper)?.baseContext?.activity()
}
rjrjr
источник
0

Activity - это специализация Context, поэтому, если у вас есть Context, вы уже знаете, какую деятельность вы намереваетесь использовать, и можете просто привести a к c ; где a - это активность, а c - это контекст.

Activity a = (Activity) c;
ACLIMA
источник
7
Это опасно, потому что, как упоминалось в отдельном комментарии, контекст не всегда может быть действием.
4
typecast только if (context instanceof Activity) {// typecast}
Амит Ядав
0

Я использовал конвертировать активность

Activity activity = (Activity) context;
Самуэль Иван
источник
2
Существуют разные виды контекстов. Действия и Приложения могут иметь контексты. Это будет работать только тогда, когда контекст имеет вид деятельности.
cylov
0

Этот метод должен быть полезным ..!

public Activity getActivityByContext(Context context){

if(context == null){
    return null;
    }

else if((context instanceof ContextWrapper) && (context instanceof Activity)){
        return (Activity) context;
    }

else if(context instanceof ContextWrapper){
        return getActivity(((ContextWrapper) context).getBaseContext());
    }

return null;

    }

Я надеюсь, что это помогает .. Веселое кодирование!

Таслим Осени
источник
Убедитесь, что контекст, который вы передали, не равен нулю. Скорее всего, это проблема.
Taslim Oseni
0

как насчет живого обратного вызова данных,

class ProfileView{
    private val _profileViewClicked = MutableLiveData<ProfileView>()
    val profileViewClicked: LiveData<ProfileView> = _profileViewClicked
}

class ProfileActivity{

  override fun onCreateView(...){

    profileViewClicked.observe(viewLifecycleOwner, Observer { 
       activityMethod()
    })
  }

}
Абхинав Атул
источник