首页 > 解决方案 > 在保存之前初始化空对象并在活动之间传递

问题描述

我正在尝试做一些对我来说非常复杂的事情....我正在尝试使用两个对象创建发票 1) Invoice 2) InvoiceItems (Line Items)

相同的活动用于编辑现有发票或创建新发票,因此 Invoice 和 InvoiceItems 都可以为空。

我试图完成这项工作的方式是在活动启动时,

  1. 用户在发票活动的表单中输入详细信息
  2. 用户单击添加项目详细信息(输入标题等数据的发票,并且在单击时将 invoiceItems 发送到编辑 InvoiceItems 活动)
  3. 启动新活动以添加单个项目详细信息
  4. 进行了更改并更新了发票项目
  5. 按下返回时,对象将再次发送回上一个活动

问题有两个方面:

  1. 这是正确的方法吗?
  2. 在发送到 InvoiceItem 活动之前将字段详细信息添加到发票对象时出现空点错误异常

请看下面的代码:

class EditInvoice : AppCompatActivity() {

companion object {
    @JvmStatic
    fun start(context: Context, invoice: Invoice?, invoiceItems: InvoiceItems?) {
        val starter = Intent(context, EditInvoice::class.java)
            .putExtra("invoice", invoice)
            .putExtra("invoiceItems", invoiceItems)
        context.startActivity(starter)
    }
}

private var invoice: Invoice? = null
private var invoiceEdit: Invoice? = null
private lateinit var contact: Contact
private var invoiceItems: List<InvoiceItems>? = null
private lateinit var dueDate: Calendar
private val calendar = Calendar.getInstance()
private var total = 0
private var subTotal = 0
private var taxrate = 0
private var invoiceType: String = ""
private var invoiceUpdt: InvoiceItems? = null
private var j: Int = 0
private var clientLkey: String? = ""

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_edit_invoice)

    val toolbar: Toolbar = findViewById(R.id.toolbar_editinv)
    setSupportActionBar(toolbar)
    supportActionBar?.setDisplayShowHomeEnabled(true)

    val invoiceClient = findViewById<AutoCompleteTextView>(R.id.invoiceClient)
    val invoiceDueDt = findViewById<TextView>(R.id.invoiceDueDt)
    dueDate = Calendar.getInstance()

    //getting values from intent
    invoice = intent.getSerializableExtra("invoice") as? Invoice
    invoiceItems = intent.getSerializableExtra("invoiceItem") as? List<InvoiceItems>
    invoiceUpdt = intent.getSerializableExtra("invoiceItemUpdt") as? InvoiceItems
    j = intent.getIntExtra("i",0)

    if (invoice == null){
        invoiceType = "new"
        edit_inv.text = "Add Invoice"
        invoiceEdit = Invoice("0","0","0","0","0","0",0,0,null,"")
        addinvoiceItem()
    } else {
        invoiceEdit = invoice
        editInvoice()
    }

    //Setup Due date for the invoice
    invoiceDueDt.setOnClickListener {
        showCalendar()
    }

    //Auto complete based on database for selecting the client
    val clientContact: List<Contact> = ArrayList<Contact>()
    val adapter = ClientSelectAdapter(this, R.layout.userlatomcontacts, clientContact)
    invoiceClient.setAdapter(adapter)
    invoiceClient.threshold = 2

    invoiceClient.setOnItemClickListener { parent, _, position, id ->
        val selectedClient = parent.adapter.getItem(position) as Contact?
        invoiceClient.setText(selectedClient?.name)
        clientLkey = selectedClient?.lookupKey
    }

    val saveInvoice = findViewById<TextView>(R.id.editinv_save)
    val invoiceTitle = findViewById<EditText>(R.id.invoiceTitle)

    saveInvoice.setOnClickListener {

        if(invoiceTitle.toString().isEmpty()){
            Toast.makeText(this, "Invoice title can't be empty", Toast.LENGTH_SHORT).show()
            return@setOnClickListener
        }
        if(invoiceClient.toString().isEmpty()){
            Toast.makeText(this, "Please select a Client for the invoice", Toast.LENGTH_SHORT).show()
            return@setOnClickListener
        }
        if(invoiceDueDt.toString().isEmpty()){
            Toast.makeText(
                this,
                "Please enter the due date for the invoice",
                Toast.LENGTH_SHORT
            ).show()
            return@setOnClickListener
        }
        if(invoiceItems == null){
            Toast.makeText(
                this,
                "Please enter the line items/services for the invoice",
                Toast.LENGTH_SHORT
            ).show()
            return@setOnClickListener
        }

        //updating values as current
        updateInvoiceValues()

        //Storing values to DB
        val db = AppDatabase.getDatabase(this)
        if(invoiceType == "new"){
            db.InvoicesDao().addInvoice(invoice!!)
            for(i in invoiceItems!!.indices){
                db.InvoiceItemsDao().addInvItem(invoiceItems!![i])
            }
        } else {
            db.InvoicesDao().updateInvoice(invoice!!)
            for(i in invoiceItems!!.indices){
                db.InvoiceItemsDao().updateInvItem(invoiceItems!![i])
            }
        }
    }

}

inner class ClientSelectAdapter(
    context: Context,
    @LayoutRes private val layoutResource: Int,
    private var allContacts: List<Contact>
):
    ArrayAdapter<Contact>(context, layoutResource, allContacts),
    Filterable {private var mContact: List<Contact> = allContacts

    override fun getCount(): Int {
        return mContact.size
    }

    override fun getItem(p0: Int): Contact {
        return mContact[p0]

    }
    override fun getItemId(p0: Int): Long {
        // Or just return p0
        return mContact[p0].id.toLong()
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {

        var view = convertView
        if (view == null) {
            view = LayoutInflater.from(parent.context)
                .inflate(layoutResource, parent, false)
        }
        val invoiceClient = view!!.findViewById<View>(R.id.invClientName) as TextView
        invoiceClient.text = mContact[position].name
        val clientProfile = view.findViewById<View>(R.id.profile_image) as ShapeableImageView

        Picasso.get().load(mContact[position].photoUri)
            .placeholder(R.drawable.ic_baseline_whatshot_24).fit().centerCrop()
            .into(clientProfile)

        val contLabel = view.findViewById<View>(R.id.salesLabelText) as TextView

        if(mContact[position].label != null){
            contLabel.text = mContact[position].label
            if (mContact[position].label == "Lead" || mContact[position].label == "LEAD"){
                contLabel.setBackgroundColor(resources.getColor(R.color.ColorPurple))
            } else if (mContact[position].label == "Qualified"){
                contLabel.setBackgroundColor(resources.getColor(R.color.ColorBlueNote))
            } else if (mContact[position].label== "Proposal"){
                contLabel.setBackgroundColor(resources.getColor(R.color.ColorMaroon))
            } else if (mContact[position].label == "Client"){
                contLabel.setBackgroundColor(resources.getColor(R.color.ColorGreen))
            } else if (mContact[position].label == "Invoiced"){
                contLabel.setBackgroundColor(resources.getColor(R.color.ColorYellow))
            } else if (mContact[position].label == "Unpaid"){
                contLabel.setBackgroundColor(resources.getColor(R.color.ColorRed))
            } else if (mContact[position].label == ""){
                salesLabel.visibility = View.GONE
            }
        }

        return view
    }

    override fun getFilter(): Filter {
        return object : Filter() {
            override fun publishResults(
                charSequence: CharSequence?,
                filterResults: FilterResults
            ) {
                mContact = filterResults.values as List<Contact>
                notifyDataSetChanged()
            }

            override fun performFiltering(charSequence: CharSequence?): FilterResults {
                var queryString = charSequence?.toString()?.toLowerCase(Locale.ROOT)

                val results = FilterResults()
                results.values = if (queryString == null || queryString.isEmpty())
                    allContacts
                else {
                    queryString = "%${charSequence}%"
                    val db = AppDatabase.getDatabase(context)
                    allContacts = db.contactsDao().getBySearch(queryString)
                    allContacts
                }
                return results
            }


        }
    }
}

private fun showCalendar() {

    val datePicker = DatePickerDialog(
        this,
        R.style.DateTimePickerTheme,
        { datePicker: DatePicker, year: Int, month: Int, day: Int ->
            dueDate.set(year, month, day)
            updateDateFields()
        },
        calendar.get(Calendar.YEAR),
        calendar.get(Calendar.MONTH),
        calendar.get(Calendar.DAY_OF_MONTH)
    )
    datePicker.show()
}

private fun updateDateFields() {
    val formatter = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault())
    val invoiceDueDt = findViewById<TextView>(R.id.invoiceDueDt)
    if(dueDate.time < Calendar.getInstance().time)
    {
        Toast.makeText(this, "Please select a date that is today or later", Toast.LENGTH_SHORT).show()
    } else {
        invoiceDueDt.text = formatter.format(dueDate.time)
    }
}


private fun editInvoice() {

    val invoiceTitle = findViewById<EditText>(R.id.invoiceTitle)
    val invoiceClient = findViewById<AutoCompleteTextView>(R.id.invoiceClient)
    val itemOtherDetails = findViewById<EditText>(R.id.itemOtherDetails)

    if(invoice!!.invoiceTitle != "0"){
        invoiceTitle.setText(invoice!!.invoiceTitle)
    }
    if(invoice!!.invoiceClientLKey != "0"){
        //getting client name from the database
        val getClient = AppDatabase.getDatabase(this)
        contact = getClient.contactsDao().getSingleContact(invoice!!.invoiceClientLKey!!)
        invoiceClient.setText(contact.name)
    }
    if(invoice!!.othComments != "0"){
        itemOtherDetails.setText(invoice!!.othComments)
    }

    addinvoiceItem()

    invoiceClient.setOnClickListener{
        val intent = Intent(this, ContactDetailsHome::class.java)
        intent.putExtra("contact", contact as Serializable)
        this.startActivity(intent)
    }
}

private fun addinvoiceItem() {

    val invoiceItemsLayout = findViewById<RelativeLayout>(R.id.invoiceItemsLayout)
    val invoiceSubValue = findViewById<TextView>(R.id.invoiceSubValue)
    val invoiceTaxValue = findViewById<TextView>(R.id.invoiceTaxValue)
    val invoiceTotalValue = findViewById<TextView>(R.id.invoiceTotalValue)

    if(invoice != null) {

        if(intent.getSerializableExtra("invoiceItem") == null) {
            //getting invoice items stored in the local db
            val getItems = AppDatabase.getDatabase(this)
            invoiceItems = getItems.InvoiceItemsDao().getinvItem(invoice!!.invNo)
        }

        for (i in invoiceItems!!.indices+1) {

            //Check if invoiceItems have been received from EditBillingItems if yes, then update the particular object
            if(j == i){
                invoiceItems!![i].itemTitle = invoiceUpdt!!.itemTitle
                invoiceItems!![i].itemDesc = invoiceUpdt!!.itemDesc
                invoiceItems!![i].itemQty = invoiceUpdt!!.itemQty
                invoiceItems!![i].itemRate = invoiceUpdt!!.itemRate
                invoiceItems!![i].itemTaxable = invoiceUpdt!!.itemTaxable
                invoiceItems!![i].itemTotal = invoiceUpdt!!.itemTotal
            }


            //Item Name Display on Invoice Activity
            val itemTitle: ArrayList<TextView>? = null
            itemTitle!![i] = TextView(this)
            val layoutParams: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
                RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT
            ) // or wrap_content
            layoutParams.setMargins(0, 0, 0, 0)
            layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START)
            itemTitle[i].hint = "Enter Item Title"
            itemTitle[i].textSize = 16f
            itemTitle[i].layoutParams = layoutParams
            itemTitle[i].setTextColor(resources.getColor(R.color.txtcolor))
            invoiceItemsLayout.addView(itemTitle[i])

            itemTitle[i].text = invoiceItems!![i].itemTitle


            //Total Item Value
            val itemValue: ArrayList<TextView>? = null
            itemValue!![i] = TextView(this)
            val layoutParams2: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
                RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT
            ) // or wrap_content
            layoutParams2.setMargins(0, 0, 0, 0)
            layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_END)
            itemValue[i].text = "00.00"
            itemValue[i].textSize = 16f
            itemValue[i].setTextColor(resources.getColor(R.color.txtcolor))
            itemValue[i].setPadding(10, 0, 0, 10)
            itemValue[i].layoutParams = layoutParams2
            invoiceItemsLayout.addView(itemValue[i], layoutParams)


            itemValue[i].text = invoiceItems!![i].itemTotal.toString()

            if(invoiceItems!![i + 1].itemTitle == ""){
                //New Item Title field
                val itemTitle: ArrayList<TextView>? = null
                itemTitle!![i] = TextView(this)
                val layoutParams: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
                    RelativeLayout.LayoutParams.WRAP_CONTENT,
                    RelativeLayout.LayoutParams.WRAP_CONTENT
                ) // or wrap_content
                layoutParams.setMargins(0, 0, 0, 0)
                layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START)
                itemTitle[i].hint = "Enter Item Title"
                itemTitle[i].textSize = 16f
                itemTitle[i].setTextColor(resources.getColor(R.color.txtcolor))
                invoiceItemsLayout.addView(itemTitle[i], layoutParams)

                //New item value field
                val itemValue: ArrayList<TextView>? = null
                itemValue!![i] = TextView(this)
                val layoutParams2: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
                    RelativeLayout.LayoutParams.WRAP_CONTENT,
                    RelativeLayout.LayoutParams.WRAP_CONTENT
                ) // or wrap_content
                layoutParams2.setMargins(0, 0, 0, 0)
                layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_END)
                itemValue[i].text = "00.00"
                itemValue[i].textSize = 16f
                itemValue[i].setTextColor(resources.getColor(R.color.txtcolor))
                itemValue[i].setPadding(10, 0, 0, 10)
                itemValue[i].layoutParams = layoutParams2
                invoiceItemsLayout.addView(itemValue[i], layoutParams)

                itemTitle[i].setOnClickListener {

                    val invoiceItemSend: InvoiceItems = invoiceItems!![i]

                    updateInvoiceValues()

                    val intent = Intent(this, EditBillingItem::class.java)
                    intent.putExtra("invoiceItem", invoiceItems as Serializable)
                    intent.putExtra("index", i)
                    intent.putExtra("invoice", invoice as Serializable)
                    this.startActivity(intent)
                }
            }
            subTotal += invoiceItems!![i].itemTotal
            val taxValue = invoiceItems!![i].itemRate!!
            taxrate += (subTotal * taxValue)
        }
        invoiceSubValue.setText(subTotal)
        total = subTotal + taxrate
        invoiceTaxValue.setText(taxrate)
        invoiceTotalValue.setText(total)

    } else {

        //Item Name Display on Invoice Activity
        val layoutParams: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
            RelativeLayout.LayoutParams.WRAP_CONTENT,
            RelativeLayout.LayoutParams.WRAP_CONTENT
        ) // or wrap_content
        layoutParams.setMargins(0, 0, 0, 0)
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START)
        val itemTitle = TextView(this)
        itemTitle.hint = "Enter Item Title"
        itemTitle.textSize = 16f
        itemTitle.setTextColor(resources.getColor(R.color.txtcolor))
        invoiceItemsLayout.addView(itemTitle, layoutParams)

        //Total Item Value
        val itemValue = TextView(this)
        val layoutParams2: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(
            RelativeLayout.LayoutParams.WRAP_CONTENT,
            RelativeLayout.LayoutParams.WRAP_CONTENT
        ) // or wrap_content
        layoutParams2.setMargins(0, 0, 0, 0)
        layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_END)
        itemValue.text = "00.00"
        itemValue.textSize = 16f
        itemValue.setTextColor(resources.getColor(R.color.txtcolor))
        itemValue.setPadding(10, 0, 0, 10)
        itemValue.layoutParams = layoutParams2
        invoiceItemsLayout.addView(itemValue, layoutParams2)

        itemTitle.setOnClickListener {
            updateInvoiceValues()
            val intent = Intent(this, EditBillingItem::class.java)
            if(invoiceItems != null){
                intent.putExtra("invoiceItem", invoiceItems as Serializable)
            }
            if(invoice != null){
                intent.putExtra("invoice", invoice as Serializable)
            }
            intent.putExtra("index",0)
            this.startActivity(intent)
        }
    }
}

private fun updateInvoiceValues() {
    //updating values

    val invoiceTitle = findViewById<EditText>(R.id.invoiceTitle)
    val invoiceDueDt = findViewById<TextView>(R.id.invoiceDueDt)

    if(invoiceTitle.text.toString().isNotEmpty()){
        invoiceEdit?.invoiceTitle = invoiceTitle.text.toString()
    }
    if(clientLkey != ""){
        invoiceEdit?.invoiceClientLKey = clientLkey
    }
    if (invoiceDueDt.text.toString().isNotEmpty()){
        invoiceEdit?.dueDate = invoiceDueDt.text.toString()
    }

}

活动 B - 编辑项目

class EditBillingItem : AppCompatActivity() {

companion object {
    @JvmStatic
    fun start(context: Context, invoice: Invoice?, invoiceItems: InvoiceItems?) {
        val i = 0
        val starter = Intent(context, EditInvoice::class.java)
            .putExtra("invoice", invoice)
            .putExtra("invoiceItem", invoiceItems)
            .putExtra("i", i)
        context.startActivity(starter)
    }
}

private var invoiceItem: List<InvoiceItems>? = null
private var invoice: Invoice? = null
private lateinit var invoiceItemUpdt: InvoiceItems
private var i = 0

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_billing_item_update)

    invoice = intent.getSerializableExtra("invoiceEdit") as Invoice
    invoiceItem = intent.getSerializableExtra("invoiceItem") as? List<InvoiceItems>
    i = intent.getIntExtra("i", i)

    val itemTitle = findViewById<EditText>(R.id.itemTitle)
    val itemDesc = findViewById<EditText>(R.id.itemDesc)
    val itemQty = findViewById<EditText>(R.id.itemQty)
    val itemChrgRt = findViewById<EditText>(R.id.itemChrgRt)
    val itemTaxable = findViewById<SwitchCompat>(R.id.itemTaxable)
    val itemTotal = findViewById<TextView>(R.id.itemTotal)
    val itemBack = findViewById<ImageButton>(R.id.itemBack)

    if(intent.getSerializableExtra("invoiceItems") != null){

        itemTitle.setText(invoiceItem!![i].itemTitle)
        itemDesc.setText(invoiceItem!![i].itemDesc)
        itemQty.setText(invoiceItem!![i].itemQty.toString())
        itemChrgRt.setText(invoiceItem!![i].itemRate.toString())
        itemTaxable.isChecked = invoiceItem!![i].itemTaxable == true
        itemTotal.setText(invoiceItem!![i].itemTotal)
    }

    itemChrgRt.addTextChangedListener(object : TextWatcher {

            override fun afterTextChanged(s: Editable) {}

            override fun beforeTextChanged(s: CharSequence, start: Int,
                                           count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence, start: Int,
                                       before: Int, count: Int) {

                if(s.toString().isNotEmpty() && itemQty.text.toString().isNotEmpty()){

                    var i = itemQty.text.toString().toInt()
                    val j = s.toString().toInt()
                    i *= j
                    itemTotal.text = i.toString()
                }
            }
        })

    itemQty.addTextChangedListener(object : TextWatcher {

        override fun afterTextChanged(s: Editable) {}

        override fun beforeTextChanged(s: CharSequence, start: Int,
                                       count: Int, after: Int) {
        }

        override fun onTextChanged(s: CharSequence, start: Int,
                                   before: Int, count: Int) {

            if(itemChrgRt.text.toString().isNotEmpty() && itemChrgRt.text.toString().isNotEmpty()){

                var i = s.toString().toInt()
                val j = itemChrgRt.text.toString().toInt()
                i *= j
                itemTotal.text = i.toString()
            }

        }
    })

    itemBack.setOnClickListener {

        if(itemTitle.toString().isBlank() && itemQty.toString().isNotBlank() || itemChrgRt.toString().isNotBlank() && itemTitle.toString().isBlank()){
            Toast.makeText(this, "Title can't be empty", Toast.LENGTH_SHORT).show()
            return@setOnClickListener
        }
        invoiceItemUpdt = InvoiceItems("0","0","0",null,null,null,null,0)

        invoiceItemUpdt.itemTitle = itemTitle.toString()
        invoiceItemUpdt.itemTitle = itemDesc.toString()
        if(itemQty.toString().isEmpty()){
            invoiceItemUpdt.itemQty = 1
        } else {
            invoiceItemUpdt.itemQty = itemQty.text.toString().toInt()
        }
        invoiceItemUpdt.itemRate = itemChrgRt.text.toString().toInt()
        itemTaxable.isChecked = invoiceItemUpdt.itemTaxable == true

        val intent = Intent(this, EditInvoice::class.java)
        intent.putExtra("invoice", invoice as Serializable)
        intent.putExtra("invoiceItem", invoiceItem as Serializable)
        intent.putExtra("invoiceItemUpdt", invoiceItemUpdt as Serializable)
        intent.putExtra("index", i)
        this.startActivity(intent)
    }
}

在此处输入图像描述 在此处输入图像描述

来自 logcat 的错误 java.lang.NullPointerException:null 不能在 android.view.View 的 in.latom.latom.Billing.ui.EditInvoice$addinvoiceItem$2.onClick(EditInvoice.kt:449) 处转换​​为非 null 类型 java.io.Serializable .performClick(View.java:7398) at android.view.View.performClickInternal(View.java:7375) at android.view.View.access$3700(View.java:817) at android.view.View$PerformClick.run (View.java:28516) 在 android.os.Handler.handleCallback(Handler.java:938) 在 android.os.Handler.dispatchMessage(Handler.java:99) 在 android.os.Looper.loopOnce(Looper.java: 201) 在 android.os.Looper.loop(Looper.java:288) 在 android.app.ActivityThread.main(ActivityThread.java:7858) 在 java.lang.reflect.Method.invoke(Native Method) 在 com.android .internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:984)

标签: androidkotlinandroid-activityserializable

解决方案


如果此行返回 null,您的invoice变量将为 null: invoice = intent.getSerializableExtra("invoice") as? Invoice

稍后在您的代码中,您从未填充过您的invoice变量,但您在这一行中强制解包它:

db.InvoicesDao().addInvoice(invoice!!)

此 if 语句始终为空:

if(invoice != null){
   intent.putExtra("invoice", invoice as Serializable)
}

推荐阅读