Toggle button in ListView changes State on Scrolling - android

I think I have searched through all the posts relating to my question but could not find a solution to my problem. I have A ListView with two textviews and one Toggle button. Toggle button Changes its state when I scroll up or down (go out of view). I am using viewholder (suggested on the forum) that does make the button to retain its state but then the recycling property comes into play and mess up my application i.e multiple toggle buttons change their state on one click.
What I want?
I want my toggle button to retain its previous state when it goes out of view and comes back plus when I click one toggle button only that toggle button should work.
Here is my Code for Listview (pardon me for not putting much comments)
public class ActivityB extends AppCompatActivity {
ListView list;
Button edit;
public String text= "";
public SharedPreferences.Editor editor;
public SharedPreferences sharedPreferences;
private ArrayList<String> data = new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
list = (ListView) findViewById(R.id.listView);
generateListContent();
list.setAdapter(new MyListadapter(this,R.layout.constraint,data));
edit= (Button) findViewById(R.id.button);
sharedPreferences=getSharedPreferences("MyData",Context.MODE_PRIVATE);
editor=sharedPreferences.edit();
text=sharedPreferences.getString("Text","Null");
//Toast.makeText(this,text,Toast.LENGTH_LONG).show();
}
public void generateListContent() {
for (int i = 0; i < 11; i++) {
data.add("This is item no " + i);
}
}
private class MyListadapter extends ArrayAdapter<String> {
private int layout;
ArrayList<String> list;
public MyListadapter(#NonNull Context context, #LayoutRes int resource, #NonNull List<String> objects) {
super(context, resource, objects);
layout=resource;
list= (ArrayList<String>) objects;
}
#NonNull
#Override
public View getView(final int position, #Nullable View convertView, #NonNull ViewGroup parent) {
ViewHolder mainviewHolder=null;
if(convertView==null){
LayoutInflater inflater=LayoutInflater.from(getContext());
convertView= inflater.inflate(layout,parent,false);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.title= (TextView) convertView.findViewById(R.id.textView);
viewHolder.lastone= (TextView) convertView.findViewById(R.id.textView2);
viewHolder.button= (ToggleButton) convertView.findViewById(R.id.toggleButton);
//mainviewHolder.title.setText(getItem(position));
viewHolder.button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
buttonView.setChecked(true);
Toast.makeText(getContext(), "Button is On" + position , Toast.LENGTH_LONG).show();
} else {
buttonView.setChecked(false);
Toast.makeText(getContext(), "Button is OFF" + position + buttonView.getId(),Toast.LENGTH_LONG).show();
}
}
});
convertView.setTag(viewHolder);
}
else{
mainviewHolder = (ViewHolder) convertView.getTag();
mainviewHolder.title.setText(getItem(position));
}
return convertView;
}
}
public class ViewHolder{
TextView title;
TextView lastone;
ToggleButton button;
}
}

check this...
Recyclerview Changing Items During Scroll
if above does not work....
Method 1
things you can try...
1.check button status outside toggle method....so that every time the listviewe loaded it can assign a value rather no value.
create a method togglebtn(viewHolder) // pass the instance of this perticualr item
Inside togglebtn....modify the toggle btn by reference with the instance.
togglebtn(viewHolder v)
{
}
Method 2
create a public data structure to hold the status of the button....preferebaly hasmap to store item no+check status....then check this value every time and update.
Feel free to ask.

Instead of creating array list of strings, create a custom object something like below.
Class A {
public String title;
public boolean isChecked;
}
Set correct value to isChecked(true/false) depending on toggle button state.
Also add one more line inside getview()
button.setChecked(list.get(position).isChecked)

Thanks Everyone for sparing your time and answering my question.
With these answers and a little help from forum' other posts, I found a solution.
Since I have a limited number of views so i removed the if/else condition on layout inflater. This solved the reuse of position variable. Now every view object has a unique position. Secondly the random button status behavior is resolved by saving the toggle button state in a separate array and keeping it outside my getview() method.

Related

RecyclerView checkbox deselect all?

I'm wondering how I'd be able to use a button in another class to deselect all checkboxes in my 3 recyclerviews(Tablayout, one recyclerview per tab). I have saved the checked value in shared pref as shown here:
public class MyRecyclerAdapter extends RecyclerView.Adapter<MyViewHolder> {
Context mContext;
ArrayList<Workout> workout;
SharedPreferences prefs;
int firstSecondOrThird;
int colorResId = R.color.defaultcard;
public MyRecyclerAdapter(Context context, ArrayList<Workout> workout, int thePosition) {
mContext = context;
this.workout = workout;
this.firstSecondOrThird = thePosition;
}
// INITIALIZE HOLDER
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.workout_item, null);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
//BIND DATA TO VIEWS
#Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.exercise.setText(workout.get(position).getExercise());
holder.percent.setText(workout.get(position).getPercent());
holder.reps.setText(workout.get(position).getReps());
holder.weight.setText(workout.get(position).getWeight());
holder.check1.setOnCheckedChangeListener(null);
final Workout isCheck = workout.get(position);
prefs = mContext.getSharedPreferences("checkState", Context.MODE_PRIVATE);
holder.check1.setChecked(prefs.getBoolean(firstSecondOrThird+"checkState"+position, false));
isCheck.setCheck1(prefs.getBoolean(firstSecondOrThird+"checkState"+position, false));
holder.check1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
prefs.edit().putBoolean(firstSecondOrThird+"checkState"+position, isChecked).apply();
}
});
}
And here is where I try to clear shared pref, which I thought would also clear all checkmarks:
mButton = (Button) findViewById(R.id.button2);
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
prefs.edit().clear();
prefs.edit().apply();
Toast.makeText(getApplicationContext(), "Checkboxes Cleared.", Toast.LENGTH_LONG).show();
}
});
}
Unfortunately, after clicking the button, all checkboxes that were previously selected are still selected. Thanks in advance!
EDIT: I tried to add an if statement to see if that would work, but now it doesn't load the saved checkmarks are all, even when the button isn't clicked... lol
prefs = mContext.getSharedPreferences("checkState", Context.MODE_PRIVATE);
String restoredCheck = prefs.getString("checkState", null);
if (restoredCheck != null) {
holder.check1.setChecked(prefs.getBoolean(firstSecondOrThird+"checkState"+position, false));
} else holder.check1.setChecked(false);
You need to alter the List inside the adapter, then, call ((MyRecyclerAdapter) mMyListView.getAdapter()).notifyDataSetChanged();
For experience, some times depending on what change are made, the adapter do not update with this, in this cases, i create a new adapter with the new list of data, and set to the ListView, after, you may need to call the notifyDataSetChanged() anyway
Edit:
Well, inside your adapter you use a `List', the state change must be done in that List, and after that you notify the adapter that the data was changed.
So you need a method to get the ArrayList and do something like:
mButton = (Button) findViewById(R.id.button2);
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ArrayList<Workout> list = ((MyRecyclerAdapter) MyListView.getAdapter()).getList();
for(Workout workout : list){
workout.setCheck(true);
}
((MyRecyclerAdapter) MyListView.getAdapter()).notifyDataSetChanged();
}
});
}
I'm not able to test right now, but i think it's some like that, like i said, if this don't work, create another adapter with the new data and set him to the ListView.
Ps. I considered you correctly check if the object isChecked and change the checkbox in the ViewHoler

A TextView change applies to multiple TextViews using ListView and ArrayAdapter

I've started working on a small project not to long ago, the main goal is to forge a way for me to keep track of my actions during the course of 100 weeks.
I'm still a rookie android developer and I've encountered an issue that I couldn't explain.
Basically I've populated a ListView using the ArrayAdapter with a list containing 100 strings (Week1, Week2, Week3 ... Week100)
Setting up an onclicklistener on each of the TextViews so that when a user performs a click on a textview, the background color would change to red.
However; whenever I click a single textview - more than a single textview is being colored.
Notes:
I'm using a ScrollView to scroll through the entire list. (Once populated, the 100 week list fills up the entire screen, the scroll view is used to access the entire list.)
I also saved a reference to the currently painted textview so I could make sure that when a user clicks a different textview, the previous one would lose its red background.
MainActivity initialization:
public class MainActivity extends ActionBarActivity
{
TextView selectedWeek; // Reference to the selected week.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
populateWeekList(); // Populating the ListView
initWeekClick(); // Initializing click listener
}
Populating the ListView:
public void populateWeekList()
{
String[] weeks = new String [100]; // 100 weeks
for (int i=0; i<100;i++)
{
weeks[i] = "Week"+(i+1);
}
ArrayAdapter<String> weekAdapter = new ArrayAdapter<String>(
this,
R.layout.weeksview,
weeks
);
// R.id.weekTypeList is just a normal TextView.
ListView weekList=(ListView) findViewById(R.id.weekTypeList);
weekList.setAdapter(weekAdapter);
}
Code for initializing onClickListener and saving the selectedWeek reference:
public void initWeekClick()
{
ListView weekList=(ListView) findViewById(R.id.weekTypeList);
weekList.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View viewClicked, int position, long id)
{
if (selectedWeek != null)
{
selectedWeek.setBackgroundColor(0);
}
TextView clicked = (TextView) viewClicked;
// Change clicked TextView color to red.
clicked.setBackgroundColor(getResources().getColor(android.R.color.holo_red_light));
// Save the selected week reference
selectedWeek = clicked;
}
});
}
Ok, your background is shuffling because when you scroll your ListView getView() is called and it consider your current position of TextView(as current view) and set background on it as it detect setBackground() method at onClick listener on it..
First I recommend to create a Adapter class extends ArrayAdapter<?>
Solution 1 :
Use setTag() at onClick listener on your text view like..
text.setTag(position);
and above it use getTag() and put condition
if(holder.text.getTag().equals(position)){
holder.text.setBackgroundColor(Color.BLUE);
}else{
holder.text.setBackgroundColor(Color.WHITE);
}
Solution 2 :
Added this to onCreate method
ArrayList<String> _array = new ArrayList<String>();
for(int i=0 ; i <1000; i ++){ // 1000 value
_array.add(i+"");
}
list.setAdapter(new MainAdapter(this, _array)); // pass you list here
ArrayAdapter class :
public class MainAdapter extends ArrayAdapter<String> {
ArrayList<String> _st = new ArrayList<String>();
ArrayList<Integer> check = new ArrayList<Integer>();
Context _context;
public MainAdapter(Context context,ArrayList<String> _st) {
super(context,R.layout.main, _st); // your inflate layout
this._context = context;
this._st = _st;
}
#Override
public int getCount() {
return _st.size();
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
//---//
// check if current position is there in arraylist
if(checking(position)){
holder.text.setBackgroundColor(Color.BLUE);
}else{
holder.text.setBackgroundColor(Color.WHITE);
}
holder.text.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
// set background and put value in array list
holder.text.setBackgroundColor(Color.BLUE);
check.add(position);
}
});
return convertView;
}
// this will check whether current position is there is array list or not and if it there it will break loop and return true
public boolean checking(int position){
boolean fine = false;
for(int i=0; i<check.size();i++){
if(position == check.get(i)){
fine = true;
break;
}
}
return fine;
}
}
public class ViewHolder{
TextView text;
}
}
I don't how much I am ethical in this code...but as you have specified that you have 100 value.I have tested it on 1000 value and it worked
I am not expert so let me know if I am wrong somewhere
Hope it works !!!

For loop in listview

I have a ListView that uses a custom adapter. Each row contains 2 CheckBoxes that I want to check if they are ticked.
How would I loop through each row to check this and then, if they are checked, change a boolean array at the row position to true, then save these arrays using SharedPreferences?
And how would I set each CheckBox after opening the activity again by looking if the array at the position is true or false?
Thanks.
ListAdapter
public class LockerAdapter extends ArrayAdapter<LockerData> {
private Context mContext;
private int mLayoutResourceId;
LockerData[] mData = null;
CheckBox checkBoxHwk;
CheckBox checkBoxLkr;
//trying to change these arrays if boxes are ticked
public boolean[] homeworkCheck = new boolean[6];
public boolean[] lockerCheck = new boolean[6];
public LockerAdapter(Context context, int resource, LockerData[] objects) {
super(context, resource, objects);
this.mContext = context;
this.mLayoutResourceId = resource;
this.mData = objects;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
LockerHolder holder = null;
if(row == null){
LayoutInflater inflater = LayoutInflater.from(mContext);
row = inflater.inflate(mLayoutResourceId,parent,false);
holder = new LockerHolder();
holder.theLessonName = (TextView) row.findViewById(R.id.generalLessonName);
holder.hwkCheck = (CheckBox) row.findViewById(R.id.homeworkCheck);
holder.lkrCheck = (CheckBox) row.findViewById(R.id.lockerCheck);
row.setTag(holder);
}else {
holder = (LockerHolder) row.getTag();
}
LockerData lesson = mData[position];
holder.theLessonName.setText(lesson.lessonsName);
return row;
}
public class LockerHolder{
TextView theLessonName;
CheckBox hwkCheck;
CheckBox lkrCheck;
}
}
Activity
public class HomeworkLockerActivity extends Activity {
private ListView hwkListview;
private LockerAdapter lockerAdapter;
private CheckBox checkBoxChem;
private CheckBox checkBoxPhys;
private CheckBox checkBoxMech;
private CheckBox checkBoxFP;
private CheckBox checkBoxCore;
private CheckBox checkBoxStats;
/*LockerData is just a class that takes in a single string in the
constructor to store the data*/
public LockerData[] lessons = {
new LockerData("Chemistry"),
new LockerData("Physics"),
new LockerData("Mechanics"),
new LockerData("Further Pure"),
new LockerData("Core 1"),
new LockerData("Statistics"),
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_homework_locker);
hwkListview = (ListView) findViewById(R.id.homeworkListview);
lockerAdapter = new LockerAdapter(getApplicationContext(),R.layout.list,lessons);
hwkListview.setAdapter(lockerAdapter);
}
}
I recommend editing your LockerData class to accept three inputs: the name (ex: Chemistry), whether the homework is checked, and whether the locker is checked. Then you could edit your getView() in your adapter to case over the boolean values within the LockerData class instance, updating your checkboxes accordingly. With this approach, your two lists homeworkCheck and lockerCheck are not necessary.
As for Shared Preferences, I believe that you can only store primitive values (ex: boolean, int, string, etc.) and not lists. I would use an onCheckChanged Listener to update your shared preferences every time a checkbox is updated; so something like:
holder.hwkCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
SharedPreferences.Editor editor = settings.edit();
//check what kind of button this is, in this case its chemistry
editor.putBoolean("chemistryCheck", isChecked);
}
}
);
I would retrieve these shared preferences in your Activity's onCreate class, and initialize your LockerData[] lessons accordingly.

Strikethrough an ArrayList<String> item when long pressed

I'm trying to make a simple to-do list where you would long-press an item to mark it as 'done', in which case it will be greyed out and strikethrough.
I'm working on the strikethrough first and found some sample code here creating a strikethrough text in Android? . However the problem is that the setPaintFlags() method only seems to work on TextView whereas the items on my list are String. I can't cast a String to a TextView, and I found a workaround here but apparently it's highly discouraged to do it: Cast String to TextView . Also I looked up SpannableString but it doesn't seem to work for strings of varying length.
So I'm back at square one - is it at all possible to implement what I'm trying to do? Or will I have to store my list items differently instead?
Relevant code:
public class MainActivity extends ActionBarActivity {
private ArrayList<String> items;
private ArrayAdapter<String> itemsAdapter;
private ListView lvItems;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Setting what the ListView will consist of
lvItems = (ListView) findViewById(R.id.lvItems);
readItems();
itemsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items);
lvItems.setAdapter(itemsAdapter);
// Set up remove listener method call
setupListViewListener();
}
//Attaches a long click listener to the listview
private void setupListViewListener() {
lvItems.setOnItemLongClickListener(
new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapter,
View item, int pos, long id) {
// Trying to make the onLongClick strikethrough the text
String clickedItem = items.get(pos);
//What do I do here??
// Refresh the adapter
itemsAdapter.notifyDataSetChanged();
writeItems();
// Return true consumes the long click event (marks it handled)
return true;
}
});
}
Let's take a step back and consider your app. You want to show a list of jobs to the user. Each job has a description. And each job has two possible states: 'done' or 'not done'.
So I would like to introduce a class 'Job'
class Job
{
private String mDescription;
private boolean mDone;
public Job(String description)
{
this.mDescription = description;
this.mDone = false;
}
// ... generate the usual getters and setters here ;-)
// especially:
public boolean isDone()
{
return mIsDone;
}
}
This way your ArrayList 'items' becomes be a ArrayList< Job >. Wether a job is done or not will be stored together with its description. This is important because you want to show the current state of the job to the user by changing the look of the UI element, but you need to keep track of the job's state on the data level as well.
The UI element - the TextView - will be configured to present information about the job to the user. One piece of information is the description. The TextView will store this as a String. The other piece of information is the state (done/ not done). The TextView will (in your app) store this by setting the strike-through flag and changing its color.
Because for performance reasons a ListView uses less elements than the data list ('items') contains, you have to write a custom adapter. For brevity's sake, I'm keeping the code very simple, but it's worth the time to read up on the View Holder pattern:
Let's use a layout file 'mytextviewlayout.xml' for the list rows:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Medium Text"
android:id="#+id/textView"/>
</LinearLayout>
Now the code for the adapter looks like this:
EDIT changed from ArrayAdapter to BaseAdapter and added a view holder (see comments):
public class MyAdapter extends BaseAdapter
{
private ArrayList<Job> mDatalist;
private int mLayoutID;
private Activity mCtx;
private MyAdapter(){} // the adapter won't work with the standard constructor
public MyAdapter(Activity context, int resource, ArrayList<Job> objects)
{
super();
mLayoutID = resource;
mDatalist = objects;
mCtx = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = convertView;
if (rowView == null) {
LayoutInflater inflater = mCtx.getLayoutInflater();
rowView = inflater.inflate(mLayoutID, null);
ViewHolder viewHolder = new ViewHolder();
viewHolder.tvDescription = (TextView) rowView.findViewById(R.id.textView);
rowView.setTag(viewHolder);
}
ViewHolder vholder = (ViewHolder) rowView.getTag();
TextView tvJob = vholder.tvDescription;
Job myJob = mDatalist.get(position);
tvJob.setText(myJob.getJobDescription());
if (myJob.isDone())
{
// apply changes to TextView
tvJob.setPaintFlags(tvJob.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
tvJob.setTextColor(Color.GRAY);
}
else
{
// show TextView as usual
tvJob.setPaintFlags(tvJob.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
tvJob.setTextColor(Color.BLACK); // or whatever is needed...
}
return rowView;
}
#Override
public int getCount()
{
return mDatalist.size();
}
#Override
public Object getItem(int position)
{
return mDatalist.get(position);
}
#Override
public long getItemId(int position)
{
return position;
}
static class ViewHolder
{
public TextView tvDescription;
}
}
Due to the changed adapter,
in the MainActivity, you have to declare 'items' and 'itemsAdapter' as follows:
private ArrayList<Job> items;
private MyAdapter itemsAdapter;
...and in your 'onCreate()' method, you write:
itemsAdapter = new MyAdapter<String>(this, R.layout.mytextviewlayout, items);
Don't forget to change the 'readItems()' and 'writeItems()' methods because 'items' now is a ArrayList< Job >.
Then, finally, the 'onItemLongClick()' method:
EDIT use 'parent.getItemAtPosition()' instead of 'items.get()', see comments
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
{
// items.get(position).setDone(true);
Object o = parent.getItemAtPosition(position);
if (o instanceof Job)
{
((Job) o).setDone(true);
}
// and now indeed the data set has changed :)
itemsAdapter.notifyDataSetChanged();
writeItems();
return true;
}

How does the checkbox know its association with the textview

I am coding a checklist on android, following an example from a textbook.
The ListActivity consists of a listview (R.layout.PCheckList) where each row has a checkbox and a textview locked inside a horizontal layout (R.layout.lchecklist).
There is a line of the code which I totally do NOT understand
The code is below, and the i've highlighted the line
public class PChecklist extends ListActivity {
TextView selection;
String[] tasks={"Do Laundry",
"Wash Dishes",
"Make the bed",
"Study",
"Buy Groceries",
"Mow the lawn",
"Shave",
"Iron Clothes",
"Breathe periodically"};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.pchecklist);
this.selection = (TextView) findViewById(R.id.selection2);
ArrayList<DailyTask> dailyTaskListModel = new ArrayList<DailyTask>();
for(String t:tasks)
{
dailyTaskListModel.add(new DailyTask(t));
}
this.setListAdapter(new CheckListAdapter(this,dailyTaskListModel));
}
private DailyTask getTaskAt(int position)
{
return ((CheckListAdapter)getListAdapter()).getItem(position);
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
this.selection.setText(String.format("Selection: %s", getTaskAt(position).toString()));
}
class DailyTask
{
String task;
boolean isCompleted = false;
DailyTask(String task)
{
this.task = task;
}
public String toString()
{
if(this.isCompleted)
return (task.toUpperCase());
else return (task);
}
}
class CheckListAdapter extends ArrayAdapter<DailyTask>
{
Activity activity;
CheckListAdapter(Activity context,ArrayList<DailyTask> taskList)
{
super(context, R.layout.lchecklist,taskList);
this.activity = context;
}
public View getView(int position, View convertView, ViewGroup parent) {
View recycledView = convertView;
CheckListViewAccessor checkListViewAccessor=null;
CheckBox checkbox;
if(recycledView==null)
{
//if there is no view, we have to make one by inflating a layout.
LayoutInflater inflater = activity.getLayoutInflater();
recycledView = inflater.inflate(R.layout.lchecklist, null,false);
checkListViewAccessor = new CheckListViewAccessor(recycledView);
recycledView.setTag(recycledView);
checkbox = checkListViewAccessor.getCheckBox();
CompoundButton.OnCheckedChangeListener checkStateChangedListener = new CompoundButton.OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton cb, boolean isChecked) {
//When the check button is pressed, we want two things to happen.
//1. Update the model.
//for some reason we have to do this.
int positionOfCheckedItem = (Integer) cb.getTag();
DailyTask task = getTaskAt(positionOfCheckedItem);
task.isCompleted = isChecked;
//2. Change the String in the row to upper case.
cb.setText(task.toString());
}
};
checkbox.setOnCheckedChangeListener(checkStateChangedListener);
}else//if recycledView is not null, then we don't need to add a listener, we just need to get access to the UI components
{
checkListViewAccessor = (CheckListViewAccessor) recycledView.getTag();
checkbox = checkListViewAccessor.getCheckBox();
}
DailyTask task = getTaskAt(position);
checkbox.setTag(new Integer(position));
**checkbox.setText(task.toString());
//^This line I don't get.**
checkbox.setChecked(task.isCompleted);
return (recycledView);
}
}
class CheckListViewAccessor
{
View checkListView;
CheckBox checkbox=null;
CheckListViewAccessor(View checkListView)
{
this.checkListView = checkListView;
}
CheckBox getCheckBox()
{
if(checkbox==null)
this.checkbox = (CheckBox) findViewById(R.id.checkbox);
return (checkbox);
}
}
}
In the line indicated, how does the checkbox know which TextView's text to change? When was this relationship established?
There is no associated TextView with CheckBox. Because CheckBox is a TextView. Take a look at hierarchy here:
ava.lang.Object
↳ android.view.View
↳ android.widget.TextView
↳ android.widget.Button
↳ android.widget.CompoundButton
↳ android.widget.CheckBox
In other words CheckBox is a TextView with additional state management for checked and unchecked. (In fact CompoundButton is responsible for state management, but those are details).
The relationship is established by the adapter. For each list element, the "checkbox" object corresponds to the actual list element's checkbox. This means it's very easy to modify the Views of the list elements, and in extension it's very tricky to alter another element from within a particular element in the getView().
You might think of it this way; in the adapter - for every getView() - the current element will be seen as "local variables" from your point of view. You won´t have to bother about all global elements at this time.

Categories

Resources