Настройка RadioButton.setПроверено несколькими фрагментами
Эй, у меня есть действительно глупая проблема, и я не могу понять, почему она работает не так, как ожидалось.
Поэтому у меня есть макет MultiFragment (у каждого есть несколько разных вопросов), используя viewpager и FragmentStatePagerAdapter.
Когда я открываю экран, на котором размещаются все эти фрагменты, я пытаюсь восстановить предыдущее состояние (отмечая все ответы на вопросы) с помощью сетевого вызова.
Однако кажется, что если мой фрагмент не отображается пользователю, он не может обновить проверенное состояние переключателя/флажков.
![введите описание изображения здесь]()
Кто-нибудь знает, что я могу сделать для достижения желаемого поведения?
Приветствия и благодарности заранее!
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
presenter.restoreAnswersFromPreviousSession(questionId);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisible()) {
if (isVisibleToUser) {
presenter.restoreAnswersFromPreviousSession(questionId);
Log.d("Fit", "My Fragment is visible");
} else {
Log.d("Fit", "My Fragment is not visible");
}
}
}
Здесь я восстанавливаю состояние (вызывается после успешного запроса)
previousReplies = repliesToRestore;
for (QualityReportReply reportReply : repliesToRestore) {
int id = reportReply.id();
switch (id) {
case 201: {
boolean tooThin = (boolean) reportReply.value();
if (tooThin) {
materialTooThinGroupYes.setChecked(true);
} else {
materialTooThinGroupNo.setChecked(true);
}
break;
}
case 202: {
boolean tooThick = (boolean) reportReply.value();
if (tooThick) {
materialTooThickGroupYes.setChecked(true);
} else {
materialTooThickGroupNo.setChecked(true);
}
break;
}
case 203: {
boolean drawingThreads = (boolean) reportReply.value();
if (drawingThreads) {
materialDrawThreadsGroupYes.setChecked(true);
} else {
materialDrawThreadsGroupNo.setChecked(true);
}
break;
}
case 204: {
boolean flyingThreads = (boolean) reportReply.value();
if (flyingThreads) {
materialFlyingThreadsGroupYes.setChecked(true);
} else {
materialFlyingThreadsGroupNo.setChecked(true);
}
break;
}
case 205: {
boolean knots = (boolean) reportReply.value();
if (knots) {
materialKnotsGroupYes.setChecked(true);
} else {
materialKnotsGroupNo.setChecked(true);
}
break;
}
Ответы
Ответ 1
UPDATE
Здесь - рабочий пример, вы можете его увидеть.
Я думаю, что вы восстанавливаете состояние на хосте Activity
. Не делайте этого, делайте это в каждом Fragment
. Скажем, у вас есть два фрагмента, Passform
и Material
, поэтому вместо того, чтобы делать это в Activity
, сделайте вызов внутри фрагмента onCreateView
.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_passform, container, false);
new FetchPassFormState().execute();
return view;
}
Обновить views
внутри onPostExecute
FetchPassFormState
или где угодно, в соответствии с вашими потребностями.
UPDATE
Вы можете сделать одно. Вы должны добавить Fragments
в свой QuestionsViewPagerAdapter
, попробуйте setArguments
вашего Fragments
там. Как,
Fragment fragment = new YourFragment();
Bundle bundle = new Bundle();
bundle.putString("your_key", "Your Data");
...
fragment.setArguments(bundle);
Теперь добавьте этот Fragment
. И внутри Fragment
onCreateView
, получим этот Bundle
.
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_material_questions, container, false);
ButterKnife.bind(this, view);
getFragmentComponent().inject(this);
presenter.setView(this);
Bundle bundle = getArguments();
String youdData = bundle.getString("your_key");
...
}
И затем используйте эти данные для установки представлений.
Ответ 2
Вам нужно использовать setOffscreenPageLimit
viewPager.setOffscreenPageLimit(<total_page_count>);
Как говорится в документах
Задайте количество страниц, которые должны быть сохранены в обе стороны текущей страницы в иерархии представлений в состоянии ожидания. Страницы, выходящие за этот предел, будут воссозданы из адаптера, когда это необходимо.
Ответ 3
Я думаю, что одна из проблем, которые у вас есть, - это сделать восстановление в onCreateView.
Так я обновляю свои фрагменты, вызывая веб-службы, когда фрагмент становится видимым.
callToRestore() запускает вызов async webservice (с использованием иона), после которого я получаю обратный вызов, который заполняет информацию моего фрагмента.
protected boolean restoreOnHold = false;
@Override
public void onResume() {
super.onResume();
if(restoreOnHold ){
restoreOnHold = false;
callToRestore();
}
}
@Override
public void setUserVisibleHint(boolean isVisible){
super.setUserVisibleHint(isVisible);
if (isVisible) {
if(getView() != null){
restoreOnHold = false;
callToRestore();
}else{
restoreOnHold = true;
}
}
}
Ответ 4
Так как ваши фрагменты (которые являются VIEWS!) обрабатывают данные, у вас возникают проблемы с их обновлением в нужное время. Надеюсь, это даст вам хорошее начало для большего количества кодировок:-)
** edit: я хочу указать свой комментарий на putAllAnswersFromPreviousSessions (DataObject mObject)
public class QuestionPagerAdapter extends FragmentStatePagerAdapter {
private List<Fragment> mFragments;
private Bundle mData = null;
public QuestionPagerAdapter(FragmentManager fm) {
super(fm);
// u can add and remove fragments anytime during runtime with this List<Fragments>
mFragments = new ArrayList<>();
}
/**
* Everytime we show a new position, set your Bundle to your Fragments.
* @param position
* @return fragment with data;
*/
@Override
public Fragment getItem(int position) {
// Note, that the method .setArguments(Bundle bundle) should only be called,
// before the Fragments are attached. Therefore, we need to
// @Override getItemPosition(Obj) to ensure, that your Fragments are redrawn, when selected;
mFragments.get(position).setArguments(mData);
return mFragments.get(position);
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
@Override
public int getCount() {
return mFragments.size();
}
/**
* Invoke this Method from your activity, when u get your data from the internet;
* You could register an receiver inside your activity and when u get an result from your AsyncTask,
* u could call
* mQuestionPagerAdapter.putAllAnswersFromPreviousSessions(result);
* mQuestionPagerAdapter.notifyDataSetChanged();
* @param mObject a simple data object.
*/
public void putAllAnswersFromPreviousSessions(DataObject mObject){
Bundle dataObject = new Bundle();
boolean[] mReceivedData = mObject.getAllAnswers();
for (int i = 0; i < mReceivedData.length; i++){
dataObject.putBoolean("answer_"+String.valueOf(i), mReceivedData[i]);
}
this.mData = dataObject;
}
}
Если вы заботитесь о том, чтобы ваши данные загружались перед показом ваших фрагментов, вы не должны вызывать mQuestionPagerAdapter.notifyDataSetChanged(). Этот метод заставляет FragmentStatePagerAdapter перерисовывать ваши фрагменты, и положение экрана будет потеряно (тот же фрагмент будет показан, хотя:-))
Еще один аккуратный трюк, который я узнал, - это получение ваших данных в любое время из вашего FragmentStatePagerAdapter:
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewPager = (ViewPager) getActivity().findViewById(R.id.view_pager);
mStatePagerAdapter = (FragmentStatePagerAdapter) mViewPager.getAdapter();
}
При использовании обоих подходов вы должны иметь возможность хранить свои данные за пределами своих представлений (ваши фрагменты) и получать/устанавливать их, когда вам нужно.
public class DataObject {
private boolean[] mAnswersFromPreviousSessions;
/**
* This data-object is a suggestion.
* Since u need to decide between three states (true / false / empty),
* u should work with enums e.g.
* @param answers your data..
*/
public DataObject(boolean[] answers) {
super();
this.mAnswersFromPreviousSessions = answers;
}
/** get all answers, or **/
public boolean[] getAllAnswers() {
return mAnswersFromPreviousSessions;
}
/** just ask for a specific one **/
public boolean getAnswerFor(int questionPosition){
return mAnswersFromPreviousSessions[questionPosition];
}
}
Gruß
** следующее редактирование: я не знаю, почему я получаю мгновенно вниз, но мой подход соответствует его требованиям. Он получает свои данные в setArguments(), даже до создания Фрагмента. Поэтому он может даже применить его в OnCreateView(). Прокомментируйте @downvote.
Ответ 5
Попробуйте сохранить ответы в одном классе модели после сетевого вызова, пока вы размещаете основной фрагмент, а затем используйте этот класс модели в каждом фрагменте для заполнения деталей.
Ответ 6
Прежде всего, когда вы создаете действие, выполните следующие действия.
1. Сетевой вызов для всех данных.
2. При успешном результате разделите данные для разных фрагментов. В вашем случае 4 фрагмента, я думаю.
3. настроить просмотр пейджера и добавить фрагменты.
4. Отправляйте соответствующие данные фрагментам с помощью фрагмента .setArguments(bundle) и получайте данные с помощью getArguments.
5. Задайте результат для вашего переключателя/флажков на onCreateView вашего фрагмента.
Действие onCreate:
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// load the layout
setContentView(R.layout.activity_layout);
// Your network call
presenter.restoreAnswersFromPreviousSession(questionId);
}
@Override
public void onSuccess(Object result){
// setup view your view pager and fragments here.
// add restored replies to fragments.
Fragment fragment = new FragmentOne();
Bundle bundle = new Bundle();
bundle.putExtra("restored_replies", replies);
fragment.setArguments(bundle);
}
И получите восстановленные ответы на onCreateView для каждого фрагмента. Установите флажки/радиокнопки в соответствии с полученными данными.
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_questions, container, false);
...
Bundle bundle = getArguments();
Object data = bundle.getSerializableExtra("restored_replies");
// Serialize object before passing as an argument.
// set you check boxes and radio buttons according to the data received.
...
return view;
}
Сводка: Сделать сетевой вызов по активности и отправить результат на соответствующие фрагменты, чтобы показать.
Ответ 7
Я с комментарием Киранкумара Зинзувадия
У меня была точно такая же проблема с RadioButton во ViewPager. Причина в том, что setChecked() не работает, когда он невидим.
Поэтому я вызвал setChecked() для ViewPager.OnPageChangeListener.onPageSelected()
Jav
@Override
public void onPageSelected(int position){
if(position == 2){
materialTooThinGroupYes.setChecked(true);
}
}
Котлин>
override fun onPageSelected(position: Int) {
if(position == 2){
materialTooThinGroupYes.isChecked = true;
}
}
Ответ 8
У меня такая же проблема с моим приложением Quiz. И я обнаружил одно: система Android не обновляет пользовательский интерфейс при вызове функции radioButton.setChecked(true);
. Но это внутренне отражается в коде (когда вы его проверяете - проверено или нет).
Чтобы устранить эту проблему, вы должны использовать следующий вызов функции виджета RadioButton.
radioButton.setSelectedState(true); //This makes the ui selected. use false to make the ui not selected.
Здесь, как и для вашего кода, вы должны изменить свою функцию на
previousReplies = repliesToRestore;
for (QualityReportReply reportReply : repliesToRestore) {
int id = reportReply.id();
switch (id) {
case 201: {
boolean tooThin = (boolean) reportReply.value();
if (tooThin) {
materialTooThinGroupYes.setChecked(true); //Keep it if you want to get the values from the code
materialTooThinGroupYes.setSelectedState(true); //To Change the UI
} else {
materialTooThinGroupNo.setChecked(true);
materialTooThinGroupNo.setSelectedState(true); //To Change the UI
}
break;
}
case 202: {
boolean tooThick = (boolean) reportReply.value();
if (tooThick) {
materialTooThickGroupYes.setChecked(true);
materialTooThickGroupYes.setSelectedState(true); //To Change the UI
} else {
materialTooThickGroupNo.setChecked(true);
materialTooThickGroupNo.setSelectedState(true); //To Change the UI
}
break;
}
case 203: {
boolean drawingThreads = (boolean) reportReply.value();
if (drawingThreads) {
materialDrawThreadsGroupYes.setChecked(true);
materialDrawThreadsGroupYes.setSelectedState(true); //To Change the UI
} else {
materialDrawThreadsGroupNo.setChecked(true);
materialDrawThreadsGroupNo.setSelectedState(true); //To Change the UI
}
break;
}
case 204: {
boolean flyingThreads = (boolean) reportReply.value();
if (flyingThreads) {
materialFlyingThreadsGroupYes.setChecked(true);
materialFlyingThreadsGroupYes.setSelectedState(true); //To Change the UI
} else {
materialFlyingThreadsGroupNo.setChecked(true);
materialFlyingThreadsGroupNo.setSelectedState(true); //To Change the UI
}
break;
}
case 205: {
boolean knots = (boolean) reportReply.value();
if (knots) {
materialKnotsGroupYes.setChecked(true);
materialKnotsGroupYes.setSelectedState(true); //To Change the UI
} else {
materialKnotsGroupNo.setChecked(true);
materialKnotsGroupNO.setSelectedState(true); //To Change the UI
}
break;
}