FragmentManager需要额外的requestLayout

时间:2017-08-01 18:55:46

标签: android android-layout android-fragments fragmentmanager

我构建了一个包含EditText作为提交按钮的视图,因此它在触摸模式下无法聚焦,文本居中并且具有背景颜色。然后,我用这个按钮创建了一个片段。

当此片段是要放置在要绘制的容器中的第一个片段时,文本将按预期在编辑文本中居中。但是,如果使用click事件将此片段替换为包含其自己的提交按钮的另一个片段,则文本不会居中,更不用说左对齐或右对齐。它根本没有对齐。

Hander之后添加5ms添加延迟以请求布局,我创建了要放置在片段中的视图,文本正确居中。我意识到由于某种原因,片段事务需要额外调用requestLayout

我隔离了导致问题的代码。通过这样做,我意识到问题与我在TestTextField.java中处理字体的方式有某种关系。我贴了右边的声音。

可能导致什么?为什么?如果我的代码有问题,为什么它适用于我放在屏幕上的第一个片段,但它不适用于其他片段?

TestActivity.java

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import java.lang.reflect.Method;

/**
 * Created by eduardoj on 2017-07-19.
 */

public class TestActivity extends Activity
        implements TestFragmentA.Listener, TestFragmentB.Listener {

    private FrameLayout fragmentContainer;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ViewGroup.LayoutParams matchParent = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        );

        fragmentContainer = new FrameLayout(this);
        fragmentContainer.setId(View.generateViewId());
        fragmentContainer.setLayoutParams(matchParent);
        fragmentContainer.setFocusableInTouchMode(true);
        setContentView(fragmentContainer);

        FragmentManager manager = getFragmentManager();
        FragmentTransaction transaction = manager.beginTransaction();
        transaction.add(fragmentContainer.getId(), TestFragmentA.newInstance());
        transaction.commit();
    }

    @Override
    public void onASubmitButtonClick() {
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        transaction.add(fragmentContainer.getId(), TestFragmentB.newInstance());
        transaction.commit();
    }

    @Override
    public void onBSubmitButtonClick() {

    }
}

TestFragmentA.java

import android.app.Fragment;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

/**
 * Created by eduardoj on 2017-07-19.
 */

public class TestFragmentA extends Fragment {

    public interface Listener {
        void onASubmitButtonClick();
    }

    private Listener listener;

    public static TestFragmentA newInstance() {
        Bundle args = new Bundle();
        TestFragmentA fragment = new TestFragmentA();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        /*
         * This makes sure that the container context has implemented the
         * callback interface. If not, it throws an exception.
         */
        try {
            listener = (Listener) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString()
                    + " must implement TestFragmentA.Listener");
        }
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater,
                             @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {

        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        );

        FrameLayout layout = new FrameLayout(getActivity());
        layout.setId(View.generateViewId());
        layout.setLayoutParams(params);
        layout.setBackgroundColor(Color.CYAN);

        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                600,
                FrameLayout.LayoutParams.WRAP_CONTENT,
                Gravity.CENTER
        );

        TestTextField view = new TestTextField(getActivity());
        view.setId(View.generateViewId());
        view.setLayoutParams(lp);
        view.setText("Submit A");
        view.setBackgroundColor(Color.MAGENTA);
        view.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
        view.setEnableTextEditing(false);
        view.addListener(new TestTextField.Listener() {
            @Override
            protected void onClick(TestTextField textField, String text) {
                super.onClick(textField, text);
                listener.onASubmitButtonClick();
            }
        });

        layout.addView(view);

        return layout;
    }
}

TestFragmentB.java

import android.app.Fragment;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

/**
 * Created by eduardoj on 2017-07-19.
 */

public class TestFragmentB extends Fragment {

    public interface Listener {
        void onBSubmitButtonClick();
    }

    private Listener listener;

    public static TestFragmentB newInstance() {
        Bundle args = new Bundle();
        TestFragmentB fragment = new TestFragmentB();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        /*
         * This makes sure that the container context has implemented the
         * callback interface. If not, it throws an exception.
         */
        try {
            listener = (Listener) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString()
                    + " must implement TestFragmentA.Listener");
        }
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater,
                             @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {

        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        );

        FrameLayout layout = new FrameLayout(getActivity());
        layout.setId(View.generateViewId());
        layout.setLayoutParams(params);
        layout.setBackgroundColor(Color.LTGRAY);

        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                600,
                FrameLayout.LayoutParams.WRAP_CONTENT,
                Gravity.CENTER
        );

        TestTextField view = new TestTextField(getActivity());
        view.setId(View.generateViewId());
        view.setLayoutParams(lp);
        view.setText("Submit B");
        view.setBackgroundColor(Color.MAGENTA);
        view.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
        view.setEnableTextEditing(false);
        view.addListener(new TestTextField.Listener() {
            @Override
            protected void onClick(TestTextField textField, String text) {
                super.onClick(textField, text);
                listener.onBSubmitButtonClick();
            }
        });

        layout.addView(view);

        return layout;
    }
}

TestTextField.java

import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Handler;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.List;

/*
 * Created by eduardoj on 2017-07-13.
 */
public class TestTextField extends LinearLayout {

    public static class Listener {
        protected void onClick(TestTextField textField, String text) {}
    }

    private int BACKGROUND_COLOR = Color.MAGENTA;
    private String HINT = "Enter text here";
    private boolean isToUpdateMinHeight;

    private EditText editText;
    private Typeface textTypeface;
    private Typeface hintTypeface;

    private List<Listener> listeners;

    public TestTextField(Context context) {
        super(context);
        listeners = new ArrayList<>();

        /* Initializing text field */
        initView();
        bindListeners();
    }

    public void addListener(Listener listener) {
        if (listener != null) {
            listeners.add(listener);
        }
    }

    public void setEnableTextEditing(boolean enable) {
        editText.setFocusableInTouchMode(enable);
    }

    public void setHintTypeface(Typeface typeface) {
        hintTypeface = typeface;
        isToUpdateMinHeight = true;
        updateTypeface();
    }

    public void setText(CharSequence text) {
        editText.setText(text);
        updateTypeface();
    }

    @Override
    public void setTextAlignment(int textAlignment) {
        editText.setTextAlignment(textAlignment);
    }

    public void setTypeface(Typeface typeface) {
        isToUpdateMinHeight = true;
        textTypeface = typeface;
        updateTypeface();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        updateMinHeight();
    }

    private void initView() {
        setOrientation(HORIZONTAL);

        LayoutParams params = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
        editText = new EditText(getContext());
        editText.setId(generateViewId());
        editText.setLayoutParams(params);
        editText.setHint(HINT);
        editText.setBackgroundColor(Color.TRANSPARENT); // Removes underline
        addView(editText);
        updateTypeface();

        setBackgroundColor(BACKGROUND_COLOR);
        // Custom Typefaces: you have to set the custom typeset to reproduce the problem.
//        setTypeface(G.Font.getVegurRegular(getContext()));
//        setHintTypeface(G.Font.getVegurLight(getContext()));


        /* This is the current work around for this Issue */
        (new Handler()).postDelayed(new Runnable() {
            @Override
            public void run() {
                requestLayout();
            }
        }, 15);
    }

    private void bindListeners() {
        final TestTextField textFieldInstance = this;

        editText.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                for (Listener listener : listeners) {
                    listener.onClick(
                            textFieldInstance,
                            editText.getText().toString()
                    );
                }
            }
        });
    }

    /**
     * Updates the minimum height to the size of the considering the biggest
     * typeface.
     */
    private void updateMinHeight() {
        if (!isToUpdateMinHeight) {
            return;
        }

        Typeface original = editText.getTypeface();

        editText.setTypeface(textTypeface);
        editText.measure(0, 0);
        int textHeight = editText.getMeasuredHeight();

        editText.setTypeface(hintTypeface);
        editText.measure(0, 0);
        int hintHeight = editText.getMeasuredHeight();

        int minHeight = textHeight > hintHeight ? textHeight : hintHeight;

        editText.setMinimumHeight(minHeight);
        editText.setTypeface(original);
        isToUpdateMinHeight = false;
    }

    private void updateTypeface() {
        boolean hasText = editText.length() > 0;
        Typeface typeface = hasText ? textTypeface : hintTypeface;
        editText.setTypeface(typeface);
    }
}

1 个答案:

答案 0 :(得分:0)

我找到了一个不需要额外调用requestLayout的解决方案,我相信这是解决这个问题的正确方法。

{p> updateMinHeightsuper.onMeasureonMeasure方法中的顺序错误。正确的方法是:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    updateMinHeight();
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

对我来说,上面的这个顺序是有意义的,因为updateMinHeight实际上可以改变小部件的测量大小。虽然这个答案显示了如何解决,但我无法理解问题发生的原因以及在特定情况下如何影响文本位置,例如显示的第一个片段。