Android 进阶 2、商品列表展示案例(1)

因为使用到数据库和适配器监听等等的很多功能,我们使用java开发完成这个案例需要很多的代码,所以我们就决定转战kotlin了,相对于java,kotlin有代码简洁,功能齐全的优点,是一门可以胜任全栈开发的语言,所以这里建议大家尽快学会使用kotlin进行开发;

项目展示:

 

因为项目比较大,这里我们就先做好这些功能,长按菜单的功能制作我们留到下一期;

1、创建kotlin项目:

这里我们和之前的一样,空项目,只不过语言改成了kotlin,创建好项目我们可以发现和java的工程结构一模一样,只是文件变成了kt为后缀的kotlin类,那么如何创建一个kotlin类呢?

kotlin偏向架构开发,也就是我们需要定义好对应功能的各个包结构:

 这里有很多个包,我们在里面实现了不同的功能,目的是为了后期的维护方便,adapter包里面实现的是适配器功能,dao包里面实现的是对数据的操作功能,entity是各种各样的实体类,ui里面就是我们的界面类,管理布局文件的;

2、先从帮助类开始

因为我们想要创建数据库存储信息先要有一个帮助类来帮助程序创建数据库和数据表,所以我们打开DataHelper开始写程序:

package com.example.shoplist

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper

//创建数据库和数据表
class DataHelper(var context: Context, var dbname:String = "account.db") :
    SQLiteOpenHelper(context,dbname,null,1) {
    private val create_table:String = "create table account(_id integer primary key autoIncrement ,name text," +
            "balance integer);"
    override fun onCreate(p0: SQLiteDatabase?) {
//        TODO("Not yet implemented")
        //创建数据表
        p0?.execSQL(create_table)
    }

    override fun onUpgrade(p0: SQLiteDatabase?, p1: Int, p2: Int) {
        //TODO("Not yet implemented")
    }

}

这里对应的是帮助类,同样继承了sqliteopenhelper父类,kotlin可以直接在定义的时候实现构造方法,继承的父类可以直接在后面加上小括号来实现构造方法,帮助类的参数为上下文和数据库名称,和之前的一样,我就不过多赘述;

在接口方法里面我们需要定义一个常量用来创建一个数据表,这里我们定义了一个sql字符串,创建了一个account的表,在oncreate函数中可以通过p0的execsql方法执行语句,注意,这里的问号在kotlin很常见,用途是声明变量可以为空;

3、然后就是实体类

也就是entity包下面对应的类:

package com.example.shoplist.entity

//实体类对象
data class Account(var id:Long = 0,var name:String = "",var balance:Int = 0) {
}

实体类在kotlin当中常常用来存放数据,怎么来表明一个类是实体类呢?很简单,只需要在class前面加上data就可以了,然后不需要写任何代码;

4、操作类

dao层下面的操作类:

package com.example.shoplist.dao

import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import com.example.shoplist.DataHelper
import com.example.shoplist.entity.Account

//操作类,增删改查
class AccountDao(context: Context) {
    //1.1 创建自己的帮助类
    lateinit var helper: DataHelper
    lateinit var accountlist:MutableList<Account>
    //这些变量我们只需要创建一次就可以了
    init {
        helper = DataHelper(context)
        accountlist = mutableListOf()
    }

    //1.2 插入数据函数
    fun insert(account:Account){
        var db:SQLiteDatabase = helper.writableDatabase
        //创建存储键值对的对象
        var contentvalue:ContentValues = ContentValues()
        contentvalue.put("name",account.name)
        contentvalue.put("balance",account.balance)
        var id:Long = db.insert("account",null,contentvalue)

        //将id设置给account
        account.id = id
    }

    //1.3 获取所有商品信息
    @SuppressLint("Range")
    fun getAllList():MutableList<Account>{
        var db:SQLiteDatabase = helper.readableDatabase
        var cursor:Cursor = db.query("account",null,null,null,null,null,null)
        accountlist.clear()
        var account:Account
        while (cursor.moveToNext()){
            account = Account()
            //根据游标获取到数据表中的数据,添加到account对象列表中
            var id:Long = cursor.getLong(cursor.getColumnIndex("_id"))
            account.id = id
            var name:String = cursor.getString(cursor.getColumnIndex("name"))
            account.name = name
            var balance:Int = cursor.getInt(cursor.getColumnIndex("balance"))
            account.balance = balance
            accountlist.add(account)
        }
        cursor.close()
        return accountlist
    }
}
    //1.4 对数据的更新
    fun update(account: Account): Int{
        //获取操作对象
         var db: SQLiteDatabase = helper.writableDatabase
         var contentvalue:ContentValues = ContentValues()
        contentvalue.put("_id",account.id)
        contentvalue.put("name",account.name)
        contentvalue.put("balance",account.balance)
        var result: Int = db.update("account",contentvalue,"_id=?", arrayOf(account.id.toString()))
        return result
    }
    //1.5 对数据的删除
    fun delete(account: Account): Int{
        var db: SQLiteDatabase = helper.writableDatabase
        var result:Int = db.delete("account","_id=?", arrayOf(account.id.toString()))
        return result

    }

这里的代码比较多,因为要实现数据的增删改查,使用操作类writableDatabase和readableDatabase来对数据查询或者修改

在插入数据的函数中,我们同样需要获取数据操作对象writableDatabase,然后通过ContentValues对象将我们获取的到值存储进入数据表,使用db.insert("account",null,contentvalue)将数据插入到数据表;

在kotlin当中,实体类的用处很大,我们存储数据都是通过实体类,传递数据也是实体类,获取数据还是实体类,相当于一个数据传递的桥梁的作用;

在获取所有数据的函数中(getAllList),同样的我们需要一个游标(cursor)通过db.query查询返回的结果是一个cursor,然后通过这个游标对象的遍历就可以获取到表中所有的数据,我们只需要创建好一个实体类对象,将查询到的数据一个一个存到实体类中,再将实体类add进入对象列表中就可以了,最后返回这个列表,因为后期需要将数据显示到适配器中;

4、适配器

package com.example.shoplist.adapter

import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import com.example.shoplist.R
import com.example.shoplist.dao.AccountDao
import com.example.shoplist.entity.Account

class AccountAdapter(var context: Context, var accountlist: MutableList<Account>) : BaseAdapter() {

    //4.2 定义一个全局的dao操作变量
    var dao:AccountDao = AccountDao(context)

    //使用viewholder快速设置组件的值,提升listview的性能
    class MyViewHolder {

        lateinit  var tv_listitem_id: TextView
        lateinit var  tv_listitem_name: TextView
        lateinit var  tv_listitem_banlance: TextView

    }
    @SuppressLint("ViewHolder")
    override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
        //TODO("Not yet implemented")
        //3.1 获取到布局文件的视图对象

        var viewHolder : MyViewHolder
        var view : View

        if(p1 == null){
            viewHolder = MyViewHolder()
            //获取视图对象
            view = LayoutInflater.from(context).inflate(R.layout.listview_item, null)
            viewHolder.tv_listitem_id = view.findViewById(R.id.tv_listitem_id) as TextView
            viewHolder.tv_listitem_name = view.findViewById(R.id.tv_listitem_name) as TextView
            viewHolder.tv_listitem_banlance = view.findViewById(R.id.tv_listitem_banlance) as TextView
            view.tag = viewHolder
        }else{
            view = p1
            viewHolder =view.tag as MyViewHolder
        }
        //获取到当前的account对象
        var account: Account = accountlist.get(p0)

        //3.3 赋值给组件
        viewHolder.tv_listitem_id.text = account.id.toString()
        viewHolder.tv_listitem_name.text = account.name
        viewHolder.tv_listitem_banlance.text = account.balance.toString()

        //4.1 获取到对应的图片按钮添加单击事件
        var img_up:ImageView = view.findViewById(R.id.img_up)
        var img_down:ImageView = view.findViewById(R.id.img_down)
        var img_delete:ImageView = view.findViewById(R.id.img_delete)

        img_up.setOnClickListener {
            //需要处理的事件
            account.balance++
            dao.update(account)
            notifyDataSetChanged()
        }

        img_down.setOnClickListener {
            account.balance--
            dao.update(account)
            notifyDataSetChanged()
        }

        img_delete.setOnClickListener {
            //4.3 删除数据的时候我们需要弹出对话框
            var builder: AlertDialog.Builder = AlertDialog.Builder(context)
            builder.setTitle("警告")
            builder.setIcon(R.mipmap.icon)
            builder.setMessage("你真的要删除吗?")
            builder.setPositiveButton("确定"){dialogInterface, i ->
                //用户确认后我们将数据库和对象数列中的数据移除
                var result: Int = dao.delete(account)
                accountlist.remove(account)
                notifyDataSetChanged()
                
                if(result > 0){
                    Toast.makeText(context,"删除成功",Toast.LENGTH_LONG).show()
                }else{
                    Toast.makeText(context,"删除失败",Toast.LENGTH_LONG).show()
                }
            }
            builder.setNegativeButton("取消"){
                dialogInterface, i ->  
            }
            builder.show()
        }


        return view

       /* p1 = LayoutInflater.from(context).inflate(R.layout.listview_item, p2, false)


        //3.2 获取到视图中的组件对象
        var tv_listitem_id: TextView = p1.findViewById(R.id.tv_listitem_id)
        var tv_listitem_name: TextView = p1.findViewById(R.id.tv_listitem_name)
        var tv_listitem_banlance: TextView = p1.findViewById(R.id.tv_listitem_banlance)

        //获取到当前的account对象
        var account: Account = accountlist.get(p0)

        //3.3 赋值给组件
        tv_listitem_id.setText(account.id.toString())
        tv_listitem_name.setText(account.name)
        tv_listitem_banlance.setText(account.balance.toString())
        //返回设置好的视图对象
        return p1*/
    }

    override fun getItem(p0: Int): Any {
        //TODO("Not yet implemented")
        return accountlist.get(p0)
    }

    override fun getItemId(p0: Int): Long {
        //TODO("Not yet implemented")
        return p0.toLong()
    }

    override fun getCount(): Int {
        //TODO("Not yet implemented")
        return accountlist.size
    }
}

这里可能很多小伙伴会一头雾水了,虽然kotlin和java都可以开发Android,但是在定义变量和函数的方法上还是有很大的区别,所以我这里推荐大家先去学会kotlin基础再来入手这个项目;不过毕竟之前学过这些Android的知识点,也很容易看出来逻辑;

kotlin中的实体类获取view对象的方法还是和java有点区别:

if(p1 == null){
            viewHolder = MyViewHolder()
            //获取视图对象
            view = LayoutInflater.from(context).inflate(R.layout.listview_item, null)
            viewHolder.tv_listitem_id = view.findViewById(R.id.tv_listitem_id) as TextView
            viewHolder.tv_listitem_name = view.findViewById(R.id.tv_listitem_name) as TextView
            viewHolder.tv_listitem_banlance = view.findViewById(R.id.tv_listitem_banlance) as TextView
            view.tag = viewHolder
        }else{
            view = p1
            viewHolder =view.tag as MyViewHolder
        }
        
          //使用viewholder快速设置组件的值,提升listview的性能
    class MyViewHolder {

        lateinit  var tv_listitem_id: TextView
        lateinit var  tv_listitem_name: TextView
        lateinit var  tv_listitem_banlance: TextView

    }  

主要就是要理解这一部分,我们使用了viewholder对象,这个对象在自定义适配器中非常常用,他的作用就是快速设置组件的值,然后提高程序的运行效率,这里定义这个对象的方式也很简单,就是class里面写几个属性就可以了,但是要注意这个属性是个布局文件的组件一一对应的,然后在getview函数里面就可以通过视图对象将布局文件的组件对象设置到viewholder中,在将数据列表中的数据传递到viewholder中,总的来说和实体类的作用类似;

5、main布局界面

package com.example.shoplist.ui

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.ContextMenu
import android.view.MenuItem
import android.view.View
import android.widget.AdapterView
import android.widget.EditText
import android.widget.ListView
import android.widget.Toast
import com.example.shoplist.R
import com.example.shoplist.adapter.AccountAdapter
import com.example.shoplist.dao.AccountDao
import com.example.shoplist.entity.Account

class MainActivity : AppCompatActivity() {

    //2.1 获取到页面组件等变量
    lateinit var et_name: EditText
    lateinit var et_balance: EditText
    lateinit var shop_list: ListView
    lateinit var dao: AccountDao
    lateinit var adapter: AccountAdapter
    lateinit var accountlist: MutableList<Account>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //调用函数
        init()
        shop_list.adapter = adapter

    }

    //2.3 数据的初始化函数
    fun init() {
        et_name = findViewById(R.id.et_name)
        et_balance = findViewById(R.id.et_balance)
        shop_list = findViewById(R.id.shop_list)
        dao = AccountDao(this)
        accountlist = dao.getAllList()
        adapter = AccountAdapter(this, accountlist)
        //注册事件,只需要在对象初始化之后
        registerForContextMenu(shop_list)
    }

    //2.2 添加数据监听
    fun add(view: View) {
        var name: String = et_name.text.toString()
        var balance: Int = et_balance.text.toString().toInt()
        var account: Account = Account()
        account.name = name
        account.balance = balance
        //调用操作类的添加函数
        dao.insert(account)
        et_name.setText("")
        et_balance.setText("")
        Toast.makeText(this, "添加数据成功", Toast.LENGTH_LONG).show()
        dao.getAllList()
        adapter.notifyDataSetInvalidated()
    }

    override fun onCreateContextMenu(
        menu: ContextMenu?,
        v: View?,
        menuInfo: ContextMenu.ContextMenuInfo?
    ) {
        super.onCreateContextMenu(menu, v, menuInfo)
        menu?.setHeaderTitle("请选择")
        menu?.add(0, 1, 100, "替换")
        menu?.add(0, 2, 200, "删除")
        menu?.add(0, 3, 300, "查找")
    }

    override fun onContextItemSelected(item: MenuItem): Boolean {

        var info: AdapterView.AdapterContextMenuInfo =
            item.menuInfo as AdapterView.AdapterContextMenuInfo
        var id: Int = info.id.toInt()
        var account: Account = accountlist.get(id)
        //使用开关函数
        when (item.itemId) {
            1 -> accountupdate(account)
            2 -> accountdelete(account)
            3 -> accountlook(account)
        }
        return super.onContextItemSelected(item)


    }

    //2.3 菜单点击函数实现对应操作
    fun accountupdate(account: Account) {
        Toast.makeText(this,"亲,数据更新",Toast.LENGTH_LONG).show()
    }

    fun accountdelete(account: Account) {
        Toast.makeText(this,"亲,功能还在开发",Toast.LENGTH_LONG).show()
    }

    fun accountlook(account: Account) {
        Toast.makeText(this,"亲,已显示全部信息",Toast.LENGTH_LONG).show()
    }


}

ui界面程序实现的功能也很简单,无非就是按钮监听和组件数据传递的;

老生常谈的了,先设置组件等变量,然后初始化,在就是监听按钮的逻辑,设置调用函数设置适配器就可以了;这里出现了一个新的声明变量的东西lateinit var ,作用就是延迟赋值

最后不要忘了适配器和数据的刷新,因为我们是要实时改变界面上的数据的;

6、接下来我们在回顾一下项目,侧重讲解一下几个重要的方法:

  • 添加价格改变图标的单机事件

因为实在布局文件上面的组件,我们需要在适配器上添加单机事件,另外操作数据的话就需要定义一个dao对象,在适配器类的全局属性下面:

v   //4.2 定义一个全局的dao操作变量
    var dao:AccountDao = AccountDao(context)

//4.1 获取到对应的图片按钮添加单击事件
        var img_up:ImageView = view.findViewById(R.id.img_up)
        var img_down:ImageView = view.findViewById(R.id.img_down)
        var img_delete:ImageView = view.findViewById(R.id.img_delete)

        img_up.setOnClickListener {
            //需要处理的事件
            account.balance++
            dao.update(account)
            notifyDataSetChanged()
        }

        img_down.setOnClickListener {
            account.balance--
            dao.update(account)
            notifyDataSetChanged()
        }

        img_delete.setOnClickListener {
            //4.3 删除数据的时候我们需要弹出对话框
            var builder: AlertDialog.Builder = AlertDialog.Builder(context)
            builder.setTitle("警告")
            builder.setIcon(R.mipmap.icon)
            builder.setMessage("你真的要删除吗?")
            builder.setPositiveButton("确定"){dialogInterface, i ->
                //用户确认后我们将数据库和对象数列中的数据移除
                var result: Int = dao.delete(account)
                accountlist.remove(account)
                notifyDataSetChanged()
                
                if(result > 0){
                    Toast.makeText(context,"删除成功",Toast.LENGTH_LONG).show()
                }else{
                    Toast.makeText(context,"删除失败",Toast.LENGTH_LONG).show()
                }
            }
            builder.setNegativeButton("取消"){
                dialogInterface, i ->  
            }
            builder.show()
        }

及时都是用于Android开发,java和kotlin在很多函数和变量定义上还是有很大区别,这里kotlin添加监听事件的setOnClickListener 方法就比java要简单很多,直接加上大括号就可以写代码了;

  • 给listview注册菜单组件

      

override fun onCreateContextMenu(
        menu: ContextMenu?,
        v: View?,
        menuInfo: ContextMenu.ContextMenuInfo?
    ) {
        super.onCreateContextMenu(menu, v, menuInfo)
        menu?.setHeaderTitle("请选择")
        menu?.add(0, 1, 100, "替换")
        menu?.add(0, 2, 200, "删除")
        menu?.add(0, 3, 300, "查找")
    }

    override fun onContextItemSelected(item: MenuItem): Boolean {

        var info: AdapterView.AdapterContextMenuInfo =
            item.menuInfo as AdapterView.AdapterContextMenuInfo
        var id: Int = info.id.toInt()
        var account: Account = accountlist.get(id)
        //使用开关函数
        when (item.itemId) {
            1 -> accountupdate(account)
            2 -> accountdelete(account)
            3 -> accountlook(account)
        }
        return super.onContextItemSelected(item)


    }

    //2.3 菜单点击函数实现对应操作
    fun accountupdate(account: Account) {

    }

    fun accountdelete(account: Account) {

    }

    fun accountlook(account: Account) {

    }
  • 在我们每一个商品选项的后面有三个按钮,升价、降价和删除,我们都需要完善一下,补充对应的功能
menu?.setHeaderTitle("请选择")
menu?.add(0, 1, 100, "替换")
menu?.add(0, 2, 200, "删除")
menu?.add(0, 3, 300, "查找")

menu的add方法有四个参数,第一个是分组,第二个是id这个区分我们点击哪一个的标记一定不可以相同,第三个是排序,我们菜单按钮的显示顺序,第四个就是显示的信息了;然后在listview初始化了之后我们注册一下这个菜单组件就可以了:

//注册事件,只需要在对象初始化之后
registerForContextMenu(shop_list)

 然后就是监听菜单组件的单击事件了(onContextItemSelected),我们使用了kotlin中的开关函数when,根据菜单组件的id来进行相应功能的设定;

那么对应菜单的方法实现我们就下期制作,这里也有点多了;

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程学渣ズ

谢谢老板

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值