Как использовать Butterknife для получения ссылки на фрагмент xml

Можно ли получить 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>

Это невозможно, потому что


person Jim Clermonts    schedule 17.08.2017    source источник


Ответы (2)


Fragment не является View, @BindView Butterknife используется для привязки представления, а не фрагмента.

Вы можете использовать FragmentManager.findFragmentById(int id); для получения фрагмента, но если вы проверите реализацию, вы увидите, что FragmentManager не выглядит в иерархии View

Если вы хотите сделать это с помощью ButterKnife

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, добавив эту функциональность, или вы можете просто создать оболочку вокруг него и найти свою собственную аннотацию. Например, вы можете создать пользовательскую аннотацию

создайте оболочку с методом bind(Activity), вызовите метод ButterKnife.bind(Activity), а затем найдите свою собственную аннотацию и установите ее в поле экземпляра

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

В вашей деятельности вы могли бы сделать это

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);
    }
}
person lelloman    schedule 25.08.2017
comment
НЕИСПРАВНОЕ ИСКЛЮЧЕНИЕ: основной процесс: com.tamtam.myapplication, PID: 29138 java.lang.RuntimeException: невозможно запустить активность ComponentInfo{com.tamtam.myapplication/com.tamtam.myapplication.MainActivity}: java.lang.NullPointerException: попытка вызвать метод интерфейса 'void 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: попытка вызвать метод интерфейса «void 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) - person Jim Clermonts; 31.08.2017

Вот вам пример.

MainActivity.java

activity_main.xml

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

Фрагмент1.java

<?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>

FragmentCallBack.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() {

    }
}

Таким образом, вы можете вызвать метод интерфейса в своем фрагменте из своей деятельности.

public interface FragmentCallBack {
    public void test();
}

Спасибо @lelloman, это то, что я искал. Butterknife должен иметь эту функцию по умолчанию :).

person AndiM    schedule 25.08.2017