2017-06-14 64 views
0

我有一个由静态ArrayList对象组成的自定义类。出于某种原因,我似乎无法弄清楚,当我调用的另一个 ArrayList对象上的clear()方法时,对象中的元素正在被覆盖。这就是我说的是:在itemsArray.clear()行自定义类变量被某些未知进程覆盖

通知,我将解释进一步向下跌破

public class AddItemsActivity extends AppCompatActivity{ 

// Global variables 
// For the Description 
private EditText descEditText; 

// For the Price 
private EditText priceEditText; 

// Temporary array to store the list of items which will be passed into the Diner 
public static ArrayList<Item> itemsArray = new ArrayList<>(); 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    // THIS HAS PROBLEM!!!! 
    itemsArray.clear(); 
    // Set the content to use the activity_add_items xml file 
    setContentView(R.layout.activity_add_items); 

    final ItemsListAdapter adapter = new ItemsListAdapter(this, itemsArray); 
    // Find the ListView to display the adapter on 
    ListView listView = (ListView) findViewById(R.id.items_list); 

    // Set the ListView with the adapter 
    listView.setAdapter(adapter); 

    Button addDetailsFragment = (Button) findViewById(R.id.add_item_button); 
    addDetailsFragment.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      // Setting up a new dialog 
      final Dialog dialog = new Dialog(AddItemsActivity.this); 
      dialog.setContentView(R.layout.item_add_dialog); 
      dialog.setCancelable(true); 
      dialog.setTitle(R.string.add_item_title); 
      dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 
      dialog.setCanceledOnTouchOutside(false); 

      Button num1 = (Button) dialog.findViewById(R.id.key_1); 
      Button num2 = (Button) dialog.findViewById(R.id.key_2); 
      Button num3 = (Button) dialog.findViewById(R.id.key_3); 
      Button num4 = (Button) dialog.findViewById(R.id.key_4); 
      Button num5 = (Button) dialog.findViewById(R.id.key_5); 
      Button num6 = (Button) dialog.findViewById(R.id.key_6); 
      Button num7 = (Button) dialog.findViewById(R.id.key_7); 
      Button num8 = (Button) dialog.findViewById(R.id.key_8); 
      Button num9 = (Button) dialog.findViewById(R.id.key_9); 
      Button num0 = (Button) dialog.findViewById(R.id.key_0); 
      Button numPeriod = (Button) dialog.findViewById(R.id.key_period); 
      Button numDel = (Button) dialog.findViewById(R.id.key_del); 
      Button numAdd = (Button) dialog.findViewById(R.id.key_add_item); 

      // Properties for description field 
      descEditText = (EditText) dialog.findViewById(R.id.details_desc_input); 

      // Properties for price field 
      priceEditText = (EditText) dialog.findViewById(R.id.details_price_input); 

      // Keypad OnClickListener to append or delete digits in the price input field 
      View.OnClickListener keyOnClickListener = new View.OnClickListener() { 
       @Override 
       // Program logic when one of the buttons is pressed 
       public void onClick(View v) { 
        priceEditText.requestFocus(); 
        CharSequence originalText = priceEditText.getText(); 
        Button button = (Button)v; 
        // Prevents app from crashing when trying to delete an empty field 
        if (button.getText() == getString(R.string.details_button_delete_text) 
          && originalText != null && originalText.length()>0) { 
         // Deletes one character/digit, deletes '$' if required 
         if (originalText.length() == 2 && originalText.charAt(0) == '$') { 
          priceEditText.setText(""); 
         } else { 
          priceEditText.setText(""); 
          priceEditText.append(originalText.subSequence(0, originalText.length() - 1)); 
         } 
         // Delete key does nothing instead of displaying 'Del' 
        } else if (button.getText() == getString(R.string.details_button_delete_text)) { 
         priceEditText.append(""); 
         // Prevents a second period from appearing in the field 
        } else if (button.getText().toString() 
          .equals(getString(R.string.details_button_period_text)) 
          && originalText.toString().contains(".")) { 
         priceEditText.append(""); 
         // Adds a $ sign 
        } else if (originalText == null || originalText.length() == 0) { 
         priceEditText.append("$"); 
         priceEditText.append(button.getText()); 
         // Else, input the digit pressed 
        } else { 
         priceEditText.append(button.getText()); 
        } 
       } 
      }; 

      // Add key OnClickListener to add items into Diner's editItemsArray 
      View.OnClickListener addOnClickListener = new View.OnClickListener() { 
       @Override 
       // Program logic when one of the buttons is pressed 
       public void onClick(View v) { 
        priceEditText.requestFocus(); 
        CharSequence originalText = priceEditText.getText(); 
        Button button = (Button)v; 
        // If the input field is empty, do nothing. Else, add the items 
        if (originalText == null && button.getText() == getString(R.string.details_button_add_text)) { 
         priceEditText.append(""); 
        // Adds the items into the editItemsArray ArrayList 
        } else { 
         Item itemToAdd = new Item(descEditText.getText().toString(), 
           Double.parseDouble(priceEditText.getText().toString().replace("$", 
             ""))); 
         itemsArray.add(itemToAdd); 
         adapter.notifyDataSetChanged(); 
         dialog.dismiss(); 
         Log.e("add item", "works"); 
        } 
        Log.e("line1", "works"); 
       } 
      }; 

      num1.setOnClickListener(keyOnClickListener); 
      num2.setOnClickListener(keyOnClickListener); 
      num3.setOnClickListener(keyOnClickListener); 
      num4.setOnClickListener(keyOnClickListener); 
      num5.setOnClickListener(keyOnClickListener); 
      num6.setOnClickListener(keyOnClickListener); 
      num7.setOnClickListener(keyOnClickListener); 
      num8.setOnClickListener(keyOnClickListener); 
      num9.setOnClickListener(keyOnClickListener); 
      num0.setOnClickListener(keyOnClickListener); 
      numPeriod.setOnClickListener(keyOnClickListener); 
      numDel.setOnClickListener(keyOnClickListener); 
      numAdd.setOnClickListener(addOnClickListener); 

     /* 
     Request focus for the price input field such that focus is on that field when 
     the dialog opens 
     */ 
      priceEditText.requestFocus(); 
     /* 
     Hide the appearance of any keyboard when the user presses on the price input field 
     so the user only uses the provided in app customised keypad 
     */ 

      // Hide default keyboard when focus is on this field 
      priceEditText.setOnClickListener(new View.OnClickListener() { 
       @Override 
       public void onClick(View v) { 
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 
        imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); 
        getWindow().setSoftInputMode(WindowManager 
          .LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); 
        Log.e("testonclickclose", "works"); 
       } 
      }); 

      priceEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { 
       @Override 
       public void onFocusChange(View v, boolean hasFocus) { 
        if (hasFocus) { 
         InputMethodManager im = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 
         im.hideSoftInputFromWindow(v.getWindowToken(), 0); 
        } 
       } 
      }); 

      priceEditText.setOnTouchListener(new View.OnTouchListener() { 
       @Override 
       public boolean onTouch(View v, MotionEvent event) { 
        return false; 
       } 
      }); 


      dialog.show(); 

      // Setting the size of the dialog 
      Window window = dialog.getWindow(); 
      window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, 
        WindowManager.LayoutParams.WRAP_CONTENT); 
     } 
    }); 

    Button addNextDinerActivity = (Button) findViewById(R.id.item_done_button); 
    addNextDinerActivity.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View view) { 
      // Assign the list of items to specified Diner (selected previously or 
      // currently adding) 
      // Get name of current diner 
      String nameOfCurrentDiner = Diner.getCurrentName(); 

      // Iteration process to find the index of current Diner 
      int indexOfCurrentDiner = -1; 
      for (Diner list : AddDinerActivity.dinerArray) { 
       if (list.getmDinerName().equals(nameOfCurrentDiner)) { 
        // Stores in index in a variable 
        indexOfCurrentDiner = AddDinerActivity.dinerArray.indexOf(list); 
       } 
      } 
      // Instantiate a Diner object to be added later 
      ArrayList<Item> array = getItemsArray(); 
      Diner dinerToAdd = new Diner(nameOfCurrentDiner, getCurrentBill(getItemsArray()), array); 
      // Sets a new Diner object to the index position found 
      AddDinerActivity.dinerArray.remove(indexOfCurrentDiner); 
      AddDinerActivity.dinerArray.add(indexOfCurrentDiner, dinerToAdd); 
      // Clears the editItemsArray when leaving activity 

      // Brings the user back to Diner list page 
      Intent addDiner = new Intent(AddItemsActivity.this, AddDinerActivity.class); 
      startActivity(addDiner); 
     } 
    }); 
// Method to calculate the current individual's bill 
public double getCurrentBill(ArrayList<Item> list) { 
    double currentBill = 0; 
    for (Item item : list) { 
     currentBill += item.getmItemPrice(); 
    } 
    return currentBill; 
} 

// Method to get current item array 
public ArrayList<Item> getItemsArray() { 
    return itemsArray; 
} 
} 

我的晚餐类定义为低于由一个名为mDinerItemsList

public class Diner { 

public static String currentName; 

// To store the currentName 
private String mDinerName; 

// To store individual's total bill 
private double mDinerBill; 

// To store the individual's list of items (its description and price) 
private ArrayList<Item> mDinerItemsList = new ArrayList<>(); 

// Class constructor 
public Diner(String mDinerName, double mDinerBill, ArrayList<Item> DinerItemsList) { 
    this.mDinerName = mDinerName; 
    this.mDinerBill = mDinerBill; 
    mDinerItemsList = DinerItemsList; 
} 

// Class constructor with Name ONLY 
public Diner(String mDinerName) { 
    this.mDinerName = mDinerName; 
} 

// Class constructor with Bill ONLY 
public Diner(double mDinerBill) { 
    this.mDinerBill = mDinerBill; 
} 

// Class constructor with ItemsList ONLY 
public Diner(ArrayList<Item> mDinerItemsList) { 
    this.mDinerItemsList = mDinerItemsList; 
} 

// Method to set the current name to identify current diner 
public static void setCurrentName(String name) { 
    currentName = name; 
} 

// Method to return the current name to identify current diner 
public static String getCurrentName() { 
    return currentName; 
} 

// Method to return Diner's Name 
public String getmDinerName() { 
    return mDinerName; 
} 

// Method to return Diner's bill 
public double getmDinerBill() { 
    return mDinerBill; 
} 

public void setmDinerName(String mDinerName) { 
    this.mDinerName = mDinerName; 
} 

// Method to put an item (description and price) into Diner's list of items 
public void putmDinerItem(ArrayList<Item> array) { 
    mDinerItemsList = array; 
} 

// Method to return Diner's list of items in the form of ArrayList 
public ArrayList<Item> getmDinerItemsList() { 
    return mDinerItemsList; 
} 
} 

截至记者的ArrayList对象活动中的按钮,包含descEditText和priceEditText字符串的单个对象将被添加到itemsArray中。然后这个数组在我实例化一个新的Diner对象的时期被传递给mDinerItemsList。这些恰好在AddItemsActivity结束之前发生。通过调试过程,我发现第二次调用AddItemsActivity时,itemsArray.clear()以某种方式擦除了先前实例化的Diner对象的mDinerItemsList。每当调用活动时都会发生这种情况。因此,我无法在mDinerItemsList中为我想创建的每个Diner对象存储任何ArrayList。我哪里做错了??我一直试图弄清楚这几个小时!请帮忙!!!

+0

问题是你只是传递你的列表而不是创建副本。所以实际上只有一个List对象,但有许多变量指向该列表。 –

+0

你介意进一步阐述吗?当我使用itemsArray的元素分配mDinerItemsList时,不应将该变量“绑定”到Diner对象。因此,itemsArray的任何更改都不应该影响我的Diner类变量吗? –

+0

不知道你的意思是“绑定”。您没有使用新的关键字创建新的ArrayList。因此,不会创建新列表,并且只需将您现有的列表分配给Diner中的mDinerItemsList字段即可。 –

回答

1

问题是,你只是传递你的列表而不是创建副本。所以实际上只有一个List对象,但有许多变量指向该列表。

例如:在您的Diner构造函数中,您只需将mDinerItemsList分配给传递的DinerItemsList即可。

// Class constructor 
public Diner(String mDinerName, double mDinerBill, ArrayList<Item> DinerItemsList) { 
    this.mDinerName = mDinerName; 
    this.mDinerBill = mDinerBill; 
    mDinerItemsList = DinerItemsList; 
} 

所以,当你做

ArrayList<Item> array = getItemsArray(); 
Diner dinerToAdd = new Diner(nameOfCurrentDiner, getCurrentBill(getItemsArray()), array); 

你只是路过的静态列表getItemArray()返回到晚餐和晚餐的新目标将指向完全相同的清单。

要解决这个问题,你可以使用ArrayList的拷贝构造函数,这将创造一个浅拷贝(一个单独的清单指向同一个对象作为原始列表):

mDinerItemsList = new ArrayList<>(DinerItemsList); 

编辑:澄清一些非常简单的示例代码:

List<String> list1 = new ArrayList<String>(); 
List<String> list2 = list1; 

在上面的例子中,您将只有1个列表,但有2个变量指向它。调用list2.clear();也会清除list1(因为两个变量指向的只有1个List)。你的代码做同样的事情。它只是传递一个Object引用,但不会创建一个新引用。

+0

是的!你的建议确实解决了这个问题,你是天赐之物,谢谢!关于你说没有创建新列表的想法,还有一个问题。我理解你的解释,但我很困惑,为什么这行代码: private ArrayList mDinerItemsList = new ArrayList <>(); 不会创建一个新的List对象吗?这是否意味着我可以擦除等号后面的部分? –

+1

@ L.KT'private ArrayList mDinerItemsList = new ArrayList <>();'当对象被创建时将会创建一个新的空ArrayList,但是之后通过做'mDinerItemsList = DinerItemsList;'你改变你的列表类变量mDinerItemsList指向您在构造函数中传递的列表。由于没有变量指向空数组列表,所以很快就会被垃圾收集器处理。理论上你可以将字段声明简化为'private ArrayList mDinerItemsList;' –

+0

你的解释帮助我今天学到了一些东西。谢谢! –

1

试试这个在您的晚餐构造:

list = new ArrayList<>(passedList); 

这样,你是不是在你的晚餐对象变量设置为在其他类中的静态引用。相反,您只需在Diner对象中创建一个新列表,然后填充静态列表的内容即可。

1
public Diner(String mDinerName, double mDinerBill, ArrayList<Item> DinerItemsList) { 
    this.mDinerName = mDinerName; 
    this.mDinerBill = mDinerBill; 
    mDinerItemsList = new ArrayList<DinerItemsList>; 
}