0

我有一个具有不同类型视图的 ListView:Spinner、EditText 和 Button 到 DatePicker。

为了区分不同的元素,我在我的 BaseAdapter 中添加了一个标签到这个 ListView。

要获取字段的文本,我使用方法 findViewWithTag(Object tag)。

我已经在索尼平板电脑(Android 4.0.3)中测试了该应用程序,该方法返回文本没有任何问题。

当我在我的 Acer Liquid Duo z110(Android 2.3.3) 中进行测试时,问题就开始了。我做类似的事情:

Spinner spinner = (Spinner) listView.findViewWithTag("Item" + i);
// get value for the type
value = spinner.getSelectedItem().toString(); //Value is a string

我在行中得到 NullPointerException value=/*...*/

这是我的 BaseAdapter 和对话框,我在其中调用 ListView 的元素。

块引用

public class AttributeListAdapter extends BaseAdapter implements Serializable {
private static final long serialVersionUID = 1L;

private FeatureSet featureSet;

Field[] fields;

FeatureType[] types;

String typeIdFieldName;

private Context context;

LayoutInflater lInflator;

int[] editableFieldIndexes;

String[] typeNames;

HashMap<String, FeatureType> typeMap;

AttributeItem[] items;

private DateFormat formatter = DateFormat.getDateTimeInstance(
        DateFormat.SHORT, DateFormat.SHORT);

/**
 * Constructor
 */
public AttributeListAdapter(Context context, Field[] fields,
        FeatureType[] types, String typeIdFieldName) {

    this.setContext(context);
    this.lInflator = LayoutInflater.from(context);
    this.fields = fields;
    this.types = types;
    this.typeIdFieldName = typeIdFieldName;

    // this.fieldsTemplateMap = createInitialFieldTemplateMap(this.fields);
    // parseTypes();

    // Setup processed variables
    this.editableFieldIndexes = FeatureLayerUtils
            .createArrayOfFieldIndexes(this.fields);
    this.typeNames = FeatureLayerUtils.createTypeNameArray(this.types);
    this.typeMap = FeatureLayerUtils.createTypeMapByValue(this.types);

    // register dataset observer to track when the underlying data is
    // changed
    this.registerDataSetObserver(new DataSetObserver() {

        public void onChanged() {

            // clear the array of attribute items
            AttributeListAdapter.this.items = new AttributeItem[AttributeListAdapter.this.editableFieldIndexes.length];

        }
    });
}

public Context getContext() {
    return context;
}

public void setContext(Context context) {
    this.context = context;
}

/**
 * Implemented method from BaseAdapter class
 */
public int getCount() {

    return this.editableFieldIndexes.length;

}

/**
 * Implemented method from BaseAdapter class. This method returns the actual
 * data associated with a row in the list. In this case we return the field
 * along with the field value as a custom object. We subsequently add the
 * View which displays the value to this object so we can retrieve it when
 * applying edits.
 */
public Object getItem(int position) {

    // get field associated with the position from the editableFieldIndexes
    // array created at startup
    int fieldIndex = this.editableFieldIndexes[position];

    AttributeItem row = null;

    // check to see if we have already created an attribute item if not
    // create
    // one
    if (items[position] == null) {

        // create new Attribute item for persisting the data for subsequent
        // events
        row = new AttributeItem();
        row.setField(this.fields[fieldIndex]);
        Object value = this.getFeatureSet().getGraphics()[0]
                .getAttributeValue(fields[fieldIndex].getName());
        row.setValue(value);
        items[position] = row;

    } else {

        // reuse existing item to ensure View instance is kept.
        row = items[position];

    }

    return row;

}

/**
 * Implemented method from BaseAdapter class
 */
public long getItemId(int position) {

    return position;

}

/**
 * Implemented method from BaseAdapter class. This is the main method for
 * returning a View which corresponds to a row in the list. This calls the
 * getItem() method to get the data. It is called multiple times by the
 * ListView and may be improved on by saving the previous result.
 */
public View getView(int position, View convertView, ViewGroup parent) {

    View container = null;

    AttributeItem item = (AttributeItem) getItem(position);

    // check field type
    // TODO if you want to support domains, add checks here and use the
    // createSpinnerViewFromArray to create spinners
    if (item.getField().getName().equals(this.typeIdFieldName)) {
        // This is the featurelayers type field

        container = lInflator.inflate(R.layout.item_spinner, null);
        // get the types name for this feature from the available values
        String typeStringValue = (item.getValue() != null) ? this.typeMap
                .get(item.getValue().toString()).getName() : typeNames[2];
        createSpinnerViewFromArray(position, container, item.getField(),
                typeStringValue, this.typeNames);

        // TODO set listener to change types associated domain fields if
        // required

    } else if (item.getField().getName().equals("etat_cana")) {
        container = lInflator.inflate(R.layout.item_spinner, null);
        String[] etatCana = { "", "Oxydée", "Désagrégée", "Autre",
                "Pas d'observation" };
        String typeStringValue = (item.getValue() != null) ? item
                .getValue().toString() : etatCana[0];

        createSpinnerViewFromArray(position, container, item.getField(),
                typeStringValue, etatCana);

    } else if (item.getField().getName().equals("alerte")) {
        container = lInflator.inflate(R.layout.item_spinner, null);
        String[] alerte = { "", "Oui", "Non" };
        String typeStringValue = (item.getValue() != null) ? item
                .getValue().toString() : alerte[0];

        createSpinnerViewFromArray(position, container, item.getField(),
                typeStringValue, alerte);

    } else if (FieldType.determineFieldType(item.getField()) == FieldType.DATE) {
        // create date picker for date fields

        container = lInflator.inflate(R.layout.item_date, null);
        long date = Long.parseLong((item.getValue() != null) ? item
                .getValue().toString() : ""
                + Calendar.getInstance().getTimeInMillis());

        createDateButtonFromLongValue(position, container, item.getField(),
                date);

    } else {
        // create number and text fields
        // View object for saving in the AttrbuteItem once it has been set
        // up, for
        // accessing later when we apply edits.

        if (FieldType.determineFieldType(item.getField()) == FieldType.STRING) {

            // get the string specific layout
            container = lInflator.inflate(R.layout.item_text, null);
            createAttributeRow(position, container, item.getField(),
                    item.getValue());

        } else if (FieldType.determineFieldType(item.getField()) == FieldType.NUMBER) {

            // get the number specific layout
            container = lInflator.inflate(R.layout.item_number, null);
            createAttributeRow(position, container, item.getField(),
                    item.getValue());

        } else if (FieldType.determineFieldType(item.getField()) == FieldType.DECIMAL) {

            // get the decimal specific layout
            container = lInflator.inflate(R.layout.item_decimal, null);
            createAttributeRow(position, container, item.getField(),
                    item.getValue());

        }
    }
    container.setOnClickListener(new View.OnClickListener() {

        public void onClick(View v) {
            Log.i("AttributeListAdapter", "container clicked");

        }
    });

    return container;

}

/**
 * Sets the FeatureSet, called by the activity when a new queryResult is
 * returned
 * 
 * @param featureSet
 */
public void setFeatureSet(FeatureSet featureSet) {

    this.featureSet = featureSet;

}

/**
 * Helper method to create a spinner for a field and insert it into the View
 * container. This uses, the String[] to create the list, and selects the
 * value that is passed in from the list (the features value). Can be used
 * for domains as well as types.
 * 
 * @param position
 */
Spinner createSpinnerViewFromArray(int position, View container,
        Field field, Object value, String[] values) {

    TextView fieldAlias = (TextView) container
            .findViewById(R.id.field_alias_txt);
    Spinner spinner = (Spinner) container
            .findViewById(R.id.field_value_spinner);

    spinner.setTag("Item" + position);

    fieldAlias.setText(field.getAlias());
    spinner.setPrompt(field.getAlias());

    ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(
            this.getContext(), android.R.layout.simple_spinner_item, values);
    spinnerAdapter
            .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(spinnerAdapter);
    // set current selection based on the value passed in
    spinner.setSelection(spinnerAdapter.getPosition(value.toString()));

    return spinner;
}

/**
 * Helper method to create a date button, with appropriate onClick and
 * onDateSet listeners to handle dates as a long (milliseconds since 1970),
 * it uses the locale and presents a button with the date and time in short
 * format.
 * 
 * @param position
 */
Button createDateButtonFromLongValue(int position, View container,
        Field field, long date) {

    TextView fieldAlias = (TextView) container
            .findViewById(R.id.field_alias_txt);
    Button dateButton = (Button) container
            .findViewById(R.id.field_date_btn);
    dateButton.setTag("Item" + position);
    fieldAlias.setText(field.getAlias());

    Calendar c = Calendar.getInstance();
    c.setTimeInMillis(date);
    dateButton.setText(getFormatter().format(c.getTime()));

    addListenersToDatebutton(dateButton);

    return dateButton;
}

/**
 * Helper method to add the field alias and the fields value into columns of
 * a view using standard id names. If the field has a length set, then this
 * is used to constrain the EditText's allowable characters. No validation
 * is applied here, it is assumed that the container has this set already
 * (in XML).
 * 
 * @param position
 */
View createAttributeRow(int position, View container, Field field,
        Object value) {

    TextView fieldAlias = (TextView) container
            .findViewById(R.id.field_alias_txt);
    EditText fieldValue = (EditText) container
            .findViewById(R.id.field_value_txt);
    fieldValue.setTag("Item" + position);
    fieldAlias.setText(field.getAlias());

    // set the length of the text field and its value
    if (field.getLength() > 0) {
        InputFilter.LengthFilter filter = new InputFilter.LengthFilter(
                field.getLength());
        fieldValue.setFilters(new InputFilter[] { filter });
    }

    if (value != null) {
        fieldValue.setText(value.toString(), BufferType.EDITABLE);
    } else {
        fieldValue.setText("", BufferType.EDITABLE);
    }

    return fieldValue;
}

/**
 * Helper method to create the date button and its associated events
 */
void addListenersToDatebutton(Button dateButton) {

    // create new onDateSetLisetener with the button associated with it
    final ListOnDateSetListener listener = new ListOnDateSetListener(
            dateButton);

    // add a click listener to the button
    dateButton.setOnClickListener(new View.OnClickListener() {

        public void onClick(View v) {

            // if its a date, get the milliseconds value
            Calendar c = Calendar.getInstance();
            getFormatter().setCalendar(c);

            try {

                // parse to a double
                Button button = (Button) v;
                c.setTime(getFormatter().parse(button.getText().toString()));

            } catch (ParseException e) {
                // do nothing as should parse
            }

            int year = c.get(Calendar.YEAR);
            int month = c.get(Calendar.MONTH);
            int day = c.get(Calendar.DAY_OF_MONTH);

            // show date picker with date set to the items value (hence
            // built
            // outside of onCreateDialog)
            // TODO implement time picker if required, this picker only
            // supports
            // date and therefore showing the dialog will cause a change in
            // the time
            // value for the field
            DatePickerDialog dialog = new DatePickerDialog(getContext(),
                    listener, year, month, day);
            dialog.show();
        }
    });
}

对话

public class FeatureEditor extends Dialog {

private static String TAG = "FeatureEditor";

private static Context context;

/**
 * Details of the feature layer "Evenements"
 */
private AttributeListAdapter listAdapter;
/**
 * List layout of the edit feature dialog
 */
private View listLayout;
/**
 * Layout of the edit feature dialog
 */
private ListView listView;

private ArcGISFeatureLayer editLayer;

private Activity aep;

private ArcGISDynamicMapServiceLayer aep41MapServer;

/**
 * Constructor with the required parameters
 * @param aep Activity where the dialog is shown
 * @param edit Feature Layer where the edits are made
 * @param aep41MapServer Dynamic Layer where the edits will be seen
 * @param listAdapter The adapter with the attributes to edit
 */
public FeatureEditor(Activity aep, ArcGISFeatureLayer edit,
        ArcGISDynamicMapServiceLayer aep41MapServer,
        AttributeListAdapter listAdapter) {
    super(aep);
    this.aep = aep;
    this.editLayer = edit;
    this.aep41MapServer = aep41MapServer;
    context = aep.getApplicationContext();
    this.listAdapter = listAdapter;
    listLayout = LayoutInflater.from(context).inflate(R.layout.list_layout,
            null);
    listView = (ListView) listLayout.findViewById(R.id.list_view);

    listView.setAdapter(listAdapter);
    setContentView(listLayout);
    setTitle("Editeur d'Evenements");


    Button apply = (Button) listLayout
            .findViewById(R.id.EditFeatureApplyBt);
    apply.setOnClickListener(applyEditButton());

    Button cancel = (Button) listLayout
            .findViewById(R.id.EditFeatureCancelBt);
    cancel.setOnClickListener(cancelEditButton());
}

/**
 * Action performed when the cancel button in the Edit Dialog is pressed
 * 
 * @return OnClickListener
 */
private View.OnClickListener cancelEditButton() {
    // TODO Auto-generated method stub
    return new View.OnClickListener() {

        public void onClick(View v) {
            dismiss();
        }
    };
}

/**
 * Action performed when the apply button in the Edit Dialog is pressed
 * 
 * @return OnClickListener
 */
private View.OnClickListener applyEditButton() {
    return new View.OnClickListener() {
        public void onClick(View v) {
            boolean isTypeField = false;
            boolean hasEdits = false;
            Map<String, Object> attrs = new HashMap<String, Object>();

            // loop through each attribute and set the new values if they
            // have
            // changed
            for (int i = 0; i < listAdapter.getCount(); i++) {
                AttributeItem item = (AttributeItem) listAdapter.getItem(i);

                String value = "";

                if (item.getField().getName()
                        .equals(editLayer.getTypeIdField())) {
                    // drop down spinner

                    Spinner spinner = (Spinner) listView
                            .findViewWithTag("Item" + i);
                    // get value for the type
                    String typeName = spinner.getSelectedItem().toString();
                    value = FeatureLayerUtils.returnTypeIdFromTypeName(
                            editLayer.getTypes(), typeName);

                    // update map layer as for this featurelayer the
                    // type change will
                    // change the features symbol.
                    isTypeField = true;

                } else if (item.getField().getName().equals("etat_cana")) {
                    // DropDown to Pipeline state

                    Spinner spinner = (Spinner) listView
                            .findViewWithTag("Item" + i);
                    // get value for the type
                    value = spinner.getSelectedItem().toString();
                } else if (item.getField().getName().equals("alerte")) {
                    // DropDown to Pipeline state

                    Spinner spinner = (Spinner) listView
                            .findViewWithTag("Item" + i);
                    // get value for the type
                    value = spinner.getSelectedItem().toString();
                } else if (FieldType.determineFieldType(item.getField()) == FieldType.DATE) {
                    // date

                    Button dateButton = (Button) listView
                            .findViewWithTag("Item" + i);
                    value = dateButton.getText().toString();

                } else {
                    // edit text

                    EditText editText = (EditText) listView
                            .findViewWithTag("Item" + i);
                    value = editText.getText().toString();

                }

                // try to set the attribute value on the graphic and see
                // if it has
                // been changed
                boolean hasChanged = FeatureLayerUtils.setAttribute(attrs,
                        listAdapter.getFeatureSet().getGraphics()[0],
                        item.getField(), value, listAdapter.getFormatter());

                // if a value has for this field, log this and set the
                // hasEdits
                // boolean to true
                if (hasChanged) {

                    Log.d(TAG, "Change found for field="
                            + item.getField().getName() + " value = "
                            + value + " applyEdits() will be called");
                    hasEdits = true;
                }

                // check if this was a type field, if so set boolean
                // back to false
                // for next field
                if (isTypeField) {

                    isTypeField = false;
                }
            }

            // check there have been some edits before applying the changes
            if (hasEdits) {

                // set objectID field value from graphic held in the
                // featureset
                attrs.put(editLayer.getObjectIdField(), listAdapter
                        .getFeatureSet().getGraphics()[0]
                        .getAttributeValue(editLayer.getObjectIdField()));
                final Graphic newGraphic = new Graphic(null, null, attrs,
                        null);

                editLayer.applyEdits(null, null,
                        new Graphic[] { newGraphic },
                        new CallbackListener<FeatureEditResult[][]>() {
                            public void onError(Throwable arg0) {
                            }

                            public void onCallback(
                                    final FeatureEditResult[][] arg0) {
                                aep.runOnUiThread(new Runnable() {

                                    public void run() {
                                        aep41MapServer.refresh();

                                        if (arg0[2] != null
                                                && arg0[2][0] != null
                                                && arg0[2][0].isSuccess()) {
                                            Log.i(TAG, "Feature edited");
                                            Log.i(TAG,
                                                    arg0[2][0].toString());
                                            aep41MapServer.refresh();
                                            Toast.makeText(context, "Edition d'événement réussi", Toast.LENGTH_SHORT).show();
                                        } else {
                                            Log.e(TAG,
                                                    "Error editing feature");
                                            Toast.makeText(context, "Edition d'evenement échoe", Toast.LENGTH_SHORT).show();
                                        }
                                    }
                                });

                            }
                        });
            }
            editLayer.clearSelection();
            dismiss();
        }
    };
}
}

带有 BaseAdapter 的 ListView 在不同版本的 Android 中的工作方式是否相同?难道我做错了什么?

正如我之前所说,该应用程序在 Android 4.0.3 中没有任何错误,但在 Android 2.3.3 中没有。

谢谢您的帮助 ;)

收到的 logCat 是:

E/CellLocation( 4779): create GsmCellLocation
E/TelephonyManager( 4779): getDefaultSim is sim1
E/CellLocation( 4779): create GsmCellLocation
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/CellLocation(  166): create GsmCellLocation
E/CellLocation(  166): create GsmCellLocation
E/CellLocation(  166): create GsmCellLocation
E/CellLocation( 4823): create GsmCellLocation
E/TelephonyManager( 4823): getDefaultSim is sim1
E/CellLocation( 4823): create GsmCellLocation
E/TelephonyManager( 4823): getDefaultSim is sim1
E/TelephonyManager( 4823): getDefaultSim is sim1
E/TelephonyManager( 4823): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4823): getDefaultSim is sim1
E/TelephonyManager( 4823): getDefaultSim is sim1
E/TelephonyManager( 4823): getDefaultSim is sim1
E/Resources( 6063): sPreloadedDrawables size is 0, reload preloaded resources.
E/dalvikvm( 6063): [DVM] mmap return base = 47c58000
E/dalvikvm( 6063): [DVM] mmap return base = 48837000
E/dalvikvm( 6063): [DVM] mmap return base = 48a3f000
E/TelephonyManager( 4779): getDefaultSim is sim1
E/CellLocation( 4779): create GsmCellLocation
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/dalvikvm( 6063): [DVM] mmap return base = 48733000
E/TelephonyManager( 4779): getDefaultSim is sim1
E/CellLocation( 4779): create GsmCellLocation
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/wpa_supplicant(  236): Ongoing Scan action...
E/TelephonyManager( 4779): getDefaultSim is sim1
E/CellLocation( 4779): create GsmCellLocation
E/dalvikvm( 4779): [DVM] mmap return base = 476eb000
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/TelephonyManager( 4779): getDefaultSim is sim1
E/dalvikvm( 6063): [DVM] mmap return base = 48e49000
E/TelephonyManager( 4779): getDefaultSim is sim1
E/dalvikvm( 6063): [DVM] mmap return base = 4915a000
E/dalvikvm( 6063): [DVM] mmap return base = 4925a000
E/dalvikvm( 6063): [DVM] mmap return base = 49d0a000
E/dalvikvm( 4009): [DVM] mmap return base = 462ab000
E/dalvikvm( 6063): [DVM] mmap return base = 49d0a000
--------- beginning of /dev/log/system
E/AndroidRuntime( 6063): FATAL EXCEPTION: main
E/AndroidRuntime( 6063): java.lang.NullPointerException
E/AndroidRuntime( 6063): at com.AEP41.main.CustomDialogs.FeatureEditor$2.onClick(FeatureEditor.java:158)
E/AndroidRuntime( 6063): at android.view.View.performClick(View.java:2538)
E/AndroidRuntime( 6063): at android.view.View$PerformClick.run(View.java:9135)
E/AndroidRuntime( 6063): at android.os.Handler.handleCallback(Handler.java:618)
E/AndroidRuntime( 6063): at android.os.Handler.dispatchMessage(Handler.java:123)
E/AndroidRuntime( 6063): at android.os.Looper.loop(SourceFile:351)
E/AndroidRuntime( 6063): at android.app.ActivityThread.main(ActivityThread.java:3826)
E/AndroidRuntime( 6063): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 6063): at java.lang.reflect.Method.invoke(Method.java:538)
E/AndroidRuntime( 6063): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:969)
E/AndroidRuntime( 6063): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:727)
E/AndroidRuntime( 6063): at dalvik.system.NativeStart.main(Native Method)
E/dalvikvm(  166): [DVM] mmap return base = 4c13a000
E/AEE/LIBAEE(  166): read_cmdline:com.AEP41.main
E/AEE/AED (   80): jni/../aed/aed_trace.c, 158, No such file or directory
E/dalvikvm( 5446): [DVM] mmap return base = 45a54000
4

0 回答 0