19_5(下)

简介: 19_5(下)

修改 TextInputEditText 下划线 的颜色:

TextInputEditText 是design 风格的数据框,非常好看.


使用如下:


<android.support.design.widget.TextInputEditText
    android:id="@+id/edit_sign_in_password"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="请输入密码"
    android:inputType="textPassword" />


修改下划线的颜色。


<style name="MyEditText" parent="Theme.AppCompat.Light">
    <item name="colorControlNormal">@color/bule</item>
</style>


最后在 xml 文件中加入 即可


android:theme="@style/MyEditText"


做教学助手,快完工了。


19/5-19


在使用数据库的时候发现问题。


数据库中的主键id 不能被修改。因为数据库的id 要和RecyclerView 中的 条目相同。所以删除某个条目后数据库中也跟着删除,过就是和数据库中的条目对应不上。


解决办法:将数据库中的所有数据拿出来,然后清空数据库。删除拿出来的数据库中对应的条目。将删除后的数据存入数据库。


虽然有点麻烦,但是目前没有想到更好的办法。将就着用吧


教学助手,马上完工了。


19/5-20


日期和时间的对话框,从底部弹出的。使用如下


//日期选择
api 'com.contrarywind:Android-PickerView:4.1.8'


日期选择


TimePickerView pvTime = new TimePickerBuilder(getContext(), new OnTimeSelectListener() {
    @Override
    public void onTimeSelect(Date date, View v) {
    }
}).build();
pvTime.show();


时间选择


Date curDate = new Date(System.currentTimeMillis());//获取当前时间
SimpleDateFormat formatter_year = new SimpleDateFormat("yyyy ");
String year_str = formatter_year.format(curDate);
int year_int = (int) Double.parseDouble(year_str);
SimpleDateFormat formatter_mouth = new SimpleDateFormat("MM ");
String mouth_str = formatter_mouth.format(curDate);
int mouth_int = (int) Double.parseDouble(mouth_str);
SimpleDateFormat formatter_day = new SimpleDateFormat("dd ");
String day_str = formatter_day.format(curDate);
int day_int = (int) Double.parseDouble(day_str);
Calendar selectedDate = Calendar.getInstance();//系统当前时间
Calendar startDate = Calendar.getInstance();
startDate.set(1900, 0, 1);
Calendar endDate = Calendar.getInstance();
endDate.set(year_int, mouth_int - 1, day_int);
 TimePickerView pvTime = new TimePickerBuilder(getContext(), new OnTimeSelectListener() {
                            @Override
                            public void onTimeSelect(Date date, View v) {
                                Log.e("---------", "onTimeSelect: "+getTime(date) );
                            }
                        })
                                //年月日时分秒 的显示与否,不设置则默认全部显示
                                .setType(new boolean[]{true, true, true, true, true, true})
                                //默认设置为年月日时分秒
                                .setLabel("年", "月", "日", "时", "分", "秒")
                                .isCenterLabel(false)
                                //设置选中项的颜色
                                .setTextColorCenter(Color.RED)
                                //设置没有被选中项的颜色
                                .setTextColorOut(Color.BLUE)
                                .setDate(selectedDate)
                                .setContentTextSize(20)
                                .setLineSpacingMultiplier(1.2f)
                                //设置X轴倾斜角度[ -90 , 90°]
                                .setTextXOffset(-10, 0, 10, 0, 0, 0)
                                .setRangDate(startDate, endDate)
                                .setDecorView(null)
                                .build();
                        pvTime.show();
  private String getTime(Date date) {//可根据需要自行截取数据显示
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return format.format(date);
    }


19/5-21


分别介绍一下你所知道 Android 的几种存储方式


本地文件存储


将一些不敏感的数据保存在本地。


网络存储


以版就是 http get或者 http post 从服务器,读取数据。业务数据获取的常用方法。


数据库存储


SQLite 是轻量级嵌入式数据库引擎,他支持SQL 语言,并且利用很少的内存就有很好的性能。一般在开发程序是 ,就会用到 SQLite 来存储大量的数据。


sharepreference


不支持数据频繁的读写。频繁的读写会造成数据的错乱。适用范围,保存少量的数据。且这些数据的格式非常简单。


ContentProvider :


四大组件之一,一般配合源码使用,SharePreference,文件存储使用,支持并发读取。


CoordinatorLayout

CoordinatorLayout 可以说是一个加强版的FrameLayout ,这个布局也是由Design Support 库提供的,他在普通情况下的作用和FrameLayout 基本一致。


事实上,CoordinatorLayout 可以监听其所有子控件的各种事件,然后帮我们作出更合理的响应。


AppBarLayout


AppBarLayout继承自LinearLayout,布局方向为垂直方向。所以你可以把它当成垂直布局的LinearLayout来使用。AppBarLayout是在LinearLayou上加了一些材料设计的概念,它可以让你定制当某个可滚动View的滚动手势发生变化时,其内部的子View实现何种动作。


请注意:上面提到的某个可滚动View,可以理解为某个ScrollView。怎么理解上面的话呢?就是说,当某个ScrollView发生滚动时,你可以定制你的“顶部栏”应该执行哪些动作(如跟着一起滚动、保持不动等等)。那某个可移动的View到底是哪个可移动的View呢?这是由你自己指定的!如何指定,我们后面说


AppBarLayout 子View 的动作


内部的子View通过在布局中加app:layout_scrollFlags设置执行的动作,那么app:layout_scrollFlags可以设置哪些动作呢?分别如下:


(1) scroll:值设为scroll的View会跟随滚动事件一起发生移动


(2) enterAlways:值设为enterAlways的View,当ScrollView往下滚动时,该View会直接往下滚动。而不用考虑ScrollView是否在滚动。


高度设置为: android:attr/actionBarSize,app:layout_scrollFlags="scroll|enterAlways"


(3) exitUntilCollapsed:值设为exitUntilCollapsed的View,当这个View要往上逐渐“消逝”时,会一直往上滑动,直到剩下的的高度达到它的最小高度后,再响应ScrollView的内部滑动事件。


?简单解释:在ScrollView往上滑动时,首先是View把滑动事件“夺走”,由View去执行滑动,直到滑动最小高度后,把这个滑动事件“还”回去,让ScrollView内部去上滑 ,设置最小高度为 并将最小高度设置为?android:attr/actionBarSize,app:layout_scrollFlags="scroll|exitUntilCollapsed"


(4) enterAlwaysCollapsed:是enterAlways的附加选项,一般跟enterAlways一起使用,它是指,View在往下“出现”的时候,首先是enterAlways效果,当View的高度达到最小高度时,View就暂时不去往下滚动,直到ScrollView滑动到顶部不再滑动时,View再继续往下滑动,直到滑到View的顶部结束。


最小高度设置android:attr/actionBarSize,app:layout_scrollFlags="scroll|enerAlways|enterAlwaysCollapsed")


CollapsingTollbarLayout 可折叠式标题栏:


CollaspingToolbarLayout 是一个作用于Toolbar 基础之上的布局,他可以让Toolbar 效果变得更加丰富。不仅仅只是展示一个标题栏。


CollaspingToolbarLayout 是 不能独立存在的。他在设计的时候就被限定只能作为AppBarLayout 的直接子布局来使用。而AppBarLayout 又必须是 CoordinatorLayout 的子布局。


19/5-22


谈谈你对Android 性能优化方面的了解


启动优化:application 中不要做大量的耗时操作,如果必须的话,建议异步做耗时操作


布局优化:使用合理的控件选择,少嵌套。(合理使用 include,merge,view Stub 等使用)


apk 优化:资源文件优化,代码优化 ,lit检查,.9png ,合理使用shape 代替图片,webp 等。


性能优化:网络优化,电量优化。


避免轮询,尽量使用推送


应用处于后台时,禁用某些数据传输


限制访问频率,失败后不要无限重连


选用合适的 定位服务(GPS , 网络定位,被动定位)


使用缓存


startActivityForResult 代替发送广播


内存优化:


循环尽量不适用局部变量


避免在onDraw中创建对象。onDraw 会被频繁调用,容易造成内存抖动。循环中创建大量的对象也是如此。


不用的对象及时释放。


数据库 及时关闭


adapter 使用缓存


注册广播后,在生命周期结束时 反注册


及时关闭流操作


图片尽量使用软引用,较大的图片可以通过bitmapFactory缩放后再使用,并及时recycler。另外加载巨图时不要 使用setImageBitmap或setImageResourse 或 BitmapFactory.decodeResource ,这些方法拿到的都是bitmap的对象,占用内存较大。可以用 BitmapFactory.decodeStream 方法配合 BitmapFactory.Options 进行缩放


避免static 成员变量 引用资源耗费过多实例


避免静态内部类的引用


回答一下什么是 强,软,弱,虚引用,以及区别


强:最常用的最普遍的引用,垃圾回收机制绝不会回收他,当内存空间不足,java虚拟机 宁愿抛出 OutOfMemoryError 错误,让程序异常终止,也不会随意回收具有强引用的对象。


软:如果内存足够,垃圾回收器就不会回收他。如果内存不足了,就会回收软引用的对象。软引用可以和一个引用队列 ReferenceQueue 来一起使用,如果软引用所引用的对象被垃圾回收器回收,java 虚拟机就会自动把这个软引用加入到与之关联的引用队列中。


private final LinkedHashMap<Object, Object> MULTIPLE_FIELDS = new LinkedHashMap<>();
private final ReferenceQueue<LinkedHashMap<Object, Object>> ITEM_QUENE = new ReferenceQueue<>();
/**
 * 软引用:SoftReference
 *       如果内存空间足够,垃圾回收器绝不会回收他,如果内存不足,则会回收这些对象的内存。
 */
private final SoftReference<LinkedHashMap<Object, Object>> FIELDS_RETERENCE
        = new SoftReference<>(MULTIPLE_FIELDS, ITEM_QUENE);


弱:垃圾回收器如果发现了弱引用,不管当前内存是否充足,都会回收他的内存。弱引用可以和一个引用队列 ReferenceQueue 联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。


虚:虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。


内存泄露:


一般可以这样理解,没有用的对象 无法进行回收,这个现象就是内存泄露。


如果内存泄露,会造成下面这些问题


应用的可用内存减少,增加了堆内存的压力。


降低了应用的性能,比如会频繁地GC


严重的时候可能会导致内存溢出错误,即OOM Error


OOM 发生在当我们尝试创建对象,但是堆内存无法通过GC 释放足够的空间,堆内存也无法继续增长,从而完成对象创建的时候。OOM发生很有可能是 内存泄露 引起的,内存泄露也并不一定是 OOM 。


Java 的内存管理


java 内存管理


19/5-23


分别讲讲 final ,static,synchronized 可以修饰什么,以及修饰后的作用:


final :可以修饰 类,方法,字段。


修饰类:该类不会被继承。


修饰方法:该方法不能被重写。


修饰字段:被修饰的 字段必须 赋初始值,并且不能被改变。如果字段是引用类型的。那么他将不能引用别 的对象,但是当前的对象内的 属性值是可以改变的。


static :可以修饰内部类,方法,字段。


被static 修饰的 变量或者方法等,都会在类加载的时候 进行加载,知道程序死亡,他们才会被销毁。


修饰内部类:被static修饰 的内部类可以直接作为一个 普通的类来使用,而不需先实例一个外部列。


修饰方法:调用该方法的时候只需要类名 . 方法就可以直接调用,不需要创建对象。


修饰字段:通过类名 . 的方式可以直接 获取 或者 赋值。


synchronized 可以修饰 方法,代码块


修饰方法:被 synchronized 修饰方法方法在同一时刻只能被一个线程访问。其他线程将会被阻塞,直到当前线程释放锁。


修饰代码块:其实和修饰方法差不多,只不过 修饰代码块可以 使用 类锁。方法如果要使用类锁,只能设置为静态的方法。


19/5-24


Android 7.0 新特性


1,多窗口模式


2,Data Saver :流量保护机制


3,改进的java 8 语言支持


4,自定义壁纸


5,快随回复


6,Daydream VR 支持


7,后台省电


8,快速设置


9,Unicode 9 支持 和全新的 emoji 表情符号


还有很多…


禁用多窗口模式


//在manifest 中添加如下属性。
android:resizeableActivity="false"


需要注意 当 targetSdkVersion 设置的值小于24时,这个属性不会起作用。面对这个情况,解决方案就是应用不支持横竖屏切换,比如将 MainActivity 设置为只支持竖屏,如下。


<activity
    android:name=".ExampleActivity"
    android:screenOrientation="portrait">


SnackBar 的使用


Snackbar.make(layout, "要删除吗?", BaseTransientBottomBar.LENGTH_LONG)
        .setAction("确定", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "成功", Toast.LENGTH_SHORT).show();
            }
        }).show();


XML 属性 Selector(选择器)


通过 选择器 selector 可以使控件 在不同操作下 显示不同样式


image.png


注:上述所有属性得 取值 = boolean 属性 true ,false


示例:


<?xml version="1.0" encoding="UTF-8"?>
< selector xmlns:android="http://schemas.android.com/apk/res/android">
 < !-- 指定按钮按下时的图片 -->
 <item android:state_pressed="true"  
       android:drawable="@drawable/start_down"
 />
 < !-- 指定按钮松开时的图片 --> 
 <item android:state_pressed="false"
       android:drawable="@drawable/start"
 />
< /selector>


使用:


<Button
  android:id="@+id/startButton"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background="@drawable/button_selector" 
/>


复习Activity 的生命周期


Activity 的生命周期


19/5-25——19/5-31


谈谈线程阻塞的原因


1,I O 操作


2,同步操作


3,sleep / join 方法


4,wait 方法。直接进入阻塞状态


Array List 扩容


初始容量为10,当添加第11个元素的时候,会扩容1.5倍,当添加到16个元素的时候扩容为15 * 1.5 = 22,以此类推


什么是反射机制,应用场景有哪些?


对于任何一个类,我们都能知道他有哪些方法。对于任何一个对象我们都可以调用他的任意一个方法和属性,这种动态的获取信息以及动态的调用对象的方法就称为java的反射机制。形象一点说,任何一个类或者对象,对我们来说都是透明的,想要啥直接拿就可以。


应用场景:


逆向代码,例如反编译

与注解相结合的框架,如 Retrofit

单纯的反射机制应用框架,例如 EventBus(事件总线)

动态生成类框架 例如Gson


Java 中使用线程的方式有哪些


常用的方式:


1,继承 Thread


2,实现Runnable 接口


3,通过线程池池 创建线程


private static final ThreadFactory FACTORY = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger();
    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r,"text ---- #"+mCount.getAndIncrement());
    }
};
public static void main(String[] args) {
    ThreadPoolExecutor textPool = new ThreadPoolExecutor(3,
            5, 0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingDeque<Runnable>(),FACTORY);
    for (int i = 0; i < 10; i++) {
        textPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });
    }
}


结果:


text ---- #1
text ---- #2
text ---- #0
text ---- #2
text ---- #1
text ---- #2
text ---- #0
text ---- #2
text ---- #1
text ---- #0


View 动画


在Android 中的 View 动画框架中,一共提供了 Alpha (透明动画),Rotate(旋转动画),Scale(缩放动画),Translate(平移动画) 四种类型的补间动画。


标签表示 补间动画的集合,对应 AnimationSet 类,所以 set 标签中 可以包含多个补间动画标签,并且还可以包含补间动画中的 集合。


Alpha (透明度 动画)


android:fromAlpha

Float. 设置透明度的初始值,其中0.0是透明,1.0是不透明的。


android:toAlpha

Float. 设置透明度的结束值,其中0.0是透明,1.0是不透明的


duration : 时间


<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 透明动画 -->
    <alpha
        android:duration="2000"
        android:fromAlpha="1.0"
        android:toAlpha="0" />
</set>


调用:


Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.alpha);
animation.setFillAfter(true);
group.startAnimation(animation);


Scale(缩放动画)


android:fromXScale

Float. 水平方向缩放比例的初始值,其中1.0是没有任何变化。

android:toXScale

Float. 水平方向缩放比例的结束值,其中1.0是没有任何变化。

android:fromYScale

Float. 竖直方向缩放比例的初始值,其中1.0是没有任何变化。

android:toYScale

Float. 竖直方向缩放比例的结束值,其中1.0是没有任何变化。

android:pivotX

Float. 缩放中心点的x坐标

android:pivotY

Float. 缩放中心点的y坐标


<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!--缩放动画-->
    <scale
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="0.2"
        android:toYScale="0.2" />
</set>


使用如上


Translate (平移动画)


android:fromXDelta

Float or percentage. 移动起始点的x坐标. 表示形式有三种:

1 相对于自己的左边界的距离,单位像素值。(例如 "5")

2 相对于自己的左边界的距离与自身宽度的百分比。(例如  "5%")

3 相对于父View的左边界的距离与父View宽度的百分比。(例如 "5%p")

android:toXDelta

Float or percentage. 移动结束点的x坐标. 表现形式同上

android:fromYDelta

Float or percentage. 移动起始点的y坐标. 表示形式有三种:

1 相对于自己的上边界的距离,单位像素值。(例如 "5")

2 相对于自己的上边界的距离与自身高度的百分比。(例如  "5%")

3 相对于父View的上边界的距离与父View高度的百分比。(例如 "5%p")

android:toYDelta

Float or percentage. 移动结束点的y坐标. 表现形式同上


<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="2000"
        android:fromXDelta="20"
        android:fromYDelta="20"
        android:toXDelta="100"
        android:toYDelta="100" />
</set>


使用如上


Rotate (旋转动画)


android:fromDegrees

Float. 旋转初始的角度。

android:toDegrees

Float. 旋转结束的角度。

android:pivotX

Float or percentage. 旋转中心点x坐标,表示形式有三种:

1 相对于自己的左边界的距离,单位像素值。(例如 "5")

2 相对于自己的左边界的距离与自身宽度的百分比。(例如 "5%")

3 相对于父View的左边界的距离与父View宽度的百分比。(例如 "5%p")

android:pivotY

Float or percentage. 旋转中心点y坐标,表示形式有三种:

1 相对于自己的上边界的距离,单位像素值。(例如 "5")

2 相对于自己的上边界的距离与自身宽度的百分比。(例如 "5%")

3 相对于父View的上边界的距离与父View高度的百分比。(例如 "5%p")


<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:fromDegrees="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="1000"
        android:toDegrees="+360" />
</set>


通过补间动画 为 Activity 自动以切换动画


通过调用Activity 类的 overridePendingTransition(int enterAnim, int exitAnim) 方法可以实现自定义Activity 的切换动画,注意这个方方必须在 startActivity 和 finish 之后调用,否则无效


逐帧 动画


逐帧动画 是用来 逐帧 显示预先定义好的 一组图片,类似于电影播放,对应于 AnimationDrawable 类。


动画的资源文件一般放在 Drawable 文件夹下。


通过 xml 定义一个逐帧动画。


<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true" >
    <item
        android:drawable="@drawable/first_pic"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/second_pic"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/third_pic"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/fourth_pic"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/fifth_pic"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/sixth_pic"
        android:duration="1000"/>
</animation-list>


然后将面定义的AnimationDrawable 作为 View 的背景 并且通过 AnimationDrawable 来播放动画。


image.setImageResource(R.drawable.anim_list);
AnimationDrawable animationDrawable = (AnimationDrawable) image.getDrawable();
animationDrawable.start();
//animationDrawable.stop(); //如果oneshot为false,必要时要停止动画


通过代码实现:


//代码定义、创建、执行动画
AnimationDrawable animationDrawable = new AnimationDrawable();
animationDrawable.addFrame(getResources().getDrawable(R.drawable.first_pic), 1000);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.second_pic), 1000);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.third_pic), 1000);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.fourth_pic), 1000);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.fifth_pic), 1000);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.sixth_pic), 1000);
animationDrawable.setOneShot(true);
image.setImageDrawable(animationDrawable);
animationDrawable.start();


属性动画


ViewPropertyAnimator


使用方式:View.animate() 后面根 translationX()等方法


view.animate().translationX(500);  


具体可以跟的方法以及方法所对应的 View 中的实际操作的方法如下图所示:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TSNjJAcW-1569745892498)(F:\笔记\android\assets\1558856697517.png)]


从图中可以看到, View 的每个方法都对应了 ViewPropertyAnimator 的两个方法,其中一个是带有 -By 后缀的,例如,View.setTranslationX() 对应了 ViewPropertyAnimator.translationX() 和 ViewPropertyAnimator.translationXBy() 这两个方法。其中带有 -By() 后缀的是增量版本的方法,例如,translationX(100) 表示用动画把 View 的 translationX 值渐变为 100,而 translationXBy(100) 则表示用动画把 View 的 translationX 值渐变地增加 100。


常用的方法:

ViewPropertyAnimator.withStartAction/EndAction()


这两个方法是 ViewPropertyAnimator 的独有方法。它们和 set/addListener() 中回调的 onAnimationStart() / onAnimationEnd() 相比起来的不同主要有两点:


1,`withStartAction()` / `withEndAction()` 是一次性的,在动画执行结束后就自动弃掉了,就算之后再重用 `ViewPropertyAnimator` 来做别的动画,用它们设置的回调也不会再被调用。而 `set/addListener()` 所设置的 `AnimatorListener` 是持续有效的,当动画重复执行时,回调总会被调用

1

2,withEndAction() 设置的回调只有在动画正常结束时才会被调用,而在动画被取消时不会被执行。这点和 AnimatorListener.onAnimationEnd() 的行为是不一致的。


常用的监听

 

ViewPropertyAnimator animate = imageView.animate();
    //当动画的属性更新时(不严谨的说,即每过 10 毫秒,动画的完成度更新时),这个方法被调用。
                animate.setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                    }
                });
                animate.setListener(new AnimatorListenerAdapter() {
                    //当动画被cancel()方法取消时,这个方法会被调用。
                    //需要说明一下的是,就算动画被取消,onAnimationEnd() 也会被调用。所以当动画被取消时,如果设置了  AnimatorListener,那么 onAnimationCancel() 和 onAnimationEnd() 都会被调用。onAnimationCancel() 会先于 onAnimationEnd() 被调用。
                    @Override
                    public void onAnimationCancel(Animator animation) {
                        super.onAnimationCancel(animation);
                    }
                    //当动画结束时调用
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                    }
                    //当动画通过 setRepeatMode() / setRepeatCount() 或 repeat() 方法重复执行时,这个方法被调用。
                    @Override
                    public void onAnimationRepeat(Animator animation) {
                        super.onAnimationRepeat(animation);
                    }
                    //当动画开始时调用
                    @Override
                    public void onAnimationStart(Animator animation) {
                        super.onAnimationStart(animation);
                    }
                });


ObjectAnimator


使用方式 :


1,如果是自定义控件,需要添加 setter/getter 方法;


2,使用 ObjectAnimatior.ofXXX(),创建 ObjectAnimator 对象,


3,使用start 方法执行动画


与属性动画相比,View 动画存在一个缺陷,View 动画只是改变 View 的显示,并没有改变 View 的响应区域,并且View 动画只能对View 做四种类型的补间动画。因此 在 3.0 以后 添加了 属性动画框架


实现View 的四种动画

//利用 ObjectAnimator 实现透明动画。group 代表的是 视图
                ObjectAnimator
                        .ofFloat(group, "alpha", 1, 0, 1)
                        .setDuration(2000)
                        .start();
                //利用AnimatorSet 和 ObjectAnimation 实现缩放动画
                final AnimatorSet animatorSet = new AnimatorSet();
                group.setPivotX(group.getWidth()/2);
                group.setPivotY(group.getHeight()/2);
                animatorSet.playTogether(
                        ObjectAnimator.ofFloat(group,"scaleX",1,0.2f).setDuration(5000),
                        ObjectAnimator.ofFloat(group,"scaleY",1,0.2f).setDuration(5000)
                );
                animatorSet.start();
                //使用AnimatorSet 和 ObjectAnimation 实现平移动画
                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.playTogether(
                        ObjectAnimator.ofFloat(group,"translationX",20,100).setDuration(2000),
                        ObjectAnimator.ofFloat(group,"translationY",20,100).setDuration(2000)
                );
                animatorSet.start();
                //利用ObjectAnimation 实现旋转动画
                group.setPivotX(group.getWidth() / 2);
                group.setPivotY(group.getHeight() / 2);
                ObjectAnimator
                        .ofFloat(group, "rotation", 0, 180)
                        .setDuration(2000)
                        .start();


上面是通过代码形式实现的 属性动画,对用通过xml 定义升序工会的方式不是很常用,因为属性的起始值 和 结束值 大多是程序运行时候动态获取的。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4jVJQGcu-1569745892500)(C:\Users\Lv_345\Desktop\ObjectAnimator.gif)]


给自定义的View 设置属性动画

public class Text extends View {
    int TextX = 0;
    public int getTextX() {
        return TextX;
    }
    public void setTextX(int textX) {
        TextX = textX;
        invalidate();
    }
    public Text(Context context) {
        super(context);
    }
    public Text(Context context,AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        Paint paint = new Paint();
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);//描边
        paint.setStrokeCap(Paint.Cap.BUTT); //线帽:默认
        paint.setStrokeJoin(Paint.Join.BEVEL); //直线
        paint.setTextSize(50);
        paint.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));//字体类型
        //对齐方式
        paint.setTextAlign(Paint.Align.LEFT);
        //文本
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        //获取基线 的坐标
        float TextY = getHeight()/2 +((fontMetrics.top - fontMetrics.bottom)/2 - fontMetrics.bottom);
        canvas.drawText("我是自定义文本",TextX,TextY,paint);
    }
}


以上是自定义的 文本。只不过 给他的 起始位置 的 x 值添加了一个 get/set 方法。默认是0


<com.admin.view_core.viewGroup.Text
    android:id="@+id/text"
    android:layout_width="200dp"
    android:layout_height="100dp"
    android:background="#999999"
    />


下面给 其实位置的 x 设置属性动画


Text text = findViewById(R.id.text);
ObjectAnimator animator = ObjectAnimator.ofInt(text,"TextX",0,100);
animator.start();


属性动画常用的方法和监听:

1, 设置动画时长


 

ObjectAnimator
                        .ofFloat(group, "alpha", 1, 0, 1)
                        .setDuration(2000)
                        .start();


2,设置 速度控制器


Text text = findViewById(R.id.text);
                ObjectAnimator animator = ObjectAnimator.ofInt(text,"TextX",0,900);
                //速度设置器。设置不同的 Interpolator ,动画就会以不同的速度来执行
//                animator.setInterpolator(new LinearInterpolator());//匀速
//                animator.setInterpolator(new AccelerateDecelerateInterpolator());//先加速,在减速,这个是默认的
//                animator.setInterpolator(new AccelerateInterpolator());//持续加速
//                animator.setInterpolator(new DecelerateInterpolator());//持续 减速直到0
                // 先回拉一下在进行正常的 动画轨迹,如果是 放大,就先缩小一下在放大,其他同理
//                animator.setInterpolator(new DecelerateInterpolator());
                //动画会超过目标值一些,然后 弹回来
//                animator.setInterpolator(new OvershootInterpolator());
                //上面两个的结合,开始前会拉,最后 超过一些在回弹
//                animator.setInterpolator(new AnticipateOvershootInterpolator());
                //在目标出处弹跳
//                animator.setInterpolator(new BounceInterpolator());
                //这是一个 正弦./余弦曲线。他可以自定义 曲线的生命周期,所以动画可以不到终点就结束
                // 也可以 到达终点后 反弹,回弹的次数由 曲线的周期确定,曲线的周期由 CycleInterpolator 构造方法的参数决定
//                animator.setInterpolator(new CycleInterpolator(0.5f));
    animator.start();


3,常用的监听


ObjectAnimator animator = ObjectAnimator.ofInt(text,"TextX",50,200);
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationCancel(Animator animation) {
        super.onAnimationCancel(animation);
    }
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
    }
    @Override
    public void onAnimationRepeat(Animator animation) {
        super.onAnimationRepeat(animation);
    }
    @Override
    public void onAnimationStart(Animator animation) {
        super.onAnimationStart(animation);
    }
    //由于ObjectAnimation 支持使用 onPause 暂停,
    // 所以增加了下面两个 监听
    @Override
    public void onAnimationPause(Animator animation) {
        super.onAnimationPause(animation);
    }
    @Override
    public void onAnimationResume(Animator animation) {
        super.onAnimationResume(animation);
    }
});
animator.start();


效果和 ViewPropertyAnimator 的监听一样。


渐变 ArgbEvaluator

ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xffff0000, 0xff00ff00);
// 在这里使用 ObjectAnimator.setEvaluator() 来设置 ArgbEvaluator,修复闪烁问题
animator.setEvaluator(new ArgbEvaluator());
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(2000);
animator.start();


在同一个动画中改变多个属性值

// 使用 PropertyValuesHolder.ofFloat() 来创建不同属性的动画值方案
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 0,1);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 0,1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 0,1);
// 然后,用 ObjectAnimator.ofPropertyValuesHolder() 把三个属性合并,创建 Animator 然后执行
ObjectAnimator
        .ofPropertyValuesHolder(view, holder1, holder2, holder3)
        .start();


AnimatorSet 多个动画配合执行

说白了,就是按照指定的顺序执行


view.setTranslationX(-200f);
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "alpha", 0, 1);
animator1.setDuration(1000);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "translationX", -200, 200);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "rotation", 0, 1080);
animator3.setDuration(1000);
AnimatorSet animatorSet = new AnimatorSet();
// 用 AnimatorSet 的方法来让三个动画协作执行
// 要求 1: animator1 先执行,animator2 在 animator1 完成后立即开始
// 要求 2: animator2 和 animator3 同时开始
//先执行 1,在执行2
animatorSet.playSequentially(animator1,animator2);
//2 和 3 一起执行
animatorSet.playTogether(animator2,animator3);
animatorSet.start();

PropertyValuesHolders.ofKeyframe() 把同一个属性拆分

除了合并 多个属性和调配多个动画,你还可以在 PropertyValueHodler 的基础上更进一步,例如,你可以让一个进度增加到 100% 后在 反弹回来。


// 使用 Keyframe.ofFloat() 来为 view 的 progress 属性创建关键帧
// 初始帧:progress 为 0
Keyframe keyframe1 = Keyframe.ofFloat(0,0);
// 时间进行到一半:progress 为 100
Keyframe keyframe2 = Keyframe.ofFloat(0.5f,100);
// 结束帧:progress 回落到 80
Keyframe keyframe3 = Keyframe.ofFloat(1,80);
// 使用 PropertyValuesHolder.ofKeyframe() 来把关键帧拼接成一个完整的属性动画方案
PropertyValuesHolder holder = PropertyValuesHolder
        .ofKeyframe("progress",keyframe1,keyframe2,keyframe3);
// 使用 ObjectAnimator.ofPropertyValuesHolder() 来创建动画
ObjectAnimator animator  = ObjectAnimator.ofPropertyValuesHolder(view,holder);
animator.setDuration(1000);
animator.start();


相关文章
Jetpack Compose中ViewModel、Flow、Hilt、Coil的使用
Jetpack Compose中ViewModel、Flow、Hilt、Coil的使用
1079 0
Jetpack Compose中ViewModel、Flow、Hilt、Coil的使用
|
消息中间件 微服务
MQTT X连接阿里云微服务消息队列MQTT
MQTT X 是 EMQ 开源的一款优雅的跨平台 MQTT 5.0 桌面客户端,它支持 macOS, Linux, Windows。MQTT X 的 UI 采用了聊天界面形式,简化了页面操作逻辑,用户可以快速创建连接,允许保存多个客户端,方便用户快速测试 MQTT/MQTTS 连接,及 MQTT 消息的订阅和发布。本文演示如何使用该工具快速连接阿里云微服务消息队列MQTT,并进行消息的发布和订阅测试。
3429 0
MQTT X连接阿里云微服务消息队列MQTT
|
11月前
|
存储 安全 物联网
Android:Android 应用权限详解
这篇文章为大家系统的梳理一下 Android 权限相关的知识,在日常开发中,我们都用过权限,但是对于权限的一些细节我们可能掌握的还不够全面,这篇文章会全面的为大家介绍权限相关的知识。
670 0
Android:Android 应用权限详解
|
4月前
|
JSON IDE 开发工具
AAB 扶正!APK 再见!
AAB 扶正!APK 再见!
54 0
|
存储 缓存 开发框架
Android性能优化(二):内存优化你一定要了解的知识点
内存优化在Android开发中是一个很重要的部分,app内存的使用直接影响app的稳定性和流畅度。
|
移动开发 JSON 网络协议
Alink 协议解析 | 学习笔记
快速学习 Alink 协议解析
267 0
Alink 协议解析 | 学习笔记
|
存储 自然语言处理 前端开发
Jetpack 系列(2)—— 为什么 LiveData 会重放数据,怎么解决?
Jetpack 系列(2)—— 为什么 LiveData 会重放数据,怎么解决?
1009 0
Jetpack 系列(2)—— 为什么 LiveData 会重放数据,怎么解决?
|
物联网 Java
阿里云物联网平台设备秘钥认证概述
设备接入物联网平台之前,需通过身份认证。目前,物联网平台支持使用设备密钥、ID²和X.509证书进行设备身份认证。目前用户使用较多的是设备秘钥认证,物联平台目前提供四种设备秘钥认证方案:一机一密、一型一密预注册、一型一密免预注册和子设备动态注册。本文主要通过Code方式分别给出这几种方案的实现。
31007 8
阿里云物联网平台设备秘钥认证概述
|
Android开发
安卓10获取剪贴板内容
安卓10获取剪贴板内容
847 0
|
API Android开发
Android 复制文本到剪切板,及ClipboardManager相关操作
Android 复制文本到剪切板,及ClipboardManager相关操作
709 0