【问题标题】:How to use Butterknife to get a reference to the fragment xml如何使用 Butterknife 获取对片段 xml 的引用
【发布时间】:2018-01-25 21:53:53
【问题描述】:

是否可以通过接口获取 SomeFragment?我不想使用 FragmentManager,因为在我的原始代码中 MainActivity 是一个片段。

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.some_container)
    FragmentCallback fragment;

    public interface FragmentCallback {
        void test();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fragment.test();
    }
}

public class SomeFragment extends Fragment implements FragmentCallback {

    public SomeFragment() {
    }

    @Nullable
    @Override
    public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle
            savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_some, container, false);
        ButterKnife.bind(this, view);
        return view;
    }

    @Override
    public void test() {
        Log.d("" , "it works");
    }
}

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <fragment
            android:id="@+id/some_container"
            android:name="com.tamtam.myapplication.SomeFragment"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
</LinearLayout>

致命异常:主要 进程:com.tamtam.myapplication,PID:29138 java.lang.RuntimeException:无法启动活动 组件信息{com.tamtam.myapplication/com.tamtam.myapplication.MainActivity}: java.lang.NullPointerException:尝试调用接口方法 '无效 com.tamtam.myapplication.MainActivity$FragmentCallback.test()' 在空对象引用上 在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665) 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) 在 android.app.ActivityThread.-wrap12(ActivityThread.java) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) 在 android.os.Handler.dispatchMessage(Handler.java:102) 在 android.os.Looper.loop(Looper.java:154) 在 android.app.ActivityThread.main(ActivityThread.java:6119) 在 java.lang.reflect.Method.invoke(本机方法) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 引起:java.lang.NullPointerException:尝试调用接口 方法'无效 com.tamtam.myapplication.MainActivity$FragmentCallback.test()' 空对象引用 在 com.tamtam.myapplication.MainActivity.onCreate(MainActivity.java:21) 在 android.app.Activity.performCreate(Activity.java:6679) 在 android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) 在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618) 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) 在 android.app.ActivityThread.-wrap12(ActivityThread.java) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) 在 android.os.Handler.dispatchMessage(Handler.java:102) 在 android.os.Looper.loop(Looper.java:154) 在 android.app.ActivityThread.main(ActivityThread.java:6119)

【问题讨论】:

    标签: android android-fragments butterknife


    【解决方案1】:

    这是不可能的,因为

    Fragment 不是View,Butterknife 的@BindView 用于绑定 View,而不是 Fragment。

    您可以使用FragmentManager.findFragmentById(int id); 获取片段,但如果您检查实现,您会发现FragmentManager 不在View 层次结构中查找

    public Fragment findFragmentById(int id) {
        if (mAdded != null) {
            // First look through added fragments.
            for (int i=mAdded.size()-1; i>=0; i--) {
                Fragment f = mAdded.get(i);
                if (f != null && f.mFragmentId == id) {
                    return f;
                }
            }
        }
        if (mActive != null) {
            // Now for any known fragment.
            for (int i=mActive.size()-1; i>=0; i--) {
                Fragment f = mActive.get(i);
                if (f != null && f.mFragmentId == id) {
                    return f;
                }
            }
        }
        return null;
    }
    

    如果你想用 ButterKnife 做到这一点

    您可以通过添加此功能来扩展 ButterKnife,或者您可以围绕它创建一个包装器并查找您自己的注释。例如,您可以创建自定义注释

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface BindFrament {
        @IdRes int value();
    }
    

    使用bind(Activity) 方法创建一个包装器,调用ButterKnife.bind(Activity) 方法然后查找您自己的注解并将其设置为实例字段

    public class ButterKnifeWrapper {
    
        public static void bind(Activity activity){
            ButterKnife.bind(activity);
    
            Class clazz = activity.getClass();
            for(Field field : clazz.getDeclaredFields()){
                if(field.isAnnotationPresent(BindFrament.class)){
                    Class fieldType = field.getType();
                    if(Fragment.class.isAssignableFrom(fieldType)){
                        int fragmentId = field.getAnnotation(BindFrament.class).value();
                        Fragment fragment = activity.getFragmentManager().findFragmentById(fragmentId);
                        try {
                            field.set(activity, fragment);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    

    在你的活动中,你可以这样做

    public class MyActivity extends AppCompatActivity {
    
        @BindFrament(R.id.my_fragment)
        MyFragment myFragment;
    
        @BindView(R.id.my_view)
        View myView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.my_activity);
            ButterKnifeWrapper.bind(this);
        }
    }
    

    【讨论】:

    • thnx @lelloman 这就是我想要的。 Butterknife 默认应该有这个功能:)。
    【解决方案2】:

    尝试添加片段运行时。

    这里有一个例子。

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
        private Fragment1 fragment1;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (savedInstanceState == null) {
                fragment1 = Fragment1.newInstance();
            }
            displayFragmentHome();
            // Now you can call your fragment's method
            fragment1.test();
        }
    
        private void displayFragmentHome() {
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            if (fragment1.isAdded()) { // if the fragment is already in container
                ft.show(fragment1);
            } else { // fragment needs to be added to frame container
                ft.add(R.id.frame, fragment1, "HOME");
            }
            ft.commit();
        }
    }
    

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <FrameLayout
            android:layout_width="match_parent"
            android:id="@+id/frame"
            android:layout_height="match_parent"></FrameLayout>
    </RelativeLayout>
    

    Fragment1.java

    public class Fragment1 extends Fragment implements FragmentCallBack {
    
        private View mView;
    
        public static Fragment1 newInstance() {
            Fragment1 homeFragment = new Fragment1();
            return homeFragment;
        }
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            mView = inflater.inflate(R.layout.fragment_1, container, false);
            TextView mText = (TextView) mView.findViewById(R.id.text);
            mText.setText("Welcome..");
            return mView;
        }
    
        @Override
        public void test() {
    
        }
    }
    

    FragmentCallBack.java你的界面

    public interface FragmentCallBack {
        public void test();
    }
    

    这样你就可以从你的活动中调用你的片段中的接口方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多