返回imageView旋转位置,如果在特定位置,则停止



希望有人能帮忙。我正在创建一个应用程序,用户可以通过该应用程序触摸一系列图像来旋转它们。我想做的是在用户旋转到特定位置后突出显示图像。

这可能吗?如果,那么任何提示都将不胜感激。

edit-好的,这里是一个例子!

首先,最简单的方法,基于您刚刚发布的代码示例:

r1c1.setOnClickListener {
r1c1.animate().apply{ duration = 100 rotationBy(270f) }.start()
}

所以这里的问题是,当视图旋转到90度时,你想高亮显示它,对吧?但它首先要完成一个动画。你有三个选择真正的

  • 做一些类似if (r1c1.rotation + 270f == 90)的事情,并在动画开始时高亮显示,这可能看起来很奇怪
  • 现在执行该检查,但如果需要,请使用withEndAction运行高亮显示代码
  • 动画完成后,使用withEndAction进行检查高亮显示

后者可能最有意义——动画完成后,检查其显示状态是否需要更改。大概是这样的:

r1c1.animate().setDuration(100).rotationBy(270f).withEndAction {
// need to do modulo so 720 == 360 == 0 etc
if (r1c1.rotation % 360 == TARGET_ROTATION) highlight(r1c1)
}.start()

我假设你有一些突出ImageView的方法,而你并没有要求这样做!


不幸的是,这里的问题是,如果用户在动画制作过程中点击视图,它将取消该动画并开始新的动画,包括视图当前所在旋转的rotationBy(270)。双击,你会看到一个角度的视图,现在它几乎永远不会匹配90度的值!这就是为什么更容易保持状态,按固定、有效的数量进行更改,并告诉视图它应该是什么样子。

因此,您应该为当前旋转设置一个值,更新该值,并将其用于高亮显示检查:

# var stored outside the click listener - this is your source of truth
viewRotation += 270f
# using rotation instead of rotationBy - we're setting a specific value, not an offset
r1c1.animate().setDuration(100).rotation(viewRotation).withEndAction {
// checking our internal rotation state, not the view!
if (viewRotation % 360 == TARGET_ROTATION) highlight(r1c1)
}.start()

我并不是说有一个单一的旋转变量像这样悬而未决——你可以,但看看下一点——如果你有很多ImageView要争论,它会很快变得一团糟。但这只是为了证明一个基本想法——你拥有自己的状态值,你可以控制它的设置,而View只是反映了这种状态,而不是相反。


好的,所以组织——我从r1c1中猜测,你有一个细胞网格,所有细胞都有相同的一般行为。这意味着要有很多重复的代码,除非你尝试将其概括并粘贴在一个地方——就像点击监听器一样,它也会做同样的事情,只是在上点击的任何视图上

基本上,View.onClickListeneronClick函数作为一个参数在被点击的视图中传递——基本上,这样你就可以按照我一直说的做,重用同一个点击监听器,并根据传递的内容做不同的事情。代替lambda({ }中的代码,基本上是你在一个地方使用的一个快速而肮脏的函数),你可以在所有ImageView的上设置一个通用的点击侦听器函数

fun spin(view: View) {
// we need to store and look up a rotation for each view, like in a Map
rotations[view] = rotations[view] + 270f
// no explicit references like r1c1 now, it's "whatever view was passed in"
view.animate().setDuration(100).rotation(rotations[view]).withEndAction {
// Probably need a different target rotation for each view too?
if (rotations[view] % 360 == targetRotations[view]) highlight(view)
}.start()
}

那么你的点击监听器设置就像

r1c1.setOnClickListener { spin(it) }

或者您可以将其作为函数引用传递(这已经太长了,无法解释,但在这种情况下可以使用,所以如果您愿意,可以使用它)

r1c1.setOnClickListener(::spin)

我建议你在查找ImageView细胞时生成一个列表(有几种方法可以处理这种事情),但有一个集合可以让你做之类的事情

allCells.forEach { it.setOnClickListener(::spin) }

现在,所有的点击监听器都设置为同一个函数,该函数将处理被点击的视图及其相关的状态。明白了吗?


所以你的基本结构有点像

// maybe not vals depending on how you initialise things!
val rotations: MutableMap<View, Float>
val targetRotations: Map<View, Float>
val allCells: List<ImageView>
// or onCreateView or whatever
fun onCreate() {
...
allCells.forEach { it.setOnClickListener(::spin) }
}
fun spin(view: View) {
rotations[view] = rotations[view] + 270f
view.animate().setDuration(100).rotation(rotations[view]).withEndAction {
val highlightActive = rotations[view] % 360 == targetRotations[view] 
highlight(view, highlightActive)
}.start()
}
fun highlight(view: View, enable: Boolean) {
// do highlighting on view if enable is true, otherwise turn it off
}

我没有进入整个";用于保持其所有状态的CCD_ 14的包装器类";事情,这可能是一个更好的方式,但我不想走得太远,使事情复杂化。这已经是一个愚蠢的长度了。我可能会对它做一个快速的回答,只是作为一个演示或其他

另一个答案已经足够长了,但我的意思是封装

class RotatableImageView(val view: ImageView, startRotation: Rotation, val targetRotation: Rotation)  {
private var rotation = startRotation.degrees
init {
view.rotation = rotation
view.setOnClickListener { spin() }
updateHighlight()
}
private fun spin() {
rotation += ROTATION_AMOUNT
view.animate().setDuration(100).rotation(rotation)
.withEndAction(::updateHighlight).start()
}
private fun updateHighlight() {
val highlightEnabled = (rotation % 360f) == targetRotation.degrees
// TODO: highlighting!
}
companion object {
const val ROTATION_AMOUNT = 90f
}
}
enum class Rotation(var degrees: Float) {
ROT_0(0f), ROT_90(90f), ROT_180(180f), ROT_270(270f);
companion object {
// just avoids creating a new array each time we call random()
private val rotations = values()
fun random() = rotations.random()
}
}

基本上,不是具有Views到当前旋转值的映射、Views到目标值的映射等,而是将每个View的所有状态捆绑成一个对象。一切都是在内部处理的,您只需要在布局中找到ImageView,并将它们传递到RotatableImageView构造函数中。这将设置一个点击监听器,并在必要时处理突出显示其ImageView,您不需要做任何其他事情!

enum只是创建一个类型来表示有效值的示例——当您创建RotatableImageView时,您必须传入其中一个,并且唯一可能的值是有效的旋转量。您也可以为它们提供默认值(如果需要,可以是Rotation.random()),因此构造函数调用可以只是RotatableImageView(imageView)

(你可以更多地利用这种东西,比如也可以将其用于内部旋转量,但在这种情况下,这很尴尬,因为在为视图设置动画时,0与360不同,它可能会以错误的方式旋转-所以你几乎必须跟踪你在视图上设置的实际旋转值)


作为一个快速的仅供参考(这就是为什么我说你正在做的事情可能会变得足够笨拙,值得学习一些技巧),与其在大量ID上进行findViewById,不如更容易找到所有的ImageView-用ID(比如GridLayout?)将它们包装在布局中可以更容易地找到你想要的

val cells = findViewById<ViewGroup>(R.id.grid).children.filterIsInstance<ImageView>()

然后你可以做之类的事情

rotatables = cells.map { RotatableImageView(it) }

这取决于你需要做什么,但这是一种可能的方法。基本上,如果你发现自己在重复同样的事情,只是做了一些小的改变,就像电视广告上说的,"必须有更好的方法!"!

最新更新