androidlistadapterfastscroll

ListAdapter with implemented SectionIndexer throws ClassCastException


I'm trying to implement a ListAdapter by implementing the Interface not extending BaseAdapter sub classes like ArrayAdapter. But all I get in return is a ClassCastException telling me nothing about its specific cause.

First about the Application I want to develop:

I want to create an Hex View (and later an Hex-Editor) for Android enabled devices. The ListAdapter was business as usual and works flawlessly. The SectionIndexer part however does not. I want to enable the user to scroll through large files so he would not get tired fingers during normal scrolling. So I decided to split a loaded file into 1024 Byte Parts (my fast scroll sections).

So let me show you the code:

package in.waslos.kneo.hexviewer.models;

import in.waslos.kneo.hexviewer.R;
import in.waslos.kneo.hexviewer.views.ByteTextView;

import java.util.ArrayList;

import android.content.Context;
import android.database.DataSetObserver;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.SectionIndexer;
import android.widget.TextView;

public class ByteViewModel implements ListAdapter,SectionIndexer  {
private ArrayList<DataSetObserver> listener = new ArrayList<DataSetObserver>();
private byte[] byteData; //contains the byte data from some file
private LayoutInflater xmlInflater;
private Context context;

private StringBuffer buffer = new StringBuffer();

private String[] sections;

public ByteViewModel(Context context,byte[] data) {
    if(data != null && context != null){
        this.byteData = data;
        this.context = context;
        
        sections = new String[data.length/1024]; //1K sections
        for(int i=0;i<sections.length;i++){
            sections[i]=String.format("%d", (i+1));
            Log.e("sections",sections[i]);
        }
    }
}

public class ViewHolder{
    TextView address;
    ByteTextView binData;
    TextView asciiData;
}

private ViewHolder viewHolder;

@Override
public boolean areAllItemsEnabled() {
    return true;
}

@Override
public boolean isEnabled(int position) {
    return true;
}

@Override
public int getCount() {
    return byteData.length%16 == 0 ? byteData.length/16: byteData.length/16 + 1;
}

@Override
public Object getItem(int position) {
    return byteData;
}

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

@Override
public int getItemViewType(int position) {
    return 0;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if(convertView==null){
        if(xmlInflater==null){
            xmlInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }
        convertView = xmlInflater.inflate(R.layout.hexviewitem, parent,false);
        
        viewHolder = new ViewHolder();
        viewHolder.address = (TextView)convertView.findViewById(R.id.address_view);
        viewHolder.binData = (ByteTextView)convertView.findViewById(R.id.byte_view);
        viewHolder.asciiData= (TextView)convertView.findViewById(R.id.data_view);
        convertView.setTag(viewHolder);
    }
    viewHolder = (ViewHolder)convertView.getTag();
    //get leading zeros for the address/offset column
    viewHolder.address.setText(Long.toString(position*16 + 0x100000000L, 16).substring(1));
//costume view for the byte view column
    viewHolder.binData.setByteValue(position*16, byteData); 
    viewHolder.asciiData.setText(getAsciiRep(position));
    
    
    return convertView;
}

@Override
public int getViewTypeCount() {
    return 1;
}

@Override
public boolean hasStableIds() {
    return true;
}

@Override
public boolean isEmpty() {
    return false;
}

@Override
public void registerDataSetObserver(DataSetObserver observer) {
    listener.add(observer);
}

@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
    listener.remove(observer);
}
//gets string ascii representation for a byte
private String getAsciiRep(int pos){
    pos = pos*16;
    buffer.delete(0, buffer.length());      
    for(int i = 0;i<16;i++){
        if(pos+i<byteData.length)
            if(byteData[pos+i]<0x20){
            buffer.append('.');
            } else {
                buffer.append((char)byteData[pos+i]);               
            }
        else {
            buffer.append(' ');
        }
    }
    
    return buffer.toString();
}

@Override
public int getPositionForSection(int section) {
    return section*1024;//this function is never called!
}

@Override
public int getSectionForPosition(int position) {
    return position/1024;//this function is never called!
}

@Override
public Object[] getSections() {
    Log.e("Indexer", "returning sections!"); //this function is never called!
    return sections;
}
}

I did not use hash maps as in the tutorial since I only scroll through an static array. So returning positions should be enough. Which does not matter since the functions were never called anyways.

Everything still works you can scroll by normal fling but if you touch the "grabber" the application notifies you about its forced close. I first thought it works only with on character section strings but this did not solve the problem either.

However here is the stack trace I get:

11-04 18:29:13.914: W/dalvikvm(17078): threadid=1: thread exiting with uncaught exception (group=0x4012b560)
11-04 18:29:13.924: E/AndroidRuntime(17078): FATAL EXCEPTION: main
11-04 18:29:13.924: E/AndroidRuntime(17078): java.lang.ClassCastException:in.waslos.kneo.hexviewer.models.ByteViewModel
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.widget.FastScroller.getSectionsFromIndexer(FastScroller.java:291)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.widget.FastScroller.onTouchEvent(FastScroller.java:435)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.widget.AbsListView.onTouchEvent(AbsListView.java:2141)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.widget.ListView.onTouchEvent(ListView.java:3446)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.view.View.dispatchTouchEvent(View.java:3901)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:903)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:869)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:869)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:869)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:869)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1737)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1153)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.app.Activity.dispatchTouchEvent(Activity.java:2096)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1721)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.view.ViewRoot.deliverPointerEvent(ViewRoot.java:2200)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.view.ViewRoot.handleMessage(ViewRoot.java:1884)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.os.Handler.dispatchMessage(Handler.java:99)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.os.Looper.loop(Looper.java:130)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at android.app.ActivityThread.main(ActivityThread.java:3835)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at java.lang.reflect.Method.invokeNative(Native Method)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at java.lang.reflect.Method.invoke(Method.java:507)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
11-04 18:29:13.924: E/AndroidRuntime(17078):    at dalvik.system.NativeStart.main(Native Method)

If you need system information: I use an HTC Desire GSM running Android 2.3.7 (CyanogenMod). I also tried an emulated environment (Android 2.1) which force closes too.

I did not figure out what I'm doing wrong. Android does nothing tell me a more than this stack trace. Google search to this specific inquiry had come up empty or "not working".

I used this tutorial for my ListAdapter:

Android ListView with fast scroll and section index


Solution

  • I found the solution by looking through the issues list at google code. Should have done this before posting so sorry for that.

    Anyways:

    Its a framework bug which is more than two years old by now. So lets hope it will be fixed soon.

    To avoid or work around this bug you need to replace implements ListAdapter by extends BaseAdapter

    The funny part is that the Bugreporter already provided a solution for this bug.

    However its still present in Android 4.0 (tested in emulator)! So I hope this Question will draw more attention to this little problem.

    See: Android issue 3522