Как я могу запускать событие Onclick программно?

У меня есть пользовательский вид с двумя линейными макетами: первый — это заголовок представления, а второй — представление сведений.

В пользовательском представлении OnClickListener заголовка Linearlayout уже определен: при срабатывании он сворачивает/раскрывает второй linearlayout.

Что я хочу сделать, так это добавить больше функций в событие OnClickListener моего заголовка (например: свернуть/развернуть второй макет и показать Toast).

Я не могу изменить исходный код пользовательского представления. Я попытался установить новый OnClickListener, но он скрывает начальное событие (свернуть/развернуть).

Как мне это реализовать?

Исходный код My Custom View:

public class ExpandoLayout extends ViewGroup
{
    /* some declarations */
    private Linearlayout header;
    private linearlayout footer;

    /* some code */
    @override
    protected void onFinishInflate() {
    header= new LinearLayout(context);
    header.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                toggleExpand();
            }
        });
    }
}

Что я хочу сделать, так это добавить некоторый код к уже определенному событию OnClickListener в моей деятельности. Что-то такое:

public class myActivity extends Activity {
private Linearlayout myCustomView;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.rsdetail);
    myCustomView= (MyCustomView) findViewById(R.id.expanded);

    myCustomView.getChildAt(0).setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            if(v instanceof LinearLayout)
            {
                v.performClick();

                Toast.makeText(getActivity(), "ExpandoOnClickListener", 2000).show();
            }
        }
    });
}

person Zakaria    schedule 05.06.2012    source источник
comment
Пожалуйста дайте код.......   -  person Dheeresh Singh    schedule 05.06.2012
comment
Смотрите ответ @waqaslam.   -  person Halil    schedule 28.08.2014


Ответы (4)


Простым решением было бы получить оригинальный OnClickListener, а затем запустить его в новом:

final OnClickListener preDefinedListener = myCustomView.getChildAt(0).getOnClickListner();

myCustomView.getChildAt(0).setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        if(v instanceof LinearLayout)
        {
            preDefinedListener.onClick(v); // calls default (defined by MyCustomView)

            Toast.makeText(getActivity(), "ExpandoOnClickListener", 2000).show();
        }
    }
});

К сожалению, View не имеет getOnClickListner(), но я думаю, вы можете использовать reflection для возьми. Он хранится в поле mOnClickListener (источник) .

Вот как вы можете определить OnClickListener для своего макета:

OnClickListener tmpOnClickListener = null;
try {
    Class<View> cls = (Class<View>) Class.forName("android.view.View");
    Field fld = cls.getDeclaredField("mOnClickListener");
    fld.setAccessible(true); // because it is protected

    tmpOnClickListener = (OnClickListener) fld.get(myCustomView.getChildAt(0));

    fld.setAccessible(false); // restore it's original property
} catch (SecurityException e) {
    e.printStackTrace();
} catch (NoSuchFieldException e) {
    e.printStackTrace();
} catch (IllegalArgumentException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} 

final OnClickListener preDefinedListener = tmpOnClickListener;

if (preDefinedListener != null) {
    myCustomView.getChildAt(0).setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View paramView) {
            preDefinedListener.onClick(paramView);

            Toast.makeText(getActivity(), "ExpandoOnClickListener", Toast.LENGTH_LONG).show();
        }
});

Я действительно не удосужился правильно обработать все исключения, но этого достаточно, чтобы понять идею. Это может выглядеть беспорядочно, но на самом деле это всего лишь 5 строк нового кода для решения вашей проблемы.

person Vladimir    schedule 05.06.2012
comment
Большое спасибо Владимир :) у меня работает отлично. Я просто хочу задать вам вопрос: является ли Reflection экспансивным с точки зрения памяти и/или процессора?? - person Zakaria; 05.06.2012
comment
нет проблем =) ну, получать поля и вызывать методы через рефлексию дороже, чем делать это обычно, но в данном случае разница настолько незначительна, что ни на что не повлияет. Но есть нюанс - так как обращение к полю происходит по имени, то при изменении его имени (чего, скорее всего, не произойдет) этот код перестанет работать - не рухнет, а NoSuchFieldException выкинет, а preDefinedListener в итоге получится null, который оставляет набор слушателей для макета нетронутым. - person Vladimir; 05.06.2012

Вы можете программно вызвать событие клика в представлении, чтобы вызвать его OnClickListener, как показано ниже:

view.performClick();

Теперь, если вы вызовете это на своем втором макете в OnClickListener первого макета, я надеюсь, что это должно сделать волшебство

person waqaslam    schedule 05.06.2012
comment
Когда я делаю это. Это дает мне 06-05 10:29:59.470: E/AndroidRuntime(7518): java.lang.StackOverflowError - person Zakaria; 05.06.2012
comment
можете ли вы опубликовать свое полное исключение logcat - person waqaslam; 05.06.2012
comment
ты звонишь myCustomView.performClick()? Если так, то нужно звонить как myCustomView.getChildAt(0).performClick() - person waqaslam; 05.06.2012
comment
06-05 10:58:10.440: E/AndroidRuntime(8044): НЕИСПРАВНОЕ ИСКЛЮЧЕНИЕ: main 06-05 10:58:10.440: E/AndroidRuntime(8044): java.lang.StackOverflowError 06-05 10:58:10.440: E/AndroidRuntime(8044): в java.util.HashMap.containsKey(HashMap.java:330) 06-05 10:58:10.440: E/AndroidRuntime(8044): в android.provider.Settings$NameValueCache.getString(Settings .java:599) 06-05 10:58:10.440: E/AndroidRuntime(8044): в android.provider.Settings$System.getString(Settings.java:731) 06-05 10:58:10.440: E/AndroidRuntime (8044): в android.provider.Settings$System.getInt(Settings.java:780)06-05 - person Zakaria; 05.06.2012
comment
вот оно: expandCustomer= (ExpandoLayout) viewr.findViewById(R.id.expandedClient); expandCustomer.getChildAt(0).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(v instanceof LinearLayout) {expandCustomer.getChildAt(0).performClick(); Toast.makeText(getActivity(), ExpandoOnClickListener, 2000).show(); } } }); - person Zakaria; 05.06.2012
comment
способ, которым вы звоните, определенно приведет к сбою, потому что он вызывает себя в непрерывном цикле - прямо как черная дыра :). Вместо того, чтобы использовать PerformCLick для getChildAt(0) внутри OnCLickListener, это должен быть второй макет или getChildAt(1) (если есть) - person waqaslam; 05.06.2012

Начиная с SDK 15 вы можете просто вызвать метод:

view.callOnClick()
person Vadim    schedule 02.09.2015

Если вы не можете изменить код CustomView, у вас есть вариант ниже.

  • Расширьте пользовательский вид.
  • Переопределите метод onClick() и привяжите его как прослушиватель к первому макету.
  • Внутри onClick() сначала вызовите super.onClick(), а затем добавьте свои дополнительные функции ниже.

Таким образом, вы сохраняете существующий код и добавляете дополнительные функции.

person Sudhaker    schedule 05.06.2012
comment
событие OnClickListener определяется как анонимный прослушиватель. Как я могу переопределить это?? - person Zakaria; 05.06.2012