Last active
January 17, 2018 09:34
-
-
Save oligazar/021e64f181c8e56605a669ce06534498 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private var listener: ChildEventListener? = null | |
override fun onStart() { | |
super.onStart() | |
Log.d("OffersFragment", "onStart\n") | |
listener = mDbRef.addChildEventListener(object : ChildEventListener { | |
override fun onChildAdded(snap: DataSnapshot, previousName: String?) { | |
snap.getValue(Category::class.java)?.let { category -> | |
category.key = snap.key | |
Log.d("OffersFragment", "onChildAdded, category: $category") | |
if (categories.contains(category)) { | |
val index = categories.indexOf(category) | |
categories[index] = category | |
mAdapter.notifyItemChanged(index) | |
return | |
} | |
val index = categories.indices | |
.firstOrNull { categories[it].key == previousName } | |
?.let { it + 1 } ?: 0 | |
categories.add(index, category) | |
mAdapter.notifyItemInserted(index) | |
} | |
} | |
override fun onCancelled(p0: DatabaseError?) { } | |
override fun onChildMoved(snap: DataSnapshot?, previousName: String?) { | |
snap?.getValue(Category::class.java)?.let { category -> | |
Log.d("OffersFragment", "onChildMoved") | |
categories.indices.forEach { Log.d("OffersFragment", "onChildMoved, categories[$it]: ${categories[it]}") } | |
Log.d("OffersFragment", "onChildMoved") | |
category.key = snap.key | |
Log.d("OffersFragment", "onChildMoved, category: $category") | |
val prevPos = categories.indexOf(category) | |
Log.d("OffersFragment", "onChildMoved, oldSort: ${categories[prevPos].sort}, newSort: ${category.sort}") | |
if (categories[prevPos].sort == category.sort) { | |
Log.d("OffersFragment", "onChildMoved, return") | |
return | |
} | |
val newPos = if (previousName == null) 0 | |
else categories | |
.indexOf(categories.first { it.key == previousName } ) | |
.let { newPos -> if (newPos > prevPos) newPos else newPos + 1 } | |
Collections.swap(categories, prevPos, newPos) | |
val catA = categories[prevPos] | |
val catB = categories[newPos] | |
val temp = catA.sort | |
catA.sort = catB.sort | |
catB.sort = temp | |
mAdapter.notifyItemMoved(prevPos, newPos) | |
Log.d("OffersFragment", "onChildMoved, key: ${snap.key}, previous: $previousName") | |
Log.d("OffersFragment", "onChildMoved, previousPos: $prevPos, newPos: $newPos") | |
Log.d("OffersFragment", "onChildMoved, catA: ${categories[newPos]}") | |
Log.d("OffersFragment", "onChildMoved, catB: ${categories[prevPos]}") | |
} | |
} | |
override fun onChildChanged(snap: DataSnapshot, p1: String?) { | |
Log.d("OffersFragment", "onChildChanged") | |
snap.getValue(Category::class.java)?.let { category -> | |
category.key = snap.key | |
Log.d("OffersFragment", "onChildChanged, category: $category") | |
if (isDragging) return | |
if (categories.contains(category)) { | |
val position = categories.indexOf(category) | |
categories[position] = category | |
mAdapter.notifyItemChanged(position) | |
} | |
} | |
} | |
override fun onChildRemoved(snap: DataSnapshot?) { | |
snap?.getValue(Category::class.java)?.let { category -> | |
Log.d("OffersFragment", "onChildRemoved, category: $category") | |
val position = categories.indexOf(category) | |
categories.removeAt(position) | |
mAdapter.notifyItemRemoved(position) | |
} | |
} | |
}) | |
} | |
override fun onStop() { | |
super.onStop() | |
mDbRef.removeEventListener(listener) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class OffersCategoriesAdapter(private val dragListener: OnStartDragListener, | |
private val categories: ArrayList<Category>, | |
private var user: User?, | |
private val editHandler: (Category) -> Unit, | |
private val clickHandler: (Category) -> Unit) | |
: RecyclerView.Adapter<CategoryVH>(), ItemTouchHelperAdapter, OnUserChangedListener { | |
private val mDb = FirebaseDatabase.getInstance() | |
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean { | |
Log.d("OffersCategoriesFrag", "adapter.onItemMove from: $fromPosition, to: $toPosition") | |
Collections.swap(categories, fromPosition, toPosition) | |
swapSortPosition(categories, fromPosition, toPosition) | |
notifyItemMoved(fromPosition, toPosition) | |
return true | |
} | |
private fun swapSortPosition(categories: ArrayList<Category>, indA: Int, indB: Int) { | |
val catA = categories[indA] | |
val catB = categories[indB] | |
val temp = catA.sort | |
catA.sort = catB.sort | |
catB.sort = temp | |
val updates = mapOf( | |
Pair("categories/offers/${catA.key}/sort", catA.sort), | |
Pair("categories/offers/${catB.key}/sort", catB.sort)) | |
mDb.reference.updateChildren(updates) | |
Log.d("OffersCategoriesFrag", "adapter.swapSortPosition, catA: $catA, catB: $catB") | |
} | |
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryVH { | |
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_category, parent, false) | |
return CategoryVH(view) | |
} | |
override fun onBindViewHolder(holder: CategoryVH, position: Int) { | |
val category = categories[position] | |
Glide.with(holder.itemView.context) | |
.load(category.picPath) | |
.apply(RequestOptions() | |
.centerCrop() | |
/*.placeholder(R.drawable.ic_remove)*/) | |
.transition(DrawableTransitionOptions.withCrossFade()) | |
.into(holder.ivPicture) | |
holder.tvName.text = category.name | |
holder.btnEdit.setOnClickListener { editHandler(category) } | |
holder.itemView.setOnClickListener { clickHandler(category) } | |
holder.dragHandle.setOnTouchListener { _, event -> | |
when (event.action) { | |
MotionEvent.ACTION_DOWN -> { | |
dragListener.onStartDrag(holder) | |
} | |
} | |
false | |
} | |
setupUser(holder, user) | |
} | |
override fun onUserChanged(user: User) { | |
this.user = user | |
notifyDataSetChanged() | |
} | |
private fun setupUser(holder: CategoryVH, user: User?) { | |
when (user?.role) { | |
UserRole.Partner.toString() -> { | |
holder.btnEdit.visibility = View.GONE | |
holder.dragHandle.visibility = View.GONE | |
} | |
UserRole.Admin.toString() -> { | |
holder.btnEdit.visibility = View.VISIBLE | |
holder.dragHandle.visibility = View.VISIBLE | |
} | |
else -> { | |
holder.btnEdit.visibility = View.GONE | |
holder.dragHandle.visibility = View.GONE | |
} | |
} | |
} | |
override fun getItemCount() = categories.size | |
} | |
class CategoryVH(view: View) : RecyclerView.ViewHolder(view), ItemTouchHelperViewHolder { | |
val ivPicture = view.findViewById<View>(R.id.ivPicture) as ImageView | |
val btnEdit = view.findViewById<View>(R.id.btnEdit) as ImageButton | |
val tvName = view.findViewById<View>(R.id.tvName) as TextView | |
val dragHandle = view.findViewById<View>(R.id.dragHandle) as ImageView | |
override fun onItemSelected() { | |
ivPicture.run { | |
setPadding(dp(-4)) | |
} | |
} | |
override fun onItemClear() { | |
ivPicture.run { | |
setPadding(0) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class OffersCategoriesFragment : Fragment(), OnStartDragListener, OnUserChangedListener { | |
private val _tag = OffersCategoriesFragment::class.java.simpleName | |
private val categories = ArrayList<Category>() | |
private lateinit var mAdapter: OffersCategoriesAdapter | |
private lateinit var mTouchHelper: ItemTouchHelper | |
private val createCategoryCode = 123 | |
private var fab: FloatingActionButton? = null | |
private val categoriesRef = FirebaseDatabase | |
.getInstance() | |
.getReference("categories") | |
.child("offers") | |
.orderByChild("sort") | |
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { | |
super.onCreateView(inflater, container, savedInstanceState) | |
return inflater.inflate(R.layout.fragment_offers, container, false) | |
} | |
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | |
super.onViewCreated(view, savedInstanceState) | |
val user = activity?.getPreferences()?.getUser() | |
mAdapter = OffersCategoriesAdapter(this, categories, user, { category -> | |
val intent = CreateCategoryActivity.updateCategoryIntent(activity, category.key) | |
startActivityForResult(intent, createCategoryCode) | |
}, { category -> | |
OffersMasterActivity.startMasterActivity(this, category.key) | |
}) | |
recycler.apply { | |
adapter = mAdapter | |
layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false) | |
setHasFixedSize(true) | |
val spacing = resources.getDimension(R.dimen.spacing_small).toInt() | |
addItemDecoration(VerticalSpaceItemDecoration(spacing, isDip = false)) | |
enableDragging() | |
} | |
fab = activity?.findViewById<View>(R.id.fab) as? FloatingActionButton | |
activity?.getPreferences()?.getUser()?.let { | |
Log.d(_tag, "onViewCreated, user: $it") | |
setupUser(it) | |
} | |
} | |
override fun onUserChanged(user: User) { | |
setupUser(user) | |
mAdapter.onUserChanged(user) | |
} | |
private fun setupUser(user: User) { | |
when (user.role) { | |
UserRole.User.toString() -> { | |
fab?.visibility = View.GONE | |
} | |
UserRole.Partner.toString() -> { | |
fab?.visibility = View.GONE | |
} | |
UserRole.Admin.toString() -> { | |
fab?.visibility = View.VISIBLE | |
fab?.setOnClickListener { | |
val newSort = if (categories.isEmpty()) .0 else categories.last().sort + 1 | |
val intent = CreateCategoryActivity.createCategoryIntent(activity, newSort) | |
startActivityForResult(intent, createCategoryCode) | |
} | |
} | |
} | |
} | |
/** OnStartDragListener callback. Notifies when recycler's item started dragging by handle */ | |
override fun onStartDrag(holder: RecyclerView.ViewHolder) { | |
mTouchHelper.startDrag(holder) | |
} | |
private fun RecyclerView.enableDragging() { | |
val callback = MyTouchHelperCallback(mAdapter) | |
mTouchHelper = ItemTouchHelper(callback) | |
mTouchHelper.attachToRecyclerView(this) | |
} | |
/** makes sure categories date is cleared just before new data comes from firebase */ | |
private var mClearCategories = false | |
private fun clearAllIfNeeded() { | |
if (mClearCategories) { | |
val size = categories.count() | |
categories.clear() | |
mAdapter.notifyItemRangeRemoved(0, size) | |
mClearCategories = false | |
} | |
} | |
private var listener: ChildEventListener? = null | |
override fun onStart() { | |
super.onStart() | |
Log.d("OffersCategoriesFrag", "onStart\n") | |
listener = categoriesRef.addChildEventListener(object : ChildEventListener { | |
override fun onChildAdded(snap: DataSnapshot, previousName: String?) { | |
snap.getValue(Category::class.java)?.let { category -> | |
clearAllIfNeeded() | |
category.key = snap.key | |
Log.d("OffersCategoriesFrag", "onChildAdded, category: $category") | |
val index = categories.indices | |
.firstOrNull { categories[it].key == previousName } | |
?.let { it + 1 } ?: 0 | |
categories.add(index, category) | |
mAdapter.notifyItemInserted(index) | |
} | |
} | |
override fun onCancelled(p0: DatabaseError?) { } | |
override fun onChildMoved(snap: DataSnapshot?, previousName: String?) { | |
snap?.getValue(Category::class.java)?.let { category -> | |
Log.d("OffersCategoriesFrag", "onChildMoved") | |
categories.indices.forEach { Log.d("OffersCategoriesFrag", "onChildMoved, categories[$it]: ${categories[it]}") } | |
Log.d("OffersCategoriesFrag", "onChildMoved") | |
category.key = snap.key | |
Log.d("OffersCategoriesFrag", "onChildMoved, category: $category") | |
val prevPos = categories.indexOf(category) | |
Log.d("OffersCategoriesFrag", "onChildMoved, oldSort: ${categories[prevPos].sort}, newSort: ${category.sort}") | |
if (categories[prevPos].sort == category.sort) { | |
Log.d("OffersCategoriesFrag", "onChildMoved, return") | |
return | |
} | |
val newPos = if (previousName == null) 0 | |
else categories | |
.indexOf(categories.first { it.key == previousName } ) | |
.let { newPos -> if (newPos > prevPos) newPos else newPos + 1 } | |
Collections.swap(categories, prevPos, newPos) | |
val catA = categories[prevPos] | |
val catB = categories[newPos] | |
val temp = catA.sort | |
catA.sort = catB.sort | |
catB.sort = temp | |
mAdapter.notifyItemMoved(prevPos, newPos) | |
Log.d("OffersCategoriesFrag", "onChildMoved, key: ${snap.key}, previous: $previousName") | |
Log.d("OffersCategoriesFrag", "onChildMoved, previousPos: $prevPos, newPos: $newPos") | |
Log.d("OffersCategoriesFrag", "onChildMoved, catA: ${categories[newPos]}") | |
Log.d("OffersCategoriesFrag", "onChildMoved, catB: ${categories[prevPos]}") | |
} | |
} | |
override fun onChildChanged(snap: DataSnapshot, p1: String?) { | |
Log.d("OffersCategoriesFrag", "onChildChanged") | |
snap.getValue(Category::class.java)?.let { category -> | |
category.key = snap.key | |
Log.d("OffersCategoriesFrag", "onChildChanged, category: $category") | |
if (isDragging) return | |
if (categories.contains(category)) { | |
val position = categories.indexOf(category) | |
categories[position] = category | |
mAdapter.notifyItemChanged(position) | |
} | |
} | |
} | |
override fun onChildRemoved(snap: DataSnapshot?) { | |
snap?.getValue(Category::class.java)?.let { category -> | |
category.key = snap.key | |
Log.d("OffersCategoriesFrag", "onChildRemoved, category: $category") | |
val position = categories.indexOf(category) | |
categories.removeAt(position) | |
mAdapter.notifyItemRemoved(position) | |
} | |
} | |
}) | |
} | |
override fun onStop() { | |
super.onStop() | |
Log.d("OffersCategoriesFrag", "onStop") | |
mClearCategories = true | |
categoriesRef.removeEventListener(listener) | |
} | |
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | |
if (requestCode == createCategoryCode) { | |
if (resultCode == RESULT_OK) { | |
val message = when (data?.extras?.getString(CreateCategoryActivity.ACTION_EXTRA_KEY)) { | |
"delete" -> "Категория успешно удалена" | |
else -> { | |
"Категория успешно сохранена" | |
} | |
} | |
Snackbar.make(root, message, Snackbar.LENGTH_SHORT).show() | |
} | |
} | |
} | |
/** restrict childEventListener from affecting adapter when firebase data updates */ | |
private var isDragging = false | |
inner class MyTouchHelperCallback(private val mAdapter: OffersCategoriesAdapter): ItemTouchHelper.Callback() { | |
override fun getMovementFlags(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder?): Int { | |
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN | |
return makeMovementFlags(dragFlags, 0) | |
} | |
override fun onMove(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { | |
mAdapter.onItemMove(viewHolder.adapterPosition, target.adapterPosition) | |
return true | |
} | |
override fun isLongPressDragEnabled() = false | |
override fun onSwiped(viewHolder: RecyclerView.ViewHolder?, direction: Int) { } | |
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { | |
// We only want the active item | |
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) { | |
(viewHolder as? ItemTouchHelperViewHolder)?.onItemSelected() | |
isDragging = true | |
} | |
super.onSelectedChanged(viewHolder, actionState) | |
} | |
override fun clearView(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder) { | |
super.clearView(recyclerView, viewHolder) | |
(viewHolder as? ItemTouchHelperViewHolder)?.onItemClear() | |
isDragging = false | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment