How to handle button clicks using the XML onClick within Fragments

前端 未结 18 1545
旧时难觅i
旧时难觅i 2020-11-22 01:41

Pre-Honeycomb (Android 3), each Activity was registered to handle button clicks via the onClick tag in a Layout\'s XML:

android:onClick=\"m         


        
18条回答
  •  故里飘歌
    2020-11-22 02:13

    ButterKnife is probably the best solution for the clutter problem. It uses annotation processors to generate the so called "old method" boilerplate code.

    But the onClick method can still be used, with a custom inflator.

    How to use

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup cnt, Bundle state) {
        inflater = FragmentInflatorFactory.inflatorFor(inflater, this);
        return inflater.inflate(R.layout.fragment_main, cnt, false);
    }
    

    Implementation

    public class FragmentInflatorFactory implements LayoutInflater.Factory {
    
        private static final int[] sWantedAttrs = { android.R.attr.onClick };
    
        private static final Method sOnCreateViewMethod;
        static {
            // We could duplicate its functionallity.. or just ignore its a protected method.
            try {
                Method method = LayoutInflater.class.getDeclaredMethod(
                        "onCreateView", String.class, AttributeSet.class);
                method.setAccessible(true);
                sOnCreateViewMethod = method;
            } catch (NoSuchMethodException e) {
                // Public API: Should not happen.
                throw new RuntimeException(e);
            }
        }
    
        private final LayoutInflater mInflator;
        private final Object mFragment;
    
        public FragmentInflatorFactory(LayoutInflater delegate, Object fragment) {
            if (delegate == null || fragment == null) {
                throw new NullPointerException();
            }
            mInflator = delegate;
            mFragment = fragment;
        }
    
        public static LayoutInflater inflatorFor(LayoutInflater original, Object fragment) {
            LayoutInflater inflator = original.cloneInContext(original.getContext());
            FragmentInflatorFactory factory = new FragmentInflatorFactory(inflator, fragment);
            inflator.setFactory(factory);
            return inflator;
        }
    
        @Override
        public View onCreateView(String name, Context context, AttributeSet attrs) {
            if ("fragment".equals(name)) {
                // Let the Activity ("private factory") handle it
                return null;
            }
    
            View view = null;
    
            if (name.indexOf('.') == -1) {
                try {
                    view = (View) sOnCreateViewMethod.invoke(mInflator, name, attrs);
                } catch (IllegalAccessException e) {
                    throw new AssertionError(e);
                } catch (InvocationTargetException e) {
                    if (e.getCause() instanceof ClassNotFoundException) {
                        return null;
                    }
                    throw new RuntimeException(e);
                }
            } else {
                try {
                    view = mInflator.createView(name, null, attrs);
                } catch (ClassNotFoundException e) {
                    return null;
                }
            }
    
            TypedArray a = context.obtainStyledAttributes(attrs, sWantedAttrs);
            String methodName = a.getString(0);
            a.recycle();
    
            if (methodName != null) {
                view.setOnClickListener(new FragmentClickListener(mFragment, methodName));
            }
            return view;
        }
    
        private static class FragmentClickListener implements OnClickListener {
    
            private final Object mFragment;
            private final String mMethodName;
            private Method mMethod;
    
            public FragmentClickListener(Object fragment, String methodName) {
                mFragment = fragment;
                mMethodName = methodName;
            }
    
            @Override
            public void onClick(View v) {
                if (mMethod == null) {
                    Class clazz = mFragment.getClass();
                    try {
                        mMethod = clazz.getMethod(mMethodName, View.class);
                    } catch (NoSuchMethodException e) {
                        throw new IllegalStateException(
                                "Cannot find public method " + mMethodName + "(View) on "
                                        + clazz + " for onClick");
                    }
                }
    
                try {
                    mMethod.invoke(mFragment, v);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    throw new AssertionError(e);
                }
            }
        }
    }
    

提交回复
热议问题