Android初步进阶之自定义View、动画(一)

在小小的屏幕上组合出符合预期的视图,仅仅使用已经定义好的组件可不够,需要我们自己去设置视图。

先看看View的结构:
在这里插入图片描述

1. 首先介绍,自定义View滑动。一共有六种方法,分别是layout()、offsetLeftAndRight()与offsetTopAndBottom()、LayoutParams、动画、scrollTo与ScrollBy以及Scroller。

  • Layout的使用。
    我们自定义一个View。为CustomView.kt。
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import java.time.format.DecimalStyle

class CustomView(val contex: Context,val attrs:AttributeSet?) : View(contex, attrs) {
    private var lastX:Int = 0
    private var lastY:Int = 0
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        var x:Int = event!!.x.toInt()
        var y:Int = event!!.y.toInt()
        when(event.action){
            MotionEvent.ACTION_DOWN -> {
                lastX = x
                lastY = y
            }
            MotionEvent.ACTION_MOVE ->{
                var offsetX:Int = x - lastX
                var offsetY:Int = y - lastY
                layout(left + offsetX, top + offsetY, right + offsetX, bottom + offsetY)
            }
        }
        return true
    }
}

在视图直接使用就行,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.example.my_view.ui.layout.CustomView
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_margin="50dp"
        android:background="#8742A8A3"
        />
</LinearLayout>

拖拉这个视图,就可以实现移动了。

  • offsetLeftAndRight()与offsetTopAndBottom()
    上面代码改一下就可以了
var offsetX:Int = x - lastX
var offsetY:Int = y - lastY
offsetLeftAndRight(offsetX)
offsetTopAndBottom(offsetY)
  • LayoutParams同样更改一个地方
 var offsetX:Int = x - lastX
 var offsetY:Int = y - lastY
//                offsetLeftAndRight(offsetX)
//                offsetTopAndBottom(offsetY)
var layoutParams:LinearLayout.LayoutParams = getLayoutParams() as LinearLayout.LayoutParams
layoutParams.leftMargin = left + offsetX
layoutParams.topMargin = top + offsetY
setLayoutParams(layoutParams)
  • 动画
    在res/anim文件夹下创建translate.xml,设置好动画。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true"
    >
    <translate
        android:duration="1000"
        android:fromXDelta="0"
        android:toXDelta="300"
        android:fromYDelta="0"
        android:toYDelta="200"
        />
</set>

在主活动中设置动画,

setContentView(R.layout.main_activity)
val mCustomView:CustomView = findViewById(R.id.view)
mCustomView.animation = AnimationUtils.loadAnimation(this, R.anim.translate)
  • scrollTo与scollBy需要注意,这个滑动指的是父组件。
    同样替换ACTION_MOVE的部分。需要记得去掉上面的动画,否则不生效。
 (parent as View).scrollBy(-offsetX, -offsetY)
  • Scroller的使用
    修改CustomView.kt。
package com.example.my_view.ui.layout

import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.Scroller
import java.time.format.DecimalStyle

class CustomView(val contex: Context,val attrs:AttributeSet?) : View(contex, attrs) {
    private var lastX:Int = 0
    private var lastY:Int = 0
    private var mScroller:Scroller? = null
    init {
        this.mScroller = Scroller(context)
    }
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        var x:Int = event!!.x.toInt()
        var y:Int = event!!.y.toInt()
        when(event.action){
            MotionEvent.ACTION_DOWN -> {
                lastX = x
                lastY = y
            }
            MotionEvent.ACTION_MOVE ->{
                var offsetX:Int = x - lastX
                var offsetY:Int = y - lastY
//                offsetLeftAndRight(offsetX)
//                offsetTopAndBottom(offsetY)
//                var layoutParams:LinearLayout.LayoutParams = getLayoutParams() as LinearLayout.LayoutParams
//                layoutParams.leftMargin = left + offsetX
//                layoutParams.topMargin = top + offsetY
//                setLayoutParams(layoutParams)
//                (parent as View).scrollBy(-offsetX, -offsetY)
            }
        }
        return true
    }
    //重绘函数
    override fun computeScroll() {
        super.computeScroll()
        if(mScroller!!.computeScrollOffset()){
            (parent as View).scrollTo(mScroller!!.currX, mScroller!!.currY)
            invalidate()
        }
    }
    //新加的
    fun smoothScrollTo(destX:Int, destY:Int){
        var scrollX:Int = scrollX
        var delta:Int = destX - scrollX
        mScroller!!.startScroll(scrollX, 0, delta, 0 ,2000)
        invalidate()
    }
}

在主活动设置右移400。

val mCustomView:CustomView = findViewById(R.id.view)
mCustomView.smoothScrollTo(-400, 0)

OK,六种完事了!

2. 属性动画

基本的属性动画,包括四种:AlphaAnimation、RotateAnimation、TranslateAnimation、ScaleAnimation。它们属于View的基本动画,使用便捷,但是,缺点也很明显,不具有交互性。使用AnimationSetObjectAnimator可以进行更加精细化的控制,下面来介绍。

  • ObjectAnimator
    作为动画属性中最为重要的属性,需要注意设置的属性,必须拥有Set和Get方法,尤其在使用自定义的属性时。
    示例:
val mCustomView:CustomView = findViewById(R.id.view)
val mObjectAnimator:ObjectAnimator = ObjectAnimator.ofFloat(mCustomView, "translationX", 400F)
mObjectAnimator.duration = 3000
mObjectAnimator.start()

"translationX"为动画的属性,可以使用的还有:

  • translationX与translationY,平移
  • rotation、rotationX、rotationY。旋转
  • PrivotX、PrivotY控制View对象的支点位置。
  • alpha透明度.
  • x和y描述在容器中的最终位置。
    也可以自定义属性,记得设置set与get方法。
  • 好了,下一个ValueAnimator
    不提供动画效果,类似数值发生器,用来产生有一定规律的数字,让调用者控制动画的实现过程。
 val mValueAnimator:ValueAnimator = ValueAnimator.ofFloat(0f ,100f)
 mValueAnimator.setDuration(1000).start()
 mValueAnimator.addUpdateListener {
     val mFloat:Float = it.animatedValue as Float
}
  • 动画的监听
    start、Repeat、End、Cancel四个过程。
  • 组合动画
    AnimatorSet类提供了play()方法。
    play(),with(),after(),before()
val animator1:ObjectAnimator = ObjectAnimator.ofFloat(mCustomView, "translationX", 0.0f, 200f, 0f)
val animator2:ObjectAnimator = ObjectAnimator.ofFloat(mCustomView, "scaleX", 1.0f, 2f)
val animator3:ObjectAnimator = ObjectAnimator.ofFloat(mCustomView, "rotationX", 0.0f, 90f, 0f)
val set:AnimatorSet = AnimatorSet()
set.duration = 1000
set.play(animator1).with(animator2).after(animator3)
set.start()
  • XML中使用动画
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:duration="3000"
	android:propertyName="scaleX"
	android:valueFrom="1.0"
	android:valueTo="2.0"
	android:valueType="floatType"
	>
</objectAnimator>

3.解析Scroller

4.View事件分发机制

  • Activity构成
    在这里插入图片描述当点击事件产生之后,事件首先会传递给当前的Activity,这会调用Activity的dispatchTouchEvent()方法,具体的事件处理交给Activity的PhoneWindow来完成的,然后PhoneWindow再把事件的处理工作交给DecorView,之后再由DecorView将事件处理工作交给根ViewGroup。

5. View的工作流程

指的就是measure、layout、draw,其中,measure用来测量View的宽度和高,layout用来确定View的位置,draw则用来绘制View。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值