티스토리 뷰

결과화면

 

1. ModelCategory 

Firebase DB의 Category에 저장될 항목 설정 후 생성자 호출

2. AdapterCategory

Admin 계정 접속 시 보이는 카테고리 목록 RecyclerView용 Adapter

- Context 변수 선언

- context와 categoryArrayList 생성자 호출

- inner class인 HolderCategory 통하여 카테고리명과 삭제 휴지통 이미지 아이콘 ui 초기화

- row_category(카테고리 목록) 바인딩

- ViewHolder 바인딩 작업 (각 항목은 arraylist의 position에 따른 model로 선언)

- 삭제처리는 firebase의 removeValue()활용

- Filterable 상속

class AdapterCategory: RecyclerView.Adapter<AdapterCategory.HolderCategory>, Filterable {

    private val context: Context
    public var categoryArrayList: ArrayList<ModelCategory>
    private var filterList: ArrayList<ModelCategory>
    private var filter: FilterCategory? = null

    private lateinit var binding: RowCategoryBinding

    // constructor
    constructor(context: Context, categoryArrayList: ArrayList<ModelCategory>) {
        this.context = context
        this.categoryArrayList = categoryArrayList
        this.filterList = categoryArrayList
    }

    // inflate row_category
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HolderCategory {
       binding = RowCategoryBinding.inflate(LayoutInflater.from(context), parent, false)

        return HolderCategory(binding.root)
    }
    // data getter and setter w click handler
    override fun onBindViewHolder(holder: HolderCategory, position: Int) {
        // get data
        val model = categoryArrayList[position]
        val id = model.id
        val category = model.category
        val uid = model.uid
        val timestamp = model.timestamp
        // set data
        holder.categoryTv.text = category
        // delete btn click listener
        holder.deleteBtn.setOnClickListener{
            // alert message
            val builder = AlertDialog.Builder(context)
            builder.setTitle("Delete")
                .setMessage("Do you want to delete?")
                .setPositiveButton("Confirm"){a, d ->
                    Toast.makeText(context, "Deleting", Toast.LENGTH_SHORT).show()
                    deleteCategory(model, holder)
                }
                .setNegativeButton("Cancel"){a, d ->
                    a.dismiss()
                }
                .show()
        }
    }

    private fun deleteCategory(model: ModelCategory, holder: HolderCategory) {
        var id = model.id
        val ref = FirebaseDatabase.getInstance().getReference("Categories")
        ref.child(id)
            .removeValue()
            .addOnSuccessListener {
                Toast.makeText(context, "Deleted successfully", Toast.LENGTH_SHORT).show()
            }
            .addOnFailureListener { e ->
                Toast.makeText(context, "Unable to delete. Error: ${e.message}", Toast.LENGTH_SHORT).show()
            }
    }

    override fun getItemCount(): Int {
        return categoryArrayList.size
    }

    // Viewholder for row_category UI
    inner class HolderCategory(itemView: View): RecyclerView.ViewHolder(itemView){
        // initialize ui view
        var categoryTv: TextView = binding.categoryTv
        var deleteBtn: ImageButton = binding.deleteBtn
    }

    override fun getFilter(): Filter {
        if(filter == null){
            filter = FilterCategory(filterList, this)
        }
        return filter as FilterCategory
    }

}

 

3. FilterCategory

- filterList와 adapterCategory 선언, 생성자 호출

- Filter를 상속받아 구현하는 performFiltering에 찾는 값이 null 혹은 빈값인지 확인 후 각 처리할 것(대문자 처리 필요하며 null 혹은 공백일 경우 전체 결과값을 추출해야 하므로 filerList의 값과 수를 반환)

- publishingResults에는 값이 변할 경우에 대비하여 각 값 설정

class FilterCategory: Filter {

    // arraylist to search
    private var filterList: ArrayList<ModelCategory>
    // adapter where filter needs to be implemented
    private var adapterCategory: AdapterCategory
    // constructor
    constructor(filterList: ArrayList<ModelCategory>, adapterCategory: AdapterCategory) : super() {
        this.filterList = filterList
        this.adapterCategory = adapterCategory
    }

    override fun performFiltering(constraint: CharSequence?): FilterResults {
        var constraint = constraint
        val results = FilterResults()

        // value are NOT nullable and empty
        if(constraint != null && constraint.isNotEmpty()){
            // change to upper case
            constraint = constraint.toString().uppercase()
            val filteredModels: ArrayList<ModelCategory> = ArrayList()
            for(i in 0 until filterList.size){
                // validate data
                if(filterList[i].category.uppercase().contains(constraint)){
                    // add
                    filteredModels.add(filterList[i])
                }
            }
            results.count = filteredModels.size
            results.values = filteredModels
        } else{ // in case of null or empty
            results.count = filterList.size
            results.values = filterList
        }
        return results
    }

    override fun publishResults(constraint: CharSequence?, results: FilterResults) {
        // apply filter changes
        adapterCategory.categoryArrayList = results.values as ArrayList<ModelCategory>
        // notify of them
        adapterCategory.notifyDataSetChanged()
    }

}

4. DashboardAdminActivity

- categoryArrayList와 adapterCategory 각 전역변수 선언

- filter 기능 - addTextChangeListener에 textWatcher 상속 받은 후 onTextChanged 이외 공백으로 남기기

- Dashboard 접속 시 초기화면용 카테고리 불러오기

class DashboardAdminActivity : AppCompatActivity() {

    // arraylist to hold categories
    private lateinit var categoryArrayList: ArrayList<ModelCategory>
    // adapter
    private lateinit var adapterCategory: AdapterCategory
    
     // initialize firebase auth
        firebaseAuth = FirebaseAuth.getInstance()
        checkUser()
        loadCategories()
        
         // search event
        binding.searchEt.addTextChangedListener(object: TextWatcher{
            override fun beforeTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {
            }
            override fun onTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {
                try {
                    adapterCategory.filter.filter(s)
                }catch(e: Exception){

                }
            }
            override fun afterTextChanged(s: Editable?) {
            }
        })
        
         private fun loadCategories() {
        // initialize arraylist
        categoryArrayList = ArrayList()
        // get all saved categories from firebase db
        val ref = FirebaseDatabase.getInstance().getReference("Categories")
        ref.addValueEventListener(object: ValueEventListener{
            override fun onDataChange(snapshot: DataSnapshot) {
                // clear list before adding data
                categoryArrayList.clear()
                for(ds in snapshot.children){
                    // get data
                    val model = ds.getValue(ModelCategory::class.java)
                    // add to arraylist
                    categoryArrayList.add(model!!)
                }
                // adapter
                adapterCategory = AdapterCategory(this@DashboardAdminActivity, categoryArrayList)
                // recyclerview
                binding.categoriesRv.adapter = adapterCategory
            }

            override fun onCancelled(error: DatabaseError) {
            }

        })
    }

 

 

 

 

느낀점: RecyclerView의 Adapter가 이제야 이해가 얼추 가는 듯하다 그러나 filter를 적용할 경우 재차 원점으로 돌아가는 느낌이며 흡사 스프링할 때 pagination과 찾기의 고통이 생각난다😅😅😅 Ну что поделать? Пока с большим удовольствием от нулевой безопасности адаптируюсь)))))

 

 

 

Ref: https://www.youtube.com/channel/UCT132T980-1lhm0hcZFy4ZA