自定义ArrayAdapter

时间:2016-05-18 09:47:53

标签: java android android-arrayadapter

大家早上好。为了有一个自动完成的editText可以找到括号或括号之间的字符序列,我遵循这个AutoCompleteTextView not completing words inside parentheses,但我创建的CustomArrayAdapter创建了一个NoClassDefFoundError:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.paullemesnager.flashbac, PID: 4665
    java.lang.NoClassDefFoundError: com.paullemesnager.flashbac.CustomArrayAdapter
    at com.paullemesnager.flashbac.SelectAssocActivity.onCreate(SelectAssocActivity.java:64)
    at android.app.Activity.performCreate(Activity.java:5990)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2332)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2442)
    at android.app.ActivityThread.access$800(ActivityThread.java:156)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1351)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:211)
    at android.app.ActivityThread.main(ActivityThread.java:5373)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1020)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:815)

我无法理解为什么会收到此错误以及如何解决此错误,有人可以帮助我吗?

编辑:CustomArrayAdapter的代码与here相同,但我更改了一个类:

 private class ArrayFilter extends Filter {
    @Override
    protected FilterResults performFiltering(CharSequence prefix) {
        FilterResults results = new FilterResults();

        if (mOriginalValues == null) {
            synchronized (mLock) {
                mOriginalValues = new ArrayList<T>(mObjects);
            }
        }

        if (prefix == null || prefix.length() == 0) {
            ArrayList<T> list;
            synchronized (mLock) {
                list = new ArrayList<T>(mOriginalValues);
            }
            results.values = list;
            results.count = list.size();
        } else {
            String prefixString = prefix.toString().toLowerCase();

            ArrayList<T> values;
            synchronized (mLock) {
                values = new ArrayList<T>(mOriginalValues);
            }

            final int count = values.size();
            final ArrayList<T> newValues = new ArrayList<T>();

            for (int i = 0; i < count; i++) {
                final T value = values.get(i);
                final String valueText = value.toString().toLowerCase();

                // First match against the whole, non-splitted value
                if (valueText.startsWith(prefixString)) {
                    newValues.add(value);
                } else {
                    final String[] words = valueText.split(" ");
                    final int wordCount = words.length;

                    // Start at index 0, in case valueText starts with space(s)
                    for (int k = 0; k < wordCount; k++) {
                        if (words[k].startsWith(prefixString)) {
                            newValues.add(value);
                            break;
                        }
                    }
                }
            }

            results.values = newValues;
            results.count = newValues.size();
        }

        return results;
    }

    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
        //noinspection unchecked
        mObjects = (List<T>) results.values;
        if (results.count > 0) {
            notifyDataSetChanged();
        } else {
            notifyDataSetInvalidated();
        }
    }
}

编辑2:这里要求我如何创建CustomArrayAdapter对象:

      public class SelectAssocActivity extends AppCompatActivity {
      private CustomArrayAdapter<String> adapterA;

      .
      .
      .

    lv = (ListView) findViewById(R.id.lvAssoc);
    textView = (AutoCompleteTextView) findViewById(R.id.editTextAssoc);
    adapterA = new CustomArrayAdapter<>(this, android.R.layout.simple_list_item_1, associations);
    lv.setAdapter(adapterA);

CustomArrayAdapter.java:

      package com.paullemesnager.flashbac;

      import android.content.Context;
      import android.content.res.Resources;
      import android.util.Log;
      import android.view.ContextThemeWrapper;
      import android.view.LayoutInflater;
      import android.view.View;
      import android.view.ViewGroup;
      import android.widget.BaseAdapter;
      import android.widget.Filter;
      import android.widget.Filterable;
      import android.widget.TextView;
      import android.widget.ThemedSpinnerAdapter;

      import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.Collection;
      import java.util.Collections;
      import java.util.Comparator;
      import java.util.List;

public class CustomArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
/**
 * Lock used to modify the content of {@link #mObjects}. Any write operation
 * performed on the array should be synchronized on this lock. This lock is also
 * used by the filter (see {@link #getFilter()} to make a synchronized copy of
 * the original array of data.
 */
private final Object mLock = new Object();

private final LayoutInflater mInflater;

/**
 * Contains the list of objects that represent the data of this CustomArrayAdapter.
 * The content of this list is referred to as "the array" in the documentation.
 */
private List<T> mObjects;

/**
 * The resource indicating what views to inflate to display the content of this
 * array adapter.
 */
private int mResource;

/**
 * The resource indicating what views to inflate to display the content of this
 * array adapter in a drop down widget.
 */
private int mDropDownResource;

/**
 * If the inflated resource is not a TextView, {@link #mFieldId} is used to find
 * a TextView inside the inflated views hierarchy. This field must contain the
 * identifier that matches the one defined in the resource file.
 */
private int mFieldId = 0;

/**
 * Indicates whether or not {@link #notifyDataSetChanged()} must be called whenever
 * {@link #mObjects} is modified.
 */
private boolean mNotifyOnChange = true;

private Context mContext;

// A copy of the original mObjects array, initialized from and then used instead as soon as
// the mFilter ArrayFilter is used. mObjects will then only contain the filtered values.
private ArrayList<T> mOriginalValues;
private ArrayFilter mFilter;

/** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */
private LayoutInflater mDropDownInflater;

/**
 * Constructor
 *
 * @param context The current context.
 * @param resource The resource ID for a layout file containing a TextView to use when
 *                 instantiating views.
 */
public CustomArrayAdapter(Context context,  int resource) {
    this(context, resource, 0, new ArrayList<T>());
}

/**
 * Constructor
 *
 * @param context The current context.
 * @param resource The resource ID for a layout file containing a layout to use when
 *                 instantiating views.
 * @param textViewResourceId The id of the TextView within the layout resource to be populated
 */
public CustomArrayAdapter(Context context,  int resource,  int textViewResourceId) {
    this(context, resource, textViewResourceId, new ArrayList<T>());
}

/**
 * Constructor
 *
 * @param context The current context.
 * @param resource The resource ID for a layout file containing a TextView to use when
 *                 instantiating views.
 * @param objects The objects to represent in the ListView.
 */
public CustomArrayAdapter(Context context,  int resource,  T[] objects) {
    this(context, resource, 0, Arrays.asList(objects));
}

/**
 * Constructor
 *
 * @param context The current context.
 * @param resource The resource ID for a layout file containing a layout to use when
 *                 instantiating views.
 * @param textViewResourceId The id of the TextView within the layout resource to be populated
 * @param objects The objects to represent in the ListView.
 */
public CustomArrayAdapter(Context context,  int resource,  int textViewResourceId,
                           T[] objects) {
    this(context, resource, textViewResourceId, Arrays.asList(objects));
}

/**
 * Constructor
 *
 * @param context The current context.
 * @param resource The resource ID for a layout file containing a TextView to use when
 *                 instantiating views.
 * @param objects The objects to represent in the ListView.
 */
public CustomArrayAdapter(Context context,  int resource,  List<T> objects) {
    this(context, resource, 0, objects);
}

/**
 * Constructor
 *
 * @param context The current context.
 * @param resource The resource ID for a layout file containing a layout to use when
 *                 instantiating views.
 * @param textViewResourceId The id of the TextView within the layout resource to be populated
 * @param objects The objects to represent in the ListView.
 */
public CustomArrayAdapter(Context context, int resource,  int textViewResourceId,
                           List<T> objects) {
    mContext = context;
    mInflater = LayoutInflater.from(context);
    mResource = mDropDownResource = resource;
    mObjects = objects;
    mFieldId = textViewResourceId;
}

/**
 * Adds the specified object at the end of the array.
 *
 * @param object The object to add at the end of the array.
 */
public void add(T object) {
    synchronized (mLock) {
        if (mOriginalValues != null) {
            mOriginalValues.add(object);
        } else {
            mObjects.add(object);
        }
    }
    if (mNotifyOnChange) notifyDataSetChanged();
}

/**
 * Adds the specified Collection at the end of the array.
 *
 * @param collection The Collection to add at the end of the array.
 */
public void addAll(Collection<? extends T> collection) {
    synchronized (mLock) {
        if (mOriginalValues != null) {
            mOriginalValues.addAll(collection);
        } else {
            mObjects.addAll(collection);
        }
    }
    if (mNotifyOnChange) notifyDataSetChanged();
}

/**
 * Adds the specified items at the end of the array.
 *
 * @param items The items to add at the end of the array.
 */
public void addAll(T ... items) {
    synchronized (mLock) {
        if (mOriginalValues != null) {
            Collections.addAll(mOriginalValues, items);
        } else {
            Collections.addAll(mObjects, items);
        }
    }
    if (mNotifyOnChange) notifyDataSetChanged();
}

/**
 * Inserts the specified object at the specified index in the array.
 *
 * @param object The object to insert into the array.
 * @param index The index at which the object must be inserted.
 */
public void insert(T object, int index) {
    synchronized (mLock) {
        if (mOriginalValues != null) {
            mOriginalValues.add(index, object);
        } else {
            mObjects.add(index, object);
        }
    }
    if (mNotifyOnChange) notifyDataSetChanged();
}

/**
 * Removes the specified object from the array.
 *
 * @param object The object to remove.
 */
public void remove(T object) {
    synchronized (mLock) {
        if (mOriginalValues != null) {
            mOriginalValues.remove(object);
        } else {
            mObjects.remove(object);
        }
    }
    if (mNotifyOnChange) notifyDataSetChanged();
}

/**
 * Remove all elements from the list.
 */
public void clear() {
    synchronized (mLock) {
        if (mOriginalValues != null) {
            mOriginalValues.clear();
        } else {
            mObjects.clear();
        }
    }
    if (mNotifyOnChange) notifyDataSetChanged();
}

/**
 * Sorts the content of this adapter using the specified comparator.
 *
 * @param comparator The comparator used to sort the objects contained
 *        in this adapter.
 */
public void sort(Comparator<? super T> comparator) {
    synchronized (mLock) {
        if (mOriginalValues != null) {
            Collections.sort(mOriginalValues, comparator);
        } else {
            Collections.sort(mObjects, comparator);
        }
    }
    if (mNotifyOnChange) notifyDataSetChanged();
}

/**
 * {@inheritDoc}
 */
@Override
public void notifyDataSetChanged() {
    super.notifyDataSetChanged();
    mNotifyOnChange = true;
}

/**
 * Control whether methods that change the list ({@link #add},
 * {@link #insert}, {@link #remove}, {@link #clear}) automatically call
 * {@link #notifyDataSetChanged}.  If set to false, caller must
 * manually call notifyDataSetChanged() to have the changes
 * reflected in the attached view.
 *
 * The default is true, and calling notifyDataSetChanged()
 * resets the flag to true.
 *
 * @param notifyOnChange if true, modifications to the list will
 *                       automatically call {@link
 *                       #notifyDataSetChanged}
 */
public void setNotifyOnChange(boolean notifyOnChange) {
    mNotifyOnChange = notifyOnChange;
}

/**
 * Returns the context associated with this array adapter. The context is used
 * to create views from the resource passed to the constructor.
 *
 * @return The Context associated with this adapter.
 */
public Context getContext() {
    return mContext;
}

/**
 * {@inheritDoc}
 */
public int getCount() {
    return mObjects.size();
}

/**
 * {@inheritDoc}
 */
public T getItem(int position) {
    return mObjects.get(position);
}

/**
 * Returns the position of the specified item in the array.
 *
 * @param item The item to retrieve the position of.
 *
 * @return The position of the specified item.
 */
public int getPosition(T item) {
    return mObjects.indexOf(item);
}

/**
 * {@inheritDoc}
 */
public long getItemId(int position) {
    return position;
}

/**
 * {@inheritDoc}
 */
public View getView(int position, View convertView, ViewGroup parent) {
    return createViewFromResource(mInflater, position, convertView, parent, mResource);
}

private View createViewFromResource(LayoutInflater inflater, int position, View convertView,
        ViewGroup parent, int resource) {
    View view;
    TextView text;

    if (convertView == null) {
        view = inflater.inflate(resource, parent, false);
    } else {
        view = convertView;
    }

    try {
        if (mFieldId == 0) {
            //  If no custom field is assigned, assume the whole resource is a TextView
            text = (TextView) view;
        } else {
            //  Otherwise, find the TextView field within the layout
            text = (TextView) view.findViewById(mFieldId);
        }
    } catch (ClassCastException e) {
        Log.e("CustomArrayAdapter", "You must supply a resource ID for a TextView");
        throw new IllegalStateException(
                "CustomArrayAdapter requires the resource ID to be a TextView", e);
    }

    T item = getItem(position);
    if (item instanceof CharSequence) {
        text.setText((CharSequence)item);
    } else {
        text.setText(item.toString());
    }

    return view;
}

/**
 * <p>Sets the layout resource to create the drop down views.</p>
 *
 * @param resource the layout resource defining the drop down views
 * @see #getDropDownView(int, android.view.View, android.view.ViewGroup)
 */
public void setDropDownViewResource(int resource) {
    this.mDropDownResource = resource;
}

/**
 * Sets the {@link Resources.Theme} against which drop-down views are
 * inflated.
 * <p>
 * By default, drop-down views are inflated against the theme of the
 * {@link Context} passed to the adapter's constructor.
 *
 * @param theme the theme against which to inflate drop-down views or
 *              {@code null} to use the theme from the adapter's context
 * @see #getDropDownView(int, View, ViewGroup)
 */
@Override
public void setDropDownViewTheme(Resources.Theme theme) {
    if (theme == null) {
        mDropDownInflater = null;
    } else if (theme == mInflater.getContext().getTheme()) {
        mDropDownInflater = mInflater;
    } else {
        final Context context = new ContextThemeWrapper(mContext, theme);
        mDropDownInflater = LayoutInflater.from(context);
    }
}

@Override
public Resources.Theme getDropDownViewTheme() {
    return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
}

@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
    final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
    return createViewFromResource(inflater, position, convertView, parent, mDropDownResource);
}

/**
 * Creates a new CustomArrayAdapter from external resources. The content of the array is
 * obtained through {@link android.content.res.Resources#getTextArray(int)}.
 *
 * @param context The application's environment.
 * @param textArrayResId The identifier of the array to use as the data source.
 * @param textViewResId The identifier of the layout used to create views.
 *
 * @return An CustomArrayAdapter<CharSequence>.
 */
public static CustomArrayAdapter<CharSequence> createFromResource(Context context,
                                                                   int textArrayResId,  int textViewResId) {
    CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
    return new CustomArrayAdapter<CharSequence>(context, textViewResId, strings);
}

/**
 * {@inheritDoc}
 */
public Filter getFilter() {
    if (mFilter == null) {
        mFilter = new ArrayFilter();
    }
    return mFilter;
}

/**
 * <p>An array filter constrains the content of the array adapter with
 * a prefix. Each item that does not start with the supplied prefix
 * is removed from the list.</p>
 */
private class ArrayFilter extends Filter {
    @Override
    protected FilterResults performFiltering(CharSequence prefix) {
        FilterResults results = new FilterResults();

        if (mOriginalValues == null) {
            synchronized (mLock) {
                mOriginalValues = new ArrayList<T>(mObjects);
            }
        }

        if (prefix == null || prefix.length() == 0) {
            ArrayList<T> list;
            synchronized (mLock) {
                list = new ArrayList<T>(mOriginalValues);
            }
            results.values = list;
            results.count = list.size();
        } else {
            String prefixString = prefix.toString().toLowerCase();

            ArrayList<T> values;
            synchronized (mLock) {
                values = new ArrayList<T>(mOriginalValues);
            }

            final int count = values.size();
            final ArrayList<T> newValues = new ArrayList<T>();

            for (int i = 0; i < count; i++) {
                final T value = values.get(i);
                final String valueText = value.toString().toLowerCase();

                // First match against the whole, non-splitted value
                if (valueText.startsWith(prefixString)) {
                    newValues.add(value);
                } else {
                    final String[] words = valueText.split(" ");
                    final int wordCount = words.length;

                    // Start at index 0, in case valueText starts with space(s)
                    for (int k = 0; k < wordCount; k++) {
                        if (words[k].startsWith(prefixString)) {
                            newValues.add(value);
                            break;
                        }
                    }
                }

                if (valueText.startsWith(prefixString) || valueText.contains(prefixString)) {
                    newValues.add(value);
                } else {
                    final String[] words = valueText.split(" ");
                    final int wordCount = words.length;
                    // Start at index 0, in case valueText starts with space(s)
                    for (int k = 0; k < wordCount; k++) {
                        if (words[k].startsWith(prefixString) || words[k].contains(prefixString)) {
                            newValues.add(value);
                            break;
                        }
                    }
                }
            }

            results.values = newValues;
            results.count = newValues.size();
        }

        return results;
    }

    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
        //noinspection unchecked
        mObjects = (List<T>) results.values;
        if (results.count > 0) {
            notifyDataSetChanged();
        } else {
            notifyDataSetInvalidated();
        }
    }
}
 }

2个文件的路径:

 app\src\main\java\com\paullemesnager\flashbac\CustomArrayAdapter.java
 app\src\main\java\com\paullemesnager\flashbac\SelectAssocActivity.java

2 个答案:

答案 0 :(得分:0)

javadoc是非常自我解释的

https://docs.oracle.com/javase/7/docs/api/java/lang/NoClassDefFoundError.html

  

如果Java虚拟机或ClassLoader实例尝试,则抛出该异常   加载类的定义(作为普通方法调用的一部分或   作为使用新表达式创建新实例的一部分)和否   可以找到类的定义。搜索过的课程   编译当前正在执行的类时存在定义,   但无法再找到定义。

通常,它意味着在运行时jar中包含缺少CustomArrayAdapter的类

答案 1 :(得分:0)

正如您在日志中看到的那样,错误发生在:

java.lang.NoClassDefFoundError: com.paullemesnager.flashbac.CustomArrayAdapter
com.paullemesnager.flashbac.SelectAssocActivity.onCreate(SelectAssocActivity.java:64)
...

所以,你必须打开SelectAssocActivity.java,找到第64行并检查你在那里写的是什么。

您似乎尝试创建一些CustomArrayAdapter类型的对象。但是,该类不存在(NoClassDefFoundError)。

我查看了您发布的链接。该班级的名称为ArrayAdapter而非CustomArrayAdapter

public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
    ...
}

因此,检查SelectAssocActivity.java,第64行,如果将对象类型从CustomArrayAdapter更改为ArrayAdapter。