最近我了解了一个像Tinder一样提供滑动功能的库,库名是Yalantis/Koloda-Android。但是,当我使用它时,我发现一些方法回调被错误地放置了,因此,我对其进行了更正,但是现在的主要问题是有一种方法可以重新加载最后一张刷卡。方法名称是“ koloda.reloadPreviousCard()”,但是该方法不在代码中,而是在Github的文档中提到。我试图与该库的作者联系,但没有任何回应,我在Github上打开了一个问题,并通过电子邮件与他们联系,在Github上提到了将近一个月的电子邮件。
如果任何人都遇到过相同的问题并已解决,请在此处分享解决方案。
我正在将代码发布到需要回调方法的位置,如果您需要更多信息,请告诉我。
Koloda.kt:
class Koloda : FrameLayout {
companion object {
private const val DEFAULT_MAX_VISIBLE_CARDS = 3
private const val DEFAULT_ROTATION_ANGLE = 30f
private const val DEFAULT_SCALE_DIFF = 0.04f
}
private var maxVisibleCards = DEFAULT_MAX_VISIBLE_CARDS
private var cardPositionOffsetX = resources.getDimensionPixelSize(R.dimen.default_card_spacing)
private var cardPositionOffsetY = resources.getDimensionPixelSize(R.dimen.default_card_spacing)
private var cardRotationDegrees = DEFAULT_ROTATION_ANGLE
private var dyingViews = hashSetOf<View>()
private var activeViews = linkedSetOf<View>()
private var dataSetObservable: DataSetObserver? = null
var isNeedCircleLoading = false
var adapter: Adapter? = null
set(value) {
dataSetObservable?.let { field?.unregisterDataSetObserver(it) }
removeAllViews()
field = value
field?.registerDataSetObserver(createDataSetObserver())
dataSetObservable?.onChanged()
}
private var adapterPosition = 0
private var deckMap = hashMapOf<View, CardOperator>()
var kolodaListener: KolodaListener? = null
internal var parentWidth = 0
private var swipeEnabled = true
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(attrs)
}
private fun init(attrs: AttributeSet?) {
val a = context.obtainStyledAttributes(attrs, R.styleable.Koloda)
val cardLayoutId = a.getResourceId(R.styleable.Koloda_koloda_card_layout, -1)
maxVisibleCards = a.getInt(R.styleable.Koloda_koloda_max_visible_cards, DEFAULT_MAX_VISIBLE_CARDS)
cardPositionOffsetX = a.getDimensionPixelSize(R.styleable.Koloda_koloda_card_offsetX, resources.getDimensionPixelSize(R.dimen.default_card_spacing))
cardPositionOffsetY = a.getDimensionPixelSize(R.styleable.Koloda_koloda_card_offsetY, resources.getDimensionPixelSize(R.dimen.default_card_spacing))
cardRotationDegrees = a.getFloat(R.styleable.Koloda_koloda_card_rotate_angle, DEFAULT_ROTATION_ANGLE)
a.recycle()
if (isInEditMode) {
LayoutInflater.from(context).inflate(cardLayoutId, this, true)
}
}
/**
* Add card to visible stack
*/
private fun addCardToDeck() {
if (adapterPosition < adapter?.count ?: 0) {
val newCard = adapter?.getView(adapterPosition, null, this)
initializeCardPosition(newCard)
newCard?.let {
addView(it, 0)
deckMap.put(it, CardOperator(this, it, adapterPosition++, cardCallback))
}
} else if (isNeedCircleLoading) {
adapterPosition = 0
addCardToDeck()
}
}
/**
* Set up card offset
* @param view - new card
*/
private fun initializeCardPosition(view: View?) {
val childCount = childCount - dyingViews.size
scaleView(view, 0f, childCount)
view?.translationY = (cardPositionOffsetY * childCount).toFloat()
/*-Log.i("----> elem init", childCount.toString())
Log.i("----> translation init", view?.translationY.toString())*/
setZTranslations(childCount)
}
private fun checkTopCard() {
val childCount = childCount
setZTranslations(childCount)
if (childCount - 1 - dyingViews.size < 0) {
kolodaListener?.onEmptyDeck()
} else {
val topCard = getChildAt(childCount - 1 - dyingViews.size)
if (deckMap.containsKey(topCard)) {
deckMap[topCard]?.let { kolodaListener?.onNewTopCard(it.adapterPosition) }
}
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun setZTranslations(childCount: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
(0 until childCount).map {
getChildAt(it).translationZ = (it * 10).toFloat()
}
}
}
fun getMaxCardWidth(cardView: View): Float = cardView.height * Math.tan(Math.toRadians(cardRotationDegrees.toDouble())).toFloat()
fun canSwipe(card: View): Boolean {
/*-Log.e("====>Swipe ", swipeEnabled.toString())
Log.e("====>Active viewsempty ", activeViews.isEmpty().toString())
Log.e("====>Or contains ", (activeViews.contains(card)).toString())
Log.e("====>Index ", (indexOfChild(card) >= childCount - 2).toString())*/
return (swipeEnabled && (activeViews.isEmpty() || activeViews.contains(card))
&& indexOfChild(card) >= childCount - 2)
}
private fun updateDeckCardsPosition(progress: Float) {
val visibleChildCount = Math.min(childCount, maxVisibleCards + 1)
var childCount = Math.min(childCount, maxVisibleCards)
var cardsWillBeMoved = 0
var cardView: View
(0 until visibleChildCount).map {
cardView = getChildAt(it)
if (deckMap.containsKey(cardView) && deckMap[cardView]?.isBeingDragged != true) {
cardsWillBeMoved++
}
scaleView(cardView, progress, childCount - it - 1)
}
if (progress != 0.0f) {
for (i in 0 until cardsWillBeMoved) {
cardView = getChildAt(i)
cardView.translationY = (cardPositionOffsetY * Math.min(cardsWillBeMoved, visibleChildCount - i - 1) - cardPositionOffsetY * Math.abs(progress))
}
}
}
private fun scaleView(view: View?, progress: Float, childCount: Int) {
val currentScale = 1f - (childCount * DEFAULT_SCALE_DIFF)
val nextScale = 1f - ((childCount - 1) * DEFAULT_SCALE_DIFF)
val scale = currentScale + (nextScale - currentScale) * Math.abs(progress)
if (scale <= 1f) {
view?.scaleX = scale
view?.scaleY = scale
}
}
private fun updateCardView(card: View, sideProgress: Float) {
val rotation = cardRotationDegrees.toInt() * sideProgress
card.rotation = rotation
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if (changed) {
dataSetObservable?.onChanged()
}
}
override fun addView(child: View, index: Int) {
child.alpha = 0f
super.addView(child, index)
child.animate().alpha(1f).duration = 300
}
override fun removeAllViews() {
super.removeAllViews()
deckMap.clear()
activeViews.clear()
dyingViews.clear()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
parentWidth = (this.parent as View).measuredWidth
}
private fun createDataSetObserver(): DataSetObserver {
dataSetObservable = object : DataSetObserver() {
override fun onChanged() {
super.onChanged()
addCards()
}
override fun onInvalidated() {
super.onInvalidated()
adapterPosition = 0
removeAllViews()
addCards()
}
private fun addCards() {
val childCount = childCount - dyingViews.size
for (i in childCount until maxVisibleCards) {
addCardToDeck()
}
checkTopCard()
}
}
return dataSetObservable as DataSetObserver
}
private var cardCallback: CardCallback = object : CardCallback {
override fun onCardActionDown(adapterPosition: Int, card: View) {
activeViews.add(card)
}
override fun onCardDrag(adapterPosition: Int, card: View, sideProgress: Float) {
updateCardView(card, sideProgress)
kolodaListener?.onCardDrag(adapterPosition, card, sideProgress)
}
override fun onCardOffset(adapterPosition: Int, card: View, offsetProgress: Float) {
updateDeckCardsPosition(offsetProgress)
}
override fun onCardActionUp(adapterPosition: Int, card: View, isCardNeedRemove: Boolean) {
if (isCardNeedRemove) {
activeViews.remove(card)
}
}
override fun onCardSwipedLeft(adapterPosition: Int, card: View, notify: Boolean) {
dyingViews.add(card)
dataSetObservable?.onChanged()
if (notify) {
kolodaListener?.onCardSwipedLeft(adapterPosition)
}
}
override fun onCardSwipedRight(adapterPosition: Int, card: View, notify: Boolean) {
dyingViews.add(card)
dataSetObservable?.onChanged()
if (notify) {
kolodaListener?.onCardSwipedRight(adapterPosition)
}
}
override fun onCardOffScreen(adapterPosition: Int, card: View) {
dyingViews.remove(card)
deckMap.remove(card)
removeView(card)
}
override fun onCardSingleTap(adapterPosition: Int, card: View) {
kolodaListener?.onCardSingleTap(adapterPosition)
}
override fun onCardDoubleTap(adapterPosition: Int, card: View) {
kolodaListener?.onCardDoubleTap(adapterPosition)
}
override fun onCardLongPress(adapterPosition: Int, card: View) {
kolodaListener?.onCardLongPress(adapterPosition)
}
override fun onCardMovedOnClickRight(adapterPosition: Int, card: View, notify: Boolean) {
activeViews.remove(card)
dyingViews.add(card)
dataSetObservable?.onChanged()
findPositionAfterClick()
if (notify) {
kolodaListener?.onClickRight(adapterPosition)
}
}
override fun onCardMovedOnClickLeft(adapterPosition: Int, card: View, notify: Boolean) {
activeViews.remove(card)
dyingViews.add(card)
dataSetObservable?.onChanged()
findPositionAfterClick()
if (notify) {
kolodaListener?.onClickLeft(adapterPosition)
}
}
}
fun onClickRight() {
val childCount = childCount
val topCard = getChildAt(childCount - 1 - dyingViews.size)
topCard?.let {
activeViews.add(it)
val cardOperator: CardOperator? = deckMap[it]
cardOperator?.onClickRight()
it.rotation = 10f
}
}
fun onClickLeft() {
val childCount = childCount
val topCard = getChildAt(childCount - 1 - dyingViews.size)
topCard?.let {
activeViews.add(it)
val cardOperator: CardOperator? = deckMap[it]
cardOperator?.onClickLeft()
it.rotation = -10f
}
}
private fun findPositionAfterClick() {
var childCount = Math.min(childCount, maxVisibleCards)
(0 until childCount).map {
val view = getChildAt(it)
scaleView(view, 0f, childCount - it - 1)
view?.translationY = (cardPositionOffsetY * (childCount - it - 1)).toFloat()
}
}
}