Отримання активності з контексту в 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();
    }
}

ПрофільАктивність

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);
    }
}

Як ви бачите вище, я інстанціюю профільView програмно і передаю в нього ActivityContext. 2 питання:

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

Відповіді:


472

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

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

Згодом у вас з'явиться Contextмакет, але ви будете знати, що він насправді ваш, Activityі ви можете подати його так, щоб мати те, що вам потрібно:

Activity activity = (Activity) context;

53
Ви не можете гарантувати, що контекст, з яким ви працюєте, це контекст діяльності або контекст програми. Спробуйте передати контекст програми в DialogView, спостерігайте, як він руйнується, і ви побачите різницю.
Sky Kelsey

6
Борис, питання задає, чи є спосіб отримати активність з контексту. Це неможливо. Звичайно, ти можеш кинути участь, але це в крайньому випадку. Якщо ви хочете ставитись до контексту як до діяльності, тоді не спускайтесь на активність. Це спрощує код і менше схильний до помилок пізніше, коли інша людина підтримує ваш код.
Sky Kelsey

6
Зауважте, що "getApplicationContext ()" замість "цього" не працюватиме.
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.
Snicolas

Це правдива відповідь :)
lygstate

1
@lygstate: Який цільовий рівень API ви використовуєте у своєму додатку? Що таке помилка? Це працює лише в інтерфейсі користувача (діяльність, фрагменти тощо), а не в Сервісах.
Тео

31
  1. Немає
  2. Ви не можете

В Android є два різних контексти. Один для вашої програми (назвемо це BIG one) та один для кожного перегляду (назвемо це контекстом діяльності).

Лінійний макет - це перегляд, тому вам доведеться викликати контекст діяльності. Щоб зателефонувати йому із діяльності, просто зателефонуйте "це". Так просто, чи не так?

Коли ви використовуєте

this.getApplicationContext();

Ви викликаєте BIG контекст, той, що описує вашу програму і не може керувати вашим переглядом.

Велика проблема 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 різних Rs). Люди повинні збагатити свій словниковий запас.
an00b

3
BTW, @BorisStrandjev правильний: 2. Так, можна . (не можу посперечатися з робочим кодом)
an00b

2
2. Не дуже. Якщо контекст був контекстом програми, то ваш додаток вийде з ладу.
StackOverflowed

статичний екземпляр ?! @Nepster має найкраще рішення для цього imo
Сем

14
Створення статичної посилання на діяльність - це найкращий спосіб створити витоки пам'яті.
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){}
               });

1
Це правильна відповідь і її слід позначати як правильну відповідь. Я знаю, що відповідь, позначена як правильна, насправді відповідає на питання ОП, але вона не повинна відповідати на таке питання. Справа в тому, що не так добре проводити таку діяльність як у внутрішньому представленні. Дитина ніколи не повинен знати про їхніх батьків в будь-якому випадку, крім як через Context. Як зазначає Nepster, найкраща практика - це передача зворотного дзвінка, тому щоразу, коли щось станеться цікавим для батьків, зворотний виклик буде видалено з відповідними даними.
Дарвінд

6

Контекст може бути додатком, службою, діяльністю тощо.

Зазвичай контекст переглядів у діяльності є самою діяльністю, тому ви можете подумати, що ви можете просто передати цей контекст діяльності, але насправді ви не завжди можете це зробити, оскільки контекст також може бути ContextThemeWrapper у цьому випадку.

ContextThemeWrapper активно використовується в останніх версіях AppCompat та Android (завдяки атрибуту android: тема в макетах), тому я особисто ніколи не виконував би цей склад.

Отже, коротка відповідь: ви не можете надійно отримати активність із контексту в представленні. Передайте активність перегляду, зателефонувавши на нього, який приймає активність як параметр.


3

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

Це завжди повинен бути контекстом діяльності, оскільки погляд прив’язаний до діяльності. Також у вас може бути встановлений спеціальний набір тем, і при використанні контексту програми всі теми будуть втрачені. Детальніше про різні версії контекстів читайте тут .


3

А в Котліні:

tailrec fun Context.activity(): Activity? = when {
  this is Activity -> this
  else -> (this as? ContextWrapper)?.baseContext?.activity()
}

0

Діяльність є конкретизацією контексту так, якщо у вас є контекст ви вже знаєте, діяльність ви збираєтеся використовувати , і може просто кинути в с ; де a - це діяльність, а c - контекст.

Activity a = (Activity) c;

7
Це небезпечно, оскільки, як згадується в окремому коментарі, контекст не завжди може бути діяльністю.

4
typecast тільки якщо (контекстний примірник діяльності) {// typecast}
Amit Yadav

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;

    }

Я сподіваюся, що це допоможе .. Веселого кодування!


Переконайтеся, що контекст, який ви передали, не є нульовим. Це, швидше за все, проблема.
Таслім Осені

0

як щодо деяких зворотних дзвінків даних,

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

class ProfileActivity{

  override fun onCreateView(...){

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

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.