نمایش نتایج 1 تا 6 از 6

نام تاپیک: مشکل در نمایش سلسه مراتب درختی با زبان کاتلین

  1. #1
    کاربر جدید
    تاریخ عضویت
    دی 1392
    محل زندگی
    قزوین
    پست
    15

    مشکل در نمایش سلسه مراتب درختی با زبان کاتلین

    درود و وقت بخیر...

    می‌خواستم ازتون خواهش کنم که میشه لطف کنین و در مورد پیاده سازی این موردی که الان توضیح خواهم داد کمی راهنماییم کنین...

    خب...

    قراره یه سلسه مراتب درختی رو در قالب یه اپ ساده Todo و با زبان کاتلین و با استفاده از دیزاین پترن‌ها، معماری MVVM، کتابخونه‌های JetPack و اگه بتونم هم از Hilt برای پیاده سازیش استفاده کنم...


    و اساس کار به این صورت هستش که که هر گره (todo) می‌تونه بی‌نهایت زیر مجموعه «از جنس خودش» داشته باشه و هر زیر مجموعه هم بینهایت زیر مجموعه و الی آخر...
    همچنین اگه رو گره (todo) پدر کلیک شد، تمام زیر مجموعه هاش هم تیک بخورن
    و بر عکسش هم به این صورت که اگه، تیک گره(todo) فرزند رو برداریم، تیک تمام گره های پدر هم برداشته بشه...

    این از طرح کلی برنامه...
    و اما مشکلی که بهش برخوردم...

    تا اینجا پیش رفتم که یه لیستی دارم و می‌تونم بهش todo اضافه کنم و اون todo ها رو در قالب ریسایکلر ویو نمایش بدم...

    ولی مشکل اصلی من اینجاست که وقتی می‌خوام روی هر کدوم از آیتم‌های ریساکلر ویو کلیک کنم و برم به صفحه بعد تا بتونم زیر مجموعه‌هاش رو اضافه کنم و اون‌ها رو نمایش بدم به مشکل خوردم...
    البته این رو هم بگم که برنامه فقط از یک فراگمنت به صورت زیر استفاده می‌کنه:

    DeepinScreenshot_select-area_20200823191525.png

    مدل todoی که تعریف کردم شامل فیلدهای زیر هستش:

        var name: String,
    var parent_id: Long ,
    var id: Long



    به این صورت کار کردم که اومدم و id آیتمی که روش کلیک شده رو گرفتم، و در صفحه‌ای که قراره آیتم‌های فرزند رو اضافه کنم و نمایش بدم، این id رو به عنوان
    parentId برای گره فرزند (todoی فرزند) قرار دادم و ذخیره می‌کنم...

    ولی وقتی که روی آیتم پدر که کلیک می‌کنم تا بره صفحه بعد و todoهای فرزندش رو نشون بده، به مشکل برخوردم و دقیقا تمام آیتم هایی که وارد کردم رو نشون میده، نه todoهای فرزندش رو...
    و صفحه اول برنامه که فقط قراره todoهای پدر رو نشون بده، متاسفانه تمام todoها رو نشون میده...

    که فکر کنم به خاطر این هستش که در ابتدا فیلدی به نام
    var parentId: Long ?=0
    رو داخل فراگمنت
    تعریف کردم و با کلیک بر روی هر آیتم، وقتی میره تا فراگمنت فرزند رو نشون بده، میره و متد oncreteView رو که لود کنه، باز همون parentId پدر رو که در ابتدا مقدار صفر رو بهش دادم رو به viewModel میده و همه آیتم هایی که می‌خوام وارد کنم رو با همین parentId ذخیره می‌کنه و برای همینم هست که همه رو با parentId صفر ذخیره می‌کنه و و داخل ریسایکلر ویو نشون میده...

    که از اینجا به بعد رو واقعا تو پیاده سازیش به مشکل خوردم و واقعا نمی‌دونم چه جوری باید حلش کنم که در هر صفحه فرزند بتونم لیست فرزندان رو داشته باشم و ذخیره کنم

    تنها چیزی که می‌دونم اینه که بایستی از دیزاین پترن Composite استفاده کنم ولی نمی‌دونم چجوری و به چه شکلی...
    به نظرتون باید یه آداپتر جدا برای لیست پدر و یه آداپتر دیگه برای لیست فرزندان بسازم...

    که می‌خواستم ازتون عاجزانه خواهش کنم که میشه کمی راهنماییم کنین...

    و در پایان هم ازتون بسیار ممنون و سپاسگزارم که زمانی رو برای خوندن این نوشته اختصاص دادین...
    ممنون و سپاسگزارم ازتون...





    آخرین ویرایش به وسیله amirreza_dq : یک شنبه 02 شهریور 1399 در 19:26 عصر

  2. #2
    کاربر دائمی آواتار mehdi.safavie
    تاریخ عضویت
    دی 1388
    محل زندگی
    تهران - اندیشه
    سن
    32
    پست
    219

    نقل قول: مشکل در نمایش سلسه مراتب درختی با زبان کاتلین

    درود;

    امیر رضا جان، اول باید کد آداپتر اللخصوص کدی که روی آیتم میزنی رو بفرستی تا ببینیم چکار کردی.
    چون چیزی که بهش نیاز داری انقدر ها که براش توضیح دادی پیچیده نیست.

    اما، یه راهنمایی ساده میگم، اگر جواب نداد باید مارو با کد روشن کنی چه کردی، و چه اتفاقی افتاده، و نیاز داری چه انجام بدی.
    زمانی که Fragment رو Load میکنی تو OnCreateView همونطور که گفتی ، متغییر
    parentId رو 0 کن. اما بعد از این که هر آیتمی رو Touch کردی، نباید به هیچ وجه رویداد OnCreateView اتفاق بیافته، اگر داره اینطور میشه داری اشتباه میری، تنها باید لیست و آداپتر فیلتر بشه یه لیستی جدید.
    حالا این که لیست رو چطور جدید میکنی هم، اول آیدی اون آیتمی که روش Touch کردی رو باید بگیری تحت عنوان
    parentId جستجو بزنی و لیست Child ها رو بگیری. در نهایت لیستی که بدست اومده رو بدی به آداپتر و NotifyChange بزنی به آداپترت.
    اینطوری فقط داری لیست رو Refresh میکنی با محتویات جدید. حالا این که OnCreateView داره دوباره اتفاق میافته، یک جای کار شما به جای Refresh کردن لیست، داری Fragment رو دستکاری میکنی.

    اگر اشتباه متوجه شدم، کد هایی که گفتم رو بفرستین ببینیم چی شده دقیقا.

  3. #3
    کاربر دائمی آواتار mehdi.safavie
    تاریخ عضویت
    دی 1388
    محل زندگی
    تهران - اندیشه
    سن
    32
    پست
    219

    نقل قول: مشکل در نمایش سلسه مراتب درختی با زبان کاتلین

    برای برگشت هم باید به گزینه برگشت روی گوشی کد بدی.
    برای اینکار باید مکانیزم اون گزینه رو False کنی.
    ببین یه راهش اینیه که گفتم، که مکانیزمش رو False کنی و اگر کاربر برگشت رو زد برگرده و parentId از parentId رو بگیره و لیست رو نمایش بده. ( یعنی ردیف اول که parentId 0 دارن رو روش Touch کردی، و در ردیف دومی هستی که همه parentId مثلا 1 رو دارن، اینجا اگر کاربر برگشت رو زد، باید اول parentId رو بگیره، تا به ردیف اول و آیدی 1 برسه، همچنان باید دوباره parentId رو بگیره که 0 هست، حالا لیست رو بر اساس parentId های 0 دوباره تولید کنه ).

    همچنین، برای اینکه کاربر بتونه خروج هم داشته باشه، یا نهایت به یه Fragment دیگه برگشت کنه، باید از مکانیزم برای برگشت 2 بار از این گزینه استفاده کنید پیاده کنی.


    @Override
    public boolean handleOnBackPressed() {
    //Do your job here
    return true;
    }



    اون return اگر true باشه، کار اصلی خودش رو انجام میده، اگر false بزاری، هرچ کاری نمیکنه و فقط کد شما رو اجرا میکنه.

  4. #4
    کاربر جدید
    تاریخ عضویت
    دی 1392
    محل زندگی
    قزوین
    پست
    15

    نقل قول: مشکل در نمایش سلسه مراتب درختی با زبان کاتلین

    نقل قول نوشته شده توسط mehdi.safavie مشاهده تاپیک
    درود;

    امیر رضا جان، اول باید کد آداپتر اللخصوص کدی که روی آیتم میزنی رو بفرستی تا ببینیم چکار کردی.
    چون چیزی که بهش نیاز داری انقدر ها که براش توضیح دادی پیچیده نیست.

    اما، یه راهنمایی ساده میگم، اگر جواب نداد باید مارو با کد روشن کنی چه کردی، و چه اتفاقی افتاده، و نیاز داری چه انجام بدی.
    زمانی که Fragment رو Load میکنی تو OnCreateView همونطور که گفتی ، متغییر
    parentId رو 0 کن. اما بعد از این که هر آیتمی رو Touch کردی، نباید به هیچ وجه رویداد OnCreateView اتفاق بیافته، اگر داره اینطور میشه داری اشتباه میری، تنها باید لیست و آداپتر فیلتر بشه یه لیستی جدید.
    حالا این که لیست رو چطور جدید میکنی هم، اول آیدی اون آیتمی که روش Touch کردی رو باید بگیری تحت عنوان
    parentId جستجو بزنی و لیست Child ها رو بگیری. در نهایت لیستی که بدست اومده رو بدی به آداپتر و NotifyChange بزنی به آداپترت.
    اینطوری فقط داری لیست رو Refresh میکنی با محتویات جدید. حالا این که OnCreateView داره دوباره اتفاق میافته، یک جای کار شما به جای Refresh کردن لیست، داری Fragment رو دستکاری میکنی.

    اگر اشتباه متوجه شدم، کد هایی که گفتم رو بفرستین ببینیم چی شده دقیقا.

    درود جناب صفوی...
    ممنون که لطف کردین و این پست رو خوندین و جواب دادین...

    واقعا ازتون ممنونم...

    این کدهایی هستش که زدم و اگه خیلی مبتدیانه و پر اشتباه هستش پیشاپیش عذر می‌خوام ...

    این کلاس آداپترم هستش:

    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import androidx.recyclerview.widget.RecyclerView
    import com.example.mytodoapp.R
    import com.example.mytodoapp.data.db.models.TodoItem
    import com.example.mytodoapp.ui.todolist.OnClickRecyclerL istener
    import com.example.mytodoapp.ui.todolist.TodoViewModel
    import kotlinx.android.synthetic.main.todo_recycler_item. view.*

    class TodoRecyclerViewItemAdapter(
    var items: List<TodoItem>,
    private val viewModel: TodoViewModel

    ) : RecyclerView.Adapter<TodoRecyclerViewItemAdapter.T odoViewHolder>() {

    private lateinit var onClickRecyclerListener: OnClickRecyclerListener

    fun setOnClickRecyclerListener(onClickRecyclerListener : OnClickRecyclerListener) {
    this.onClickRecyclerListener = onClickRecyclerListener
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
    val view = LayoutInflater
    .from(parent.context)
    .inflate(R.layout.todo_recycler_item, parent, false)

    return TodoViewHolder(view)
    }

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

    override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
    val todoItem = items[position]

    holder.itemView.todoTextView.text = todoItem.todoItem

    holder.itemView.setOnClickListener(View.OnClickLis tener {
    val parenId = items.get(position)
    onClickRecyclerListener.onRecyclerItemClicked(pare nId)
    })

    holder.itemView.todoItemDeleteImageButton.setOnCli ckListener {
    viewModel.delete(todoItem)
    }

    }

    inner class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)


    }



    این هم اینترفیس برای رویداد کلیک ریسایکلر ویو

    interface OnClickRecyclerListener {

    fun onRecyclerItemClicked(todoItem: TodoItem)

    }



    اینترفیس مربوط به رویداد کلیک بر روی fab ونمایش یک دیالوگ واضافه کردن آیتم به ریسایکلر ویو:


    interface AddDialogListener {

    fun onAddButtonClicked(item: TodoItem)

    }



    کلاس مربوط به رویداد کلیک بر روی fab و اضافه کردن آیتم به ریسایکلر ویو:

    class AddTodoItemDialog(context: Context, var parentId: Long, var addDialogListener: AddDialogListener) :
    AppCompatDialog(context) {

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

    tvSaveTodo.setOnClickListener {
    val todo = etInputTodo.text.toString()

    if (todo.isEmpty()) {
    Toast.makeText(context, "Please Enter Your Todo!", Toast.LENGTH_SHORT).show()
    return@setOnClickListener
    }

    val item = TodoItem(todo, parentId)
    addDialogListener.onAddButtonClicked(item)
    dismiss()
    }

    tvCancel.setOnClickListener {
    cancel()
    }
    }
    }



    و در انتها این هم کلاس فراگمنتم هستش:


    import android.os.Bundle
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.Toast
    import androidx.core.os.bundleOf
    import androidx.fragment.app.Fragment
    import androidx.lifecycle.Observer
    import androidx.lifecycle.ViewModelProvider
    import androidx.navigation.fragment.NavHostFragment
    import androidx.navigation.fragment.findNavController
    import androidx.recyclerview.widget.LinearLayoutManager
    import com.example.mytodoapp.R
    import com.example.mytodoapp.data.db.TodoDatabase
    import com.example.mytodoapp.data.db.models.TodoItem
    import com.example.mytodoapp.data.repositories.TodoReposi tory
    import com.example.mytodoapp.other.TodoRecyclerViewItemAd apter
    import kotlinx.android.synthetic.main.fragment_todo.*


    class TodoFragment : Fragment() {

    var parentId: Long ?=0

    override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
    ): View? {
    return inflater.inflate(R.layout.fragment_todo, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val database = TodoDatabase(requireContext())
    val repository = TodoRepository(database)
    val factory = TodoViewModelFactory(repository)

    val viewModel = ViewModelProvider(this, factory).get(TodoViewModel::class.java)

    val adapter = TodoRecyclerViewItemAdapter(listOf(), viewModel)
    todoRecyclerView.layoutManager = LinearLayoutManager(requireContext())
    todoRecyclerView.adapter = adapter

    parentId?.let {
    viewModel.getChildTodoItems(it).observe(viewLifecy cleOwner, Observer {
    adapter.items = it
    adapter.notifyDataSetChanged()
    })
    }

    fabAddTodoItem.setOnClickListener {
    parentId?.let { it1 ->
    AddTodoItemDialog(requireContext(), it1, object : AddDialogListener {
    override fun onAddButtonClicked(item: TodoItem) {
    viewModel.upsert(item)
    Toast.makeText(context, "Parent id is: $parentId", Toast.LENGTH_SHORT).show()
    }
    }).show()
    }
    }
    adapter.setOnClickRecyclerListener(object : OnClickRecyclerListener {
    override fun onRecyclerItemClicked(todoItem: TodoItem) {
    parentId = todoItem.id
    Toast.makeText(context, "parent id is:: $parentId", Toast.LENGTH_SHORT).show()
    findNavController().navigate(
    R.id.action_todoFragment_self
    )

    parentId?.let {
    viewModel.getChildTodoItems(it).observe(viewLifecy cleOwner, Observer {
    adapter.items = it
    adapter.notifyDataSetChanged()
    })
    }
    }

    })

    }
    }




    و باز هم ازتون ممنونم که وقت گزاشتین و به سوام جواب دادین...
    خیلی خیلی ازتون ممنونم...
    دستون درد نکنه جناب صفوی...
    آخرین ویرایش به وسیله amirreza_dq : دوشنبه 03 شهریور 1399 در 12:18 عصر

  5. #5
    کاربر دائمی آواتار mehdi.safavie
    تاریخ عضویت
    دی 1388
    محل زندگی
    تهران - اندیشه
    سن
    32
    پست
    219

    نقل قول: مشکل در نمایش سلسه مراتب درختی با زبان کاتلین

    1. در onViewCreated از TodoFragment باید لیستی از تمام رکورد هایی که parentId برابر با 0 دارن رو بگیرین و به adapter بدین.
    2. داخل adapter در قسمت onCreateViewHolder باید متدی تعریف کنید که با Touch روی هر آیتم، این متد فراخوانی بشه. برای اینکار راه های مختلفی هست، یه راهش استفاده از Extension هست.
    میتونین همچین چیزی بنویسین:

    fun <T : RecyclerView.ViewHolder> T.listen(event: (position: Int, type: Int) -> Unit): T {
    itemView.setOnClickListener {
    event.invoke(getAdapterPosition(), getItemViewType())
    }
    return this
    }

    و از اون تو adapter استفاده کنین.
    در قسمتی که گفتم میتونین اینطوری ازش استفاده کنین:

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyViewHolder? {
    val inflater = LayoutInflater.from(parent!!.getContext())
    val view = inflater.inflate(R.layout.item_view, parent, false)
    return MyViewHolder(view).listen { pos, type ->
    val item = items.get(pos)
    //TODO do other stuff here
    }
    }

    بسیار خب؛ شما در قسمت return تمام item رو در اختیار دارین. یعنی میتونین به راحتی به فیلد های آبجکت خودتون دسترسی داشته باشین ( در صورتی که فیلد در رکورد مور نظر فقط در اختیارتون هست و این کار از طریق pos برای شما امکان پذیر شده ). دقیقا در همین قسمت شما میبایست لیست adapter رو به روزرسانی کنین. یعنی اینکه با توجه به Id که از item میگیرین، یک کوئری به دیتابیس بزنین و تمام child هایی که parentId اونها برابر با id بدست اومده هست رو لیست کنین.
    لیست بدست اومده رو بدین به adapter. که در اصل باید این 3 خط کد رو بنویسین:

    items.clear();
    items.addAll(newList);
    notifyDataSetChanged();



    ---------------------------------------------
    اون extension که براتون گذاشتم فقط یه مثاله، درگیرش نشین، فقط کافیه تو اون قسمت از adapter بتونین OnClickListener رو کنترل کنین.
    شما خودتون تو این خط که نوشتین این کارو کردین:

    adapter.setOnClickRecyclerListener(object : OnClickRecyclerListener {

    تنها کاری که باید بکنین اینه که لیست adapter رو دوباره با اطلاعات جدید پر کنین.

  6. #6
    کاربر جدید
    تاریخ عضویت
    دی 1392
    محل زندگی
    قزوین
    پست
    15

    نقل قول: مشکل در نمایش سلسه مراتب درختی با زبان کاتلین

    نقل قول نوشته شده توسط mehdi.safavie مشاهده تاپیک
    1. در onViewCreated از TodoFragment باید لیستی از تمام رکورد هایی که parentId برابر با 0 دارن رو بگیرین و به adapter بدین.
    2. داخل adapter در قسمت onCreateViewHolder باید متدی تعریف کنید که با Touch روی هر آیتم، این متد فراخوانی بشه. برای اینکار راه های مختلفی هست، یه راهش استفاده از Extension هست.
    میتونین همچین چیزی بنویسین:

    fun <T : RecyclerView.ViewHolder> T.listen(event: (position: Int, type: Int) -> Unit): T {
    itemView.setOnClickListener {
    event.invoke(getAdapterPosition(), getItemViewType())
    }
    return this
    }

    و از اون تو adapter استفاده کنین.
    در قسمتی که گفتم میتونین اینطوری ازش استفاده کنین:

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyViewHolder? {
    val inflater = LayoutInflater.from(parent!!.getContext())
    val view = inflater.inflate(R.layout.item_view, parent, false)
    return MyViewHolder(view).listen { pos, type ->
    val item = items.get(pos)
    //TODO do other stuff here
    }
    }

    بسیار خب؛ شما در قسمت return تمام item رو در اختیار دارین. یعنی میتونین به راحتی به فیلد های آبجکت خودتون دسترسی داشته باشین ( در صورتی که فیلد در رکورد مور نظر فقط در اختیارتون هست و این کار از طریق pos برای شما امکان پذیر شده ). دقیقا در همین قسمت شما میبایست لیست adapter رو به روزرسانی کنین. یعنی اینکه با توجه به Id که از item میگیرین، یک کوئری به دیتابیس بزنین و تمام child هایی که parentId اونها برابر با id بدست اومده هست رو لیست کنین.
    لیست بدست اومده رو بدین به adapter. که در اصل باید این 3 خط کد رو بنویسین:

    items.clear();
    items.addAll(newList);
    notifyDataSetChanged();



    ---------------------------------------------
    اون extension که براتون گذاشتم فقط یه مثاله، درگیرش نشین، فقط کافیه تو اون قسمت از adapter بتونین OnClickListener رو کنترل کنین.
    شما خودتون تو این خط که نوشتین این کارو کردین:

    adapter.setOnClickRecyclerListener(object : OnClickRecyclerListener {

    تنها کاری که باید بکنین اینه که لیست adapter رو دوباره با اطلاعات جدید پر کنین.

    واقعا ازتون بینهایت ممنون و سپاسگزارم که تا این راخنماییم کردین...

    خیلی خیلی ازتون ممنونم...
    واقعا دستون درد نکنه...

    ممنونم ازتون... همین الان انجامش میدم...

    بازم ممنونم ازتون...

تاپیک های مشابه

  1. پاسخ: 14
    آخرین پست: جمعه 30 آبان 1399, 21:08 عصر
  2. سوال: استفاده از لینک و ان تی تی فریم ورک با بانک اطلاعاتی اکسس
    نوشته شده توسط jmfnima در بخش C#‎‎
    پاسخ: 9
    آخرین پست: چهارشنبه 06 آذر 1392, 12:12 عصر
  3. سوال: استفاده از لینک و ان تی تی فریم ورک با بانک اطلاعاتی اکسس
    نوشته شده توسط jmfnima در بخش دسترسی به داده ها (ADO.Net و LINQ و ...)
    پاسخ: 0
    آخرین پست: چهارشنبه 29 آبان 1392, 17:07 عصر
  4. پاسخ: 1
    آخرین پست: دوشنبه 28 مرداد 1392, 11:59 صبح
  5. سوال: توابع مثلثاتی و معکوس مثلثاتی ( تبدیل مختصات دکارتی به قطبی)
    نوشته شده توسط mahak006 در بخش برنامه نویسی با زبان C و ++C
    پاسخ: 7
    آخرین پست: شنبه 25 آذر 1391, 02:47 صبح

برچسب های این تاپیک

قوانین ایجاد تاپیک در تالار

  • شما نمی توانید تاپیک جدید ایجاد کنید
  • شما نمی توانید به تاپیک ها پاسخ دهید
  • شما نمی توانید ضمیمه ارسال کنید
  • شما نمی توانید پاسخ هایتان را ویرایش کنید
  •