Java安卓开发入门-TabLayout、Fragment和属性动画

功能介绍

  • 使用Java编写
  • 主要有那些功能?
    • ex1:通过seekbar控制lottie动画播放,以及动画自动播放
    • ex2:通过下方输入参数控制中间view的动画效果
    • ex3:使用tablayout和fragment进行标签页切换,并加入切换后前5s播放lottie动画的功能,动画结束后淡入淡出

效果图

 

前置技能点

用用java写吧。

Android开发入门http://hukai.me/android-training-course-in-chinese/basics/index.html

java入门https://www.runoob.com/java/java-tutorial.html

Fragment参考

• 官⽅⽂档:

https://developer.android.com/guide/fragments

• 中⽂翻译:

https://juejin.cn/post/6900739309826441224#heading-29

https://juejin.cn/post/6901453354463920135

• Fragment 源码解读:

https://juejin.cn/post/6844904086437904398

Animation参考

• 属性动画:

https://developer.android.com/guide/topics/graphics/prop-animation

• ⻉塞尔曲线可视化:

https://cubic-bezier.com/

• ⾮官⽅总结:

https://juejin.cn/post/6844903465211133959

• 其他阅读材料:属性动画

https://rengwuxian.com/123.html

https://rengwuxian.com/127.html

• Lottie Android 指南:

https://airbnb.io/lottie/#/android

• Lottie 资源:

https://lottiefiles.com/

本项目地址在本人github

 

项目结构

项目是老师给的模板中融合了我参考Android Studio的Tabbed Activity模板添加的文件。文件比较多,只列了部分,也只挑其中重要的讲。

 

 

 

Java文件

从上到下依次为:

  • 1-3 三个按键对应的activity类
  • 4 主界面活动,控制三个按键逻辑,进入三个不同的activity
  • 5 这个名字取得不太好,因为是直接照搬的我上一篇教程。RecyclerView的自定义Adapter类,主要是规定了如何装载和控制视图内容,包括item的点击事件,这次把ViewHolder也放在里面了
  • 6 写这个的时候发现并不需要这个类,这个类是默认模板控制每个页面view内容的
  • 7 调用SectionsPagerAdapter的getItem时需要来实例化给定页面的fragment实例,通过PlaceholderFragment.newInstance实现
  • 8 自定义的view类型,可以制作彩虹文字
  • 9 自定义的SectionsPagerAdapter,继承自FragmentPagerAdapter,控制整个Fragment的显示效果,包括tab的文字,总页面数量等

 

Layout文件

分别为对应的activity的布局,不详细解释了,说一下要注意的点。

xml添加lottie动画

如何添加lottie动画在前置技能点有,在本app中,需要在ex1.xml和fragment_placeholder.xml中加入(当然id注意不要重复)

    <com.airbnb.lottie.LottieAnimationView
        android:id="@+id/animation_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        app:lottie_loop="true"
        app:lottie_rawRes="@raw/material_wave_loading" />

其中app:lottie_rawRes指定了json格式文件的动画文件位置,一般放在raw文件夹下。

fragment_placeholder.xml中只有animation_view2和recycler_view,每个fragment片段就是这两个view淡入淡出实现的

 

功能实现

勾选checkbox切换动画播放状态

这里的loopCheckBox是一个CheckBox类型

        loopCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    // 当选中自动播放的时候,开始播放 lottie 动画,同时禁止手动修改进度
                    animationView.playAnimation();
                    seekBar.setEnabled(false);
                } else {
                    // 当去除自动播放时,停止播放 lottie 动画,同时允许手动修改进度
                    animationView.pauseAnimation();
                    seekBar.setEnabled(true);
                }
            }
        });

  

seekbar控制动画进度

这里的seekBar是一个SeekBar类型,已通过findViewById(R.id.seekbar)绑定

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                // TODO ex1-2: 这里应该调用哪个函数呢
                // 提示1:可以参考 https://airbnb.io/lottie/#/android?id=custom-animators
                // 提示2:SeekBar 的文档可以把鼠标放在 OnProgressChanged 中间,并点击 F1 查看,
                // 或者到官网查询 https://developer.android.google.cn/reference/android/widget/SeekBar.OnSeekBarChangeListener.html#onProgressChanged(android.widget.SeekBar,%20int,%20boolean

                // 每次progress改变的时候都会调用这个函数,所以只需要在这时候将新的progress对应的百分比传入即可
                // progress默认是1-100的整数,用getMax()最理想
                animationView.setProgress((float)progress/seekBar.getMax());

            }

  

ex2中的一些实现

选颜色是通过给颜色view添加一个点击监听,通过ColorPicker类展示的选择颜色窗口

复杂的动画效果是通过AnimatorSet串联了多个动画实现的,其中控制颜色使用的是ObjectAnimator.ofArgb()实现的,其他的是ofFloat()

 

ex3的一些实现

主活动如下

public class Ch3Ex3Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ch3ex3);

        // TODO: ex3-1. 添加 ViewPager 和 Fragment 做可滑动界面
        SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
        ViewPager viewPager = findViewById(R.id.view_pager);
        viewPager.setAdapter(sectionsPagerAdapter);

        // TODO: ex3-2, 添加 TabLayout 支持 Tab
        TabLayout tabs = findViewById(R.id.tabs);
        tabs.setupWithViewPager(viewPager);
    }
}
 

  

SectionsPagerAdapter类

public class SectionsPagerAdapter extends FragmentPagerAdapter {

    @StringRes
    private static final int[] TAB_TITLES = new int[]{R.string.tab_text_1, R.string.tab_text_2, R.string.tab_text_3};
    private final Context mContext;

    public SectionsPagerAdapter(Context context, FragmentManager fm) {
        super(fm);
        mContext = context;
    }

    @Override
    public Fragment getItem(int position) {
        // getItem is called to instantiate the fragment for the given page.
        // Return a PlaceholderFragment (defined as a static inner class below).
        return PlaceholderFragment.newInstance(position);
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return mContext.getResources().getString(TAB_TITLES[position]);
    }

    @Override
    public int getCount() {
        // Show 3 total pages.
        return 3;
    }
}

  

PlaceholderFragment类

public class PlaceholderFragment extends Fragment {

    private LottieAnimationView animationView2;
    private RecyclerView myRecycler;
    private Context mContext;

    private List list = new ArrayList();

    private static final String ARG_SECTION_NUMBER = "tab_number";

    public static PlaceholderFragment newInstance(int index) {
        PlaceholderFragment fragment = new PlaceholderFragment();
        Bundle bundle = new Bundle();
        bundle.putInt(ARG_SECTION_NUMBER, index);
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // TODO ex3-3: 修改 fragment_placeholder,添加 loading 控件和列表视图控件
        View root = inflater.inflate(R.layout.fragment_placeholder, container, false);
        animationView2 = root.findViewById(R.id.animation_view2);
        myRecycler = root.findViewById(R.id.recycler_view);
        mContext = container.getContext();
        return root;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始化数组
        for (int i = 1; i < 101; i++) {
            list.add(String.format("这里是第 %d 行", i));
        }
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // 播放动画
        animationView2.playAnimation();

        // 展示 recycler view
        myRecycler.setLayoutManager(new LinearLayoutManager(mContext));
        myRecycler.setAdapter(new MyAdapter(list));
        myRecycler.setVisibility(View.GONE); //前5s并不显示不渲染

        ObjectAnimator fadeOutAnimator = ObjectAnimator.ofFloat(animationView2,
                "alpha", 1f, 0f);//淡出效果,alpha从1到0
        fadeOutAnimator.setDuration(1000);// 淡出1s
//        fadeOutAnimator.setRepeatCount(0); // 设置动画重复播放次数 = 重放次数+1

        ObjectAnimator fadeInAnimator = ObjectAnimator.ofFloat(myRecycler,
                "alpha", 0f, 1f);
        fadeInAnimator.setDuration(1000);
//        fadeInAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//           // 如果fadeInAnimator = ValueAnimator.ofInt(0,255) ,也可以使用这个
//            @Override
//            public void onAnimationUpdate(ValueAnimator animation) {
//                int curValue = (int)animation.getAnimatedValue();
//                myRecycler.setAlpha((float)curValue/255);
//                Log.d("now alpha:" + myRecycler.getAlpha()," curValue:"+curValue);
//            }
//        });

        // 丢到一个动画集合里,一起运行
        final AnimatorSet fadeInOut = new AnimatorSet();
        fadeInOut.playTogether(fadeInAnimator,fadeOutAnimator);

        getView().postDelayed(new Runnable() {
            @Override
            public void run() {
                // 这里会在 5s 后执行
                // TODO ex3-4:实现动画,将 lottie 控件淡出,列表数据淡入
                myRecycler.setAlpha(0f); //先设置透明度为0再显示
                myRecycler.setVisibility(View.VISIBLE);
                fadeInOut.start();
            }
        }, 5000);

        //  如果不需要等5s设置透明度,可以用这个delay
        //  fadeInOut.setStartDelay(5000);
        //  fadeInOut.start();

    }

}

  

主要的坑是

  • setRepeatCount()设置的是重复次数而不是运行次数,如果设置为1,是运行两次
  • 对于一个想要延迟淡入的view,首先要设置visibility为GONE,然后在即将淡入前设置透明度为0,visibility为VISIBLE

说明

本例中的不同fragment切换时的动画有时有,有时没有。

这是因为fragment默认是预加载下一个的,所以tab1和2都加载了,再进入tab2时没有加载动画,而到tab3就有动画了。

默认的预加载如果想要取消,需要自定义一个判断当前fragment是否可见的子类,参考https://www.jianshu.com/p/0e2d746e3a3d

 

posted @ 2021-03-08 19:10  evtricks  阅读(230)  评论(0编辑  收藏  举报