Skip to content

Instantly share code, notes, and snippets.

@slightfoot
Created December 29, 2014 16:09
Show Gist options
  • Save slightfoot/fce61d28cdf4eb7042f7 to your computer and use it in GitHub Desktop.
Save slightfoot/fce61d28cdf4eb7042f7 to your computer and use it in GitHub Desktop.
Example of reusing spannables.
package com.example.test;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Random;
import java.util.Stack;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.StyleSpan;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class SpanReuseExampleActivity extends ActionBarActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Random rand = new Random();
ArrayList<Message> data = new ArrayList<Message>(500);
for(int i = 0; i < 500; i++){
Message item = new Message();
item.message = "This is the " + (i + 1) + " test message to be displayed.";
final int len = item.message.length();
item.start = rand.nextInt(len);
item.end = item.start + rand.nextInt(len - item.start);
data.add(item);
}
ListView listView = new ListView(this);
listView.setAdapter(new MessageAdapter(this, data));
setContentView(listView);
}
public static class Message
{
public String message;
public int start;
public int end;
}
public static class BoldSpan extends StyleSpan
{
public BoldSpan()
{
super(Typeface.BOLD);
}
}
public static class MessageAdapter extends BaseAdapter
{
private ArrayList<Message> mData;
public MessageAdapter(Context context, ArrayList<Message> data)
{
mData = data;
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
SpanReuseTextView view;
if(convertView == null){
final Context context = parent.getContext();
view = new SpanReuseTextView(context);
view.setMinimumHeight((int)(48.0f * context.getResources()
.getDisplayMetrics().density));
view.setTextAppearance(context, android.R.style.TextAppearance);
view.setGravity(Gravity.CENTER);
}
else{
view = (SpanReuseTextView)convertView;
}
final Message item = mData.get(position);
SpannableString spannable = new SpannableString(item.message);
spannable.setSpan(SpanReuseCache.getInstance().get(BoldSpan.class),
item.start, item.end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
view.setText(spannable);
return view;
}
@Override
public Message getItem(int position)
{
return mData.get(position);
}
@Override
public long getItemId(int position)
{
return position;
}
@Override
public boolean hasStableIds()
{
return true;
}
@Override
public int getCount()
{
return mData.size();
}
}
public static class SpanReuseTextView extends TextView
{
public SpanReuseTextView(Context context)
{
this(context, null);
}
public SpanReuseTextView(Context context, AttributeSet attrs)
{
this(context, attrs, android.R.attr.textViewStyle);
}
public SpanReuseTextView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
@Override
public void setText(CharSequence text, BufferType type)
{
final CharSequence oldText = getText();
super.setText(text, type);
if(oldText != null && oldText != text){
if(oldText instanceof Spanned){
SpanReuseCache cache = SpanReuseCache.getInstance();
for(Object span : ((Spanned)oldText).getSpans(0, oldText.length(), Object.class)){
cache.put(span);
}
}
}
}
}
static class SpanReuseCache
{
private static HashMap<String, Stack<Object>> sCache = new HashMap<String, Stack<Object>>();
private static class Singleton {
private static final SpanReuseCache INSTANCE = new SpanReuseCache();
}
public static SpanReuseCache getInstance()
{
return Singleton.INSTANCE;
}
public Object get(Class<?> clazz)
{
synchronized(SpanReuseCache.class){
final String key = clazz.getCanonicalName().intern();
final Stack<Object> stack = sCache.get(key);
if(stack != null){
try{
return stack.pop();
}
catch(EmptyStackException e){
// fall through to default constructor
}
}
}
try{
return clazz.getConstructor().newInstance();
}
catch(Exception e){
throw new RuntimeException(e);
}
}
public void put(Object object)
{
synchronized(SpanReuseCache.class){
final String key = object.getClass().getCanonicalName().intern();
Stack<Object> stack = sCache.get(key);
if(stack == null){
stack = new Stack<Object>();
sCache.put(key, stack);
}
stack.push(object);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment