Zapamiętanie wyboru odpowiedzi w adapterze

0

Cześć ;-)
Dawno nie było mnie tutaj na forum, ale dzisiaj postanowiłem poradzić się na forum, bo nie mam pojęcia nawet pod jakim hasłem szukać odpowiedzi na moje pytanie. Byćmoże ktoś już kiedyś miał podobny problem.
Tworzę program, w którym będzie wykorzystywany formularz. Utworzyłem narzędzie do tego potrzebne, ale problem jest następujący. Adapter w ListView tworzy element od nowa gdy tylko zniknie z ekranu i pojawi się ponownie. Co oznacza, że gdy zadam pytanie "Jakiej jesteś płci?" i będą odpowiedzi "Kobieta/Mężczyzna" jako RadioButtony po zaznaczeniu i zjechaniu do następnego pytania odpowiedź z poprzedniego pytania już znikła.

Myślałem, żeby utworzyć jakiegoś Bundle'a który trzymałby odpowiedzi i podczas tworzenia elementu w adapterze przywracałby poprzedni stan.

Miał ktoś podobny problem albo ma ktoś jakiś lepszy pomysł?

Tutaj zapodaję kod mojego adaptera:

import java.util.ArrayList;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;

import com.formcreator.model.database.Answer;
import com.formcreator.model.database.Question;

public class Questions extends BaseAdapter {

	private Context				context;
	private ArrayList<Question>	data;
	private Question 			currentQuestion;
	
	public Questions( Context context, ArrayList<Question> data ) {
		this.context 	= context;
		this.data		= data;
	}
	
	@Override
	public int getCount() {
		return data.size();
	}

	@Override
	public Object getItem(int idx) {
		return data.get( idx );
	}

	@Override
	public long getItemId(int idx) {
		return data.get( idx ).id;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		currentQuestion = this.data.get( position );
		
		LinearLayout layout = newLinearLayout();
		
		TextView question = newTextView();
		question.setText( currentQuestion.text );
		layout.addView( question );
		
		LinearLayout answers = newLinearLayout();
		putAnswersToQuestion( answers, currentQuestion );
		layout.addView( answers );
		
		if( currentQuestion.isOther == 1 ) {
			EditText other = newOtherAnswer();
			layout.addView( other );
		}
		
		return layout;
	}
	
	
	private TextView newTextView( ) {
		return new TextView(context);
	}
	
	
	private LinearLayout newLinearLayout( ) {
		LinearLayout result = new LinearLayout(context);
		result.setOrientation( LinearLayout.VERTICAL );
		
		return result;
	}
		
	
	private void putAnswersToQuestion( LinearLayout answers, Question currentQuestion ) {
		if( currentQuestion.type == QuestionType.SINGLE_ANSWER ) {
			putRadioAnswersTo( answers );
		} else if( currentQuestion.type == QuestionType.MULTIPLE_ANSWERS ) {
			putCheckBoxAnswersTo( answers );
		} else if( currentQuestion.type == QuestionType.OPEN_ASWER ) {
			EditText answer = new EditText( this.context );
			answers.addView( answer );
		}
	}
	
	
	private void putRadioAnswersTo( LinearLayout answers ) {
		RadioGroup group = new RadioGroup( this.context );
		for( Answer answer : currentQuestion.answers ) {
			RadioButton button = new RadioButton( this.context );
			button.setText( answer.text );
			group.addView( button );
		}
		answers.addView( group );
	}

	
	private void putCheckBoxAnswersTo( LinearLayout answers ) {
		for( Answer answer : currentQuestion.answers ) {
			CheckBox button = new CheckBox( this.context );
			button.setText( answer.text );
			answers.addView( button );
		}
	}
	
	
	private EditText newOtherAnswer( ) {
		EditText result = new EditText( this.context );
		result.setHint( "Inne:" );
		
		return result;
	}
}
0

Po pierwsze stosuj layouty w plikach xml (tworzenie "programistycznie" widoków to bardzo brzydka praktyka w androidzie, poza kilkoma wyjątkowymi sytuacjami). Po drugie modyfikacje radio buttonów musisz przeprowadzić w metodzie getView (nic takiego tam nie widzę).

Proste rozwiązanie:

public abstract class SimpleAdapter<T> extends BaseAdapter {
    
    private List<T> items = new ArrayList<>();

    protected LayoutInflater inflater;

    public List<T> getItems() {
        return items;
    }

    public SimpleAdapter(Context ctx, List<T> items) {
        this.inflater = LayoutInflater.from(ctx);
        this.items = items;
    }

    @Override
    public T getItem(int position) {
        return items.get(position);
    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public long getItemId(int position) {
        return position;
    }
}

class Question {
    boolean czyRadioButton1Zaznaczony = false;
    boolean czyRadioButton2Zaznaczony = false;
    
}

class TwojAdapter extends SimpleAdapter<Question> {

    CompoundButton.OnCheckedChangeListener checkedChangeListener = new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            
            Question q =  buttonView.getTag();
            
            switch (buttonView.getId()) {
                case R.id.radioButton1:
                    q.czyRadioButton1Zaznaczony = isChecked;
                    break;

                case R.id.radioButton2:
                    q.czyRadioButton2Zaznaczony = isChecked;
                    break;
            }
            notifyDataSetChanged();
        }
    };
    
    public TwojAdapter(Context ctx, List<Question> items) {
        super(ctx, items);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        
        Question q = getItem(position1);
        
        convertView = inflater.inflate(R.layout.twoj_plik_layout,null);
        
        RadioButton radioButton1 = convertView.findViewById(R.id.radioButton1);
        RadioButton radioButton2 = convertView.findViewById(R.id.radioButton2);

        radioButton1.setTag(q);
        radioButton2.setTag(q);
        
        radioButton1.setOnCheckedChangeListener(checkedChangeListener);
        radioButton2.setOnCheckedChangeListener(checkedChangeListener);
        
        radioButton1.setChecked(q.czyRadioButton1Zaznaczony);
        radioButton2.setChecked(q.czyRadioButton2Zaznaczony);
        
        
        return convertView;
    }
}
0

Nic z tego, w XMLu mógłbym to zrobić gdybym na sztywno określił ile chcę mieć odpowiedzi do danego pytania. Jeśli jednak utworzę pytanie które ma 20 odpowiedzi a drugie które ma 3, to musiałbym stworzyć dwa oddzielne layouty. Myślę, że to jest ten przypadek o którym mówiłeś wyjątkowa sytuacja.

Co do rozwiązania, to też jest do sztywnego przypadku kiedy pytanie ma dwie odpowiedzi. To się nie sprawdzi.

Czas mi ucieka więc muszę utworzyć jakiegoś Bundle'a który będzie przechowywał informacje na temat odpowiedzi.

0

Żadnego bundla, to nie ma racji bytu tutaj. Przypadek specyficzny, tutaj się muszę zgodzić. Zamiast dwóch wartości boolean możesz dać listę albo jeszcze lepiej mapę gdzie kluczem będzie id widoku, a wartością boolean.

0

Wymyśliłem to jeszcze inaczej. Stworzę listę List<LinearLayout> layout będzie tworzony w momencie wczytywania danych z bazy, więc każde pytanie będzie posiadało odpowiednio tą samą pozycję w obu listach. Natomiast adapter będzie tylko podstawiał widok z listy, a nie tworzył nowy. To już powinno zapamiętać pozycje odpowiedzi ;-)

0

Możliwe, że to zadziała, ale jest też możliwość, że dostaniesz out of memory exception (spowodowane trzymaniem referencji do widoków, które powinny zostać zniszczone przez gc).

0

Raczej to się nie stanie. Referecje nie będą do niczego przypisane po usunięciu adaptera. Dlatego, chcę utworzyć listę LinearLayoutów i tworzyć wygląd dynamicznie, żeby referencje zostały nullowe po wszystkim. To co mówisz, najprawdopodobniej mogłoby się wydarzyć gdybym powiązał moje widoki z XMLami.

0

Referecje nie będą do niczego przypisane po usunięciu adaptera.

  • nie to mam na myśli. Mam na myśli błąd podczas pracy Twojego adaptera.
0

Błąd przy adapterze jest inny. Wyświetla on jeden widok po kilka razy. Teraz zrobiłem to nawet z XMLem, gdzie tylko w odpowiedzi są pakowane dynamicznie. Log natomiast pokazuje całkiem dziwną mieszaninę pozycji wyświetlanych.

package com.formcreator.controller.adapter;

import java.util.ArrayList;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;

import com.formcreator.R;
import com.formcreator.model.database.Answer;
import com.formcreator.model.database.Question;

public class QuestionAdapter extends BaseAdapter {
	
	private static final String TAG = "QuestionAdapter";
	
	private Context					context;
	private ArrayList<Question>		questions;
	
	private TextView 		question;
	private EditText 		openAnswer;
	private LinearLayout	answers;
	
	
	public QuestionAdapter( Context context, ArrayList<Question> questions ) {
		this.context 	= context;
		this.questions	= questions;
		
		for( Question question : questions )
			Log.d( TAG, question.id + ": " + question.text );
	}

	@Override
	public int getCount() {
		return questions.size();
	}

	@Override
	public Object getItem(int idx) {
		return questions.get( idx );
	}

	@Override
	public long getItemId(int idx) {
		return questions.get( idx ).id;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		Log.e( TAG, "POSITION: " + position );
		if( convertView == null ) {
			convertView = inflate( parent );
			initializeViews( convertView );
			
			question.setText( questions.get( position ).text );
			
			if( questions.get( position ).isOther == 1 )
				setOtherOption( openAnswer );
			else
				unsetOtherOption( openAnswer );

			answers.addView( putAnswersToQuestion( questions.get( position ) ) );
		}
		
		return convertView;
	}

	
	private View inflate( ViewGroup parent ) {
		LayoutInflater inflater = (LayoutInflater)context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
		return inflater.inflate( R.layout.list_item_question, parent, false );
	}
	
	
	private void initializeViews( View convertView ) {
		question	= (TextView)convertView.findViewById( R.id.listItemQuestion_question );
		openAnswer	= (EditText)convertView.findViewById( R.id.listItemQuestion_openAnswer );
		answers		= (LinearLayout)convertView.findViewById( R.id.listItemQuestion_answers );
	}
	
	
	private LinearLayout putAnswersToQuestion( Question question ) {
		if( question.type == QuestionType.SINGLE_ANSWER ) {
			return returnRadioAnswersFrom( question );
		} else if( question.type == QuestionType.MULTIPLE_ANSWERS ) {
			return returnCheckBoxAnswersFrom( question );
		} else {
			return returnEmptyLayout();
		}
	}
	
	
	private LinearLayout returnRadioAnswersFrom( Question question ) {
		LinearLayout result = newLinearLayout();
		RadioGroup group = new RadioGroup( this.context );
		
		for( Answer answer : question.answers ) {
			RadioButton button = new RadioButton( this.context );
			button.setText( answer.text );
			group.addView( button );
		}
		
		result.addView( group );
		return result;
	}
	
	
	private LinearLayout returnCheckBoxAnswersFrom( Question question ) {
		LinearLayout result = newLinearLayout( );
		
		for( Answer answer : question.answers ) {
			CheckBox button = new CheckBox( this.context );
			button.setText( answer.text );
			result.addView( button );
		}
		
		return result;
	}
	
	
	private LinearLayout newLinearLayout( ) {
		LinearLayout result = new LinearLayout( context );
		result.setOrientation( LinearLayout.VERTICAL );
		
		return result;
	}
	
	
	private LinearLayout returnEmptyLayout( ) {
		LinearLayout result = new LinearLayout( context );
		result.setVisibility( View.GONE );
		
		return result;
	}
	
	
	private void setOtherOption( EditText openAnswer ) {
		openAnswer.setVisibility( View.VISIBLE );
		openAnswer.setHint( "Inne:" );
	}
	
	
	private void unsetOtherOption( EditText openAnswer ) {
		openAnswer.setVisibility( View.GONE );
	}
}

A oto mieszanina. To chyba nie jest normalne zachowanie adaptera?

02-19 07:41:08.374: D/QuestionAdapter(1797): 15: qw
02-19 07:41:08.374: D/QuestionAdapter(1797): 16: ryy
02-19 07:41:08.374: D/QuestionAdapter(1797): 17: fdhhf
02-19 07:41:08.374: D/QuestionAdapter(1797): 18: aaaaaaa
02-19 07:41:08.374: D/QuestionAdapter(1797): 19: bbbb
02-19 07:41:08.386: D/QuestionAdapter(1797): 20: cccc
02-19 07:41:08.386: D/QuestionAdapter(1797): 21: dsa
02-19 07:41:08.464: E/QuestionAdapter(1797): POSITION: 0
02-19 07:41:08.544: E/QuestionAdapter(1797): POSITION: 1
02-19 07:41:08.564: E/QuestionAdapter(1797): POSITION: 2
02-19 07:41:08.584: E/QuestionAdapter(1797): POSITION: 3
02-19 07:41:08.684: E/QuestionAdapter(1797): POSITION: 0
02-19 07:41:08.824: E/QuestionAdapter(1797): POSITION: 1
02-19 07:41:09.104: D/dalvikvm(1797): GREF has increased to 201
02-19 07:41:09.134: E/QuestionAdapter(1797): POSITION: 2
02-19 07:41:09.384: E/QuestionAdapter(1797): POSITION: 3
02-19 07:41:09.774: E/QuestionAdapter(1797): POSITION: 4
02-19 07:41:10.134: E/QuestionAdapter(1797): POSITION: 5
02-19 07:41:10.584: I/Choreographer(1797): Skipped 217 frames!  The application may be doing too much work on its main thread.
02-19 07:41:10.674: E/QuestionAdapter(1797): POSITION: 0
02-19 07:41:11.124: E/QuestionAdapter(1797): POSITION: 1
02-19 07:41:11.154: E/QuestionAdapter(1797): POSITION: 2
02-19 07:41:11.184: E/QuestionAdapter(1797): POSITION: 3
02-19 07:41:12.274: I/Choreographer(1797): Skipped 34 frames!  The application may be doing too much work on its main thread.
02-19 07:41:12.294: E/QuestionAdapter(1797): POSITION: 0
02-19 07:41:12.355: E/QuestionAdapter(1797): POSITION: 1
02-19 07:41:12.384: E/QuestionAdapter(1797): POSITION: 2
02-19 07:41:12.434: E/QuestionAdapter(1797): POSITION: 3
 
0

Pomieszałeś coś całkiem (ale nie widzę co). Dziedzicz po klasie ArrayAdapter (mniejsze ryzyko błędów bo masz już sporą część metod zaimplementowanych).

1 użytkowników online, w tym zalogowanych: 0, gości: 1