Edit:
I'm now able to strike words in the listview but only able to do it from the bottom up and can only unstrike the bottom item afterwards. The code is below.
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final SubTask subTask = getItem(position);
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.subtask_adapter_list, parent, false);
}
final TextView tasksTitle = (TextView)convertView.findViewById(R.id.tasksName);
tasksTitle.setTypeface(typeface);
completed = (CheckBox)convertView.findViewById(R.id.completed);
changeColor();
tasksTitle.setText(subTask.getSubTask());
completed.setChecked(subTask.isCompleted());
toggleLineThrough(tasksTitle, tasksTitle.getText().toString(), subTask.getSubTask(), completed.isChecked());
completed.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(completed.isChecked()){
subTask.setCompleted(true);
subTask.save();
toggleLineThrough(tasksTitle, tasksTitle.getText().toString(), subTask.getSubTask(), completed.isChecked());
} else {
subTask.setCompleted(false);
subTask.save();
toggleLineThrough(tasksTitle, tasksTitle.getText().toString(), subTask.getSubTask(), completed.isChecked());
}
}
});
return convertView;
}
private void toggleLineThrough(TextView tasksTitle, String title, String oldTitle, boolean completed){
SpannableString styledString = new SpannableString(title);
if(completed == true){
styledString.setSpan(new StrikethroughSpan(), 0, title.length(), 0);
tasksTitle.setText(styledString);
} else {
tasksTitle.setText(oldTitle);
}
}
I'm trying to have the strike going across a TextView if it's criteria is met. Logically it works, but only for the last Item in the ListView. All other Items don't seem to create the strike at all. My code is below:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
subTask = getItem(position);
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.subtask_adapter_list, parent, false);
}
tasksTitle = (TextView)convertView.findViewById(R.id.tasksName);
tasksTitle.setTypeface(typeface);
completed = (CheckBox)convertView.findViewById(R.id.completed);
changeColor();
tasksTitle.setText(subTask.getSubTask());
toggleLineThrough(subTask.getSubTask());
completed.setChecked(subTask.isCompleted());
completed.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(completed.isChecked()){
subTask.setCompleted(true);
subTask.save();
toggleLineThrough(subTask.getSubTask());
} else {
subTask.setCompleted(false);
subTask.save();
toggleLineThrough(subTask.getSubTask());
}
}
});
return convertView;
}
private void toggleLineThrough(String text){
SpannableString styledString = new SpannableString(text);
if(completed.isChecked()){
styledString.setSpan(new StrikethroughSpan(), 0, text.length(), 0);
tasksTitle.setText(styledString);
} else {
tasksTitle.setText(text);
}
}
I just can't seem to get the strike to work for them all. I've tried also using the Paint Flags way with the same result (even hiding my TextView altogether). Any help using Spannable String for my TextViews in the ListView would be really appreciated.
You should pass checked state and tasksTitle
to toggleLineThrough()
too.
Variables completed
and tasksTitle
contain wrong values because onClickListener is called later than getView()
. They were overwritten several times by consecutive calls of getView()
.
@SmiffyKmc, I am trying to use the following to answer other similar questions, so some comments may not related to your problem.
Have made a completed sample here:
in strings.xml:
<string-array name="list_items_sample">
<item>One</item>
<item>Two</item>
<item>Three</item>
<item>Four</item>
<item>Five</item>
<item>Six</item>
<item>Seven</item>
<item>Eight</item>
<item>Nine</item>
<item>Ten</item>
<item>Eleven</item>
<item>Twelve</item>
<item>Thirteen</item>
<item>Fourteen</item>
<item>Fifteen</item>
<item>Sixteen</item>
<item>Seventeen</item>
<item>Eighteen</item>
<item>Nineteen</item>
<item>Twenty</item>
</string-array>
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="7dp" >
</ListView>
custom_list_items.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="20dp"
android:paddingTop="20dp" >
<CheckBox
android:id="@+id/cb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:focusable="false" />
<TextView
android:id="@+id/tvId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/cb"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:gravity="center"
android:minWidth="70dp"
android:textSize="25sp" />
<TextView
android:id="@+id/tvDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/cb"
android:layout_alignParentTop="true"
android:layout_toEndOf="@id/tvId"
android:layout_toLeftOf="@+id/cb"
android:layout_toRightOf="@id/tvId"
android:layout_toStartOf="@id/cb" />
MainActivity.java:
public class MainActivity extends Activity {
static final String ITEM_ID = "item_id";
static final String ITEM_NAME = "item_name";
static final String ITEM_CHECKED ="item_checked";
ListView mListView;
ArrayList<HashMap<String, Object>> mDataList = new ArrayList<>();
ArrayAdapter<HashMap<String, Object>> mArrayAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.listView1);
String items[] = getResources().getStringArray(R.array.list_items_sample);
mDataList.clear();
for(int i=1; i<20; i++){
HashMap<String, Object> newItem = new HashMap<String, Object>();
newItem.put(ITEM_ID, String.valueOf(i));
newItem.put(ITEM_NAME, "Sample<"+items[i-1]+">");
newItem.put(ITEM_CHECKED, false);
mDataList.add(newItem);
}
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
mArrayAdapter = new ArrayAdapter<HashMap<String, Object>>(getApplicationContext(), R.layout.custom_list_items, mDataList){
class ViewHolder{
CheckBox cb;
TextView tvId;
TextView tvDescription;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
// Prepare views ready for data
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.custom_list_items, parent, false);
holder = new ViewHolder();
holder.cb = (CheckBox) convertView.findViewById(R.id.cb);
holder.tvId = (TextView) convertView.findViewById(R.id.tvId);
holder.tvDescription = (TextView) convertView.findViewById(R.id.tvDescription);
convertView.setTag(holder);
holder.cb.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View view) {
// Get view, position and DataList(position)
CheckBox buttonView = (CheckBox) view;
int pos = (int) buttonView.getTag();
HashMap<String, Object> selectedItem = new HashMap<String, Object>();
selectedItem = getItem(pos);
// Update DataList
remove(selectedItem);
selectedItem.remove(ITEM_CHECKED);
if(buttonView.isChecked()){
selectedItem.put(ITEM_CHECKED, true);
insert(selectedItem, pos);
}else{
selectedItem.put(ITEM_CHECKED, false);
insert(selectedItem, pos);
}
// Update all UI views by
notifyDataSetChanged();
// or Update the affected views directly
// View itemView = (View) buttonView.getParent();
// TextView tv = (TextView) itemView.findViewById(R.id.tvDescription);
// toggleLineThrough(tv, (String)getItem(pos).get(ITEM_NAME), buttonView.isChecked());
}
});
}else{
holder = (ViewHolder) convertView.getTag();
}
// Get data from DataList
HashMap<String, Object> currentItem = getItem(position);
// Setup List items UI
holder.tvId.setText((String)currentItem.get(ITEM_ID));
holder.cb.setChecked((boolean)currentItem.get(ITEM_CHECKED));
toggleLineThrough(holder.tvDescription, (String)currentItem.get(ITEM_NAME), (boolean)currentItem.get(ITEM_CHECKED));
// Save position to CheckBox, so position can be retrieved when CheckBox is clicked
holder.cb.setTag(position);
return convertView;
}
private void toggleLineThrough(TextView tasksTitle, String title, boolean completed){
SpannableString styledString = new SpannableString(title);
if(completed == true){
styledString.setSpan(new StrikethroughSpan(), 0, title.length(), 0);
tasksTitle.setText(styledString);
}else{
tasksTitle.setText(title);
}
}
};
mListView.setAdapter(mArrayAdapter);
}
}
Some important points:
- Keep state of the CheckBox as a field in data list and setup UI accordingly, otherwise ListView will be wrong after scroll.
- getView() is over when the ListView is shown. OnClickListener SHOULD NOT use the data obtained from getView().
- OnClickListener SHOULD ABLE and NEED to get data from data list. The simplest method may be: "View.setTag(position) in getView(), View.getTag() in OnClickListener()".
- Don't just modified views only. Change data, data list first. Then call notifyDataSetChanged(). If there is only a small change to UI, can do it directly but should be the same as calling notifyDataSetChanged().
Hope that help!
来源:https://stackoverflow.com/questions/39926388/spannable-string-only-working-for-last-item-in-listview