FragmentManager нужен дополнительный requestLayout
Я построил представление, которое содержит EditText
как кнопка отправки, поэтому она не фокусируется в сенсорном режиме, текст центрируется и имеет цвет фона. Затем я создал фрагмент с этой кнопкой.
Когда этот фрагмент является первым, который будет помещен в контейнер для рисования, текст центрируется в редактируемом тексте, как и ожидалось. Однако, если использовать событие щелчка, чтобы заменить этот фрагмент другим фрагментом, который содержит собственную кнопку отправки, текст не центрируется, не говоря уже о выравнивании по левому или правому краю. Это совсем не выровнено.
После положить 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 ответ
Я пришел к решению, которое не требует вызова дополнительных requestLayout
Я считаю, что это правильный способ решить эту проблему.
updateMinHeight
а также super.onMeasure
в неправильном порядке в onMeasure
метод. Правильный путь:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
updateMinHeight();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Для меня этот порядок выше имеет смысл, так как updateMinHeight
действительно может изменить измеренный размер виджета. Хотя этот ответ показывает, как решить, я не могу понять, почему проблема возникает и как это повлияет только на положение текста в очень специфических случаях, таких как первый отображаемый фрагмент.