Android仿小红书启动页平行动画

实现效果

 

需要注意的:

view.setTag()和view.getTag()

View中的setTag(Object)表示给View添加一个格外的数据,以后可以用getTag()将这个数据取出来。

 

实现思路:

通过ViewPager加载Fragment,在Fragment中的系统控件中加入我们的自定义属性。然后我们通过解析自定义属性来实现平行动画。

首先我们可以在Fragment的系统的控件中加入我们自定义的属性。

<ImageView
    app:x_in="0.8"
    app:x_out="0.8" />

那么现在要解决的一个问题就是如何获取到系统控件中自定义属性的值。

我们可以通过继承LayoutInflater,调用setFactory2(new ParallaxFactory(this)),实现我们自己的ParallaxLayoutInflater类,然后再自定义一个类ParallaxFactory实现Factory2接口,在这个ParallaxFactory类中我们可以获取到系统控件中的所有属性和值,包括我们自定义的属性。

获取到自定义属性的值之后,先通过setTag()来保存view自定义属性的值,然后将fragment中的所有View保存起来,最后在viewPager的onPageScrolled()方法中获取到每个View的自定义属性的值,从而实现平行动画。

1.定义一个ParallaxViewTag类,将我们给View传递的数据封装起来

public class ParallaxViewTag {
​
    protected int index;
    protected float xIn;
    protected float xOut;
    protected float yIn;
    protected float yOut;
    protected float alphaIn;
    protected float alphaOut;
​
    @NonNull
    @Override
    public String toString() {
        return "ParallaxViewTag [index=" + index + ", xIn=" + xIn + ", xOut="
                + xOut + ", yIn=" + yIn + ", yOut=" + yOut + ", alphaIn="
                + alphaIn + ", alphaOut=" + alphaOut + "]";
    }
}

2.定义Fragment来加载布局文件

public class ParallaxFragment extends Fragment {
​
    //在此Fragment上实现所有的视差动画
​
    private List<View> parallaxViews = new ArrayList<>();
​
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        
        //通过Bundle获取布局文件Id
        Bundle args = getArguments();
        int layoutId = args.getInt("layoutId");
        ParallaxLayoutInflater inflater1 = new ParallaxLayoutInflater(inflater, getActivity(), this);
​
        return inflater1.inflate(layoutId, null);
    }
​
    public List<View> getParallaxViews(){
        return parallaxViews;
    }
}

3.定义ViewPager的适配器Adapter

public class ParallaxPagerAdapter extends FragmentPagerAdapter {
​
    private List<ParallaxFragment> fragments;
​
    public ParallaxPagerAdapter(FragmentManager fragmentManager, List<ParallaxFragment> fragments){
        super(fragmentManager);
        this.fragments = fragments;
​
    }
​
    @NonNull
    @Override
    public Fragment getItem(int position) {
        return fragments.get(position);
    }
​
    @Override
    public int getCount() {
        return fragments.size();
    }
}

4.定义我们自己的ParallaxLayoutInflater,继承系统的LayoutInflater,在这个类中我们要通过监听系统解析xml的过程,来将我们自己定义的属性的值提取出来,然后通过setTag()方式保存起来,以备后面调用。

关键代码:
    class ParallaxFactory implements Factory2{
​
        private final String[] sClassPrefix = {
                "android.widget.",
                "android.view."
        };
​
        int[] attrIds = {
                R.attr.a_in,
                R.attr.a_out,
                R.attr.x_in,
                R.attr.x_out,
                R.attr.y_in,
                R.attr.y_out};
​
        private LayoutInflater inflater;
​
        public ParallaxFactory(LayoutInflater inflater){
            this.inflater = inflater;
        }
​
        @Nullable
        @Override
        public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
            View view =createMyView(name, context, attrs);
            if (view != null){
                TypedArray array = context.obtainStyledAttributes(attrs, attrIds);
​
                if (array != null && array.length() > 0){
​
                    //获取自定义的属性
                    ParallaxViewTag tag = new ParallaxViewTag();
                    tag.alphaIn = array.getFloat(0, 0f);
                    tag.alphaOut = array.getFloat(1, 0f);
                    tag.xIn = array.getFloat(2, 0f);
                    tag.xOut = array.getFloat(3, 0f);
                    tag.yIn = array.getFloat(4, 0f);
                    tag.yOut = array.getFloat(5, 0f);
                    view.setTag(R.id.parallax_view_tag,tag);
                }
​
                fragment.getParallaxViews().add(view);
                array.recycle();
            }
​
            Log.i(TAG, "onCreateView: " + view);
​
            return view;
        }
​
        @Nullable
        @Override
        public View onCreateView(@NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
​
            return null;
        }
​
        private View createMyView(String name, Context context, AttributeSet attributeSet){
            if (name.contains(".")){
                //自定义的控件
                return reflectView(name, null, context, attributeSet);
            }else {
                //轮训我们在系统控件中定义的属性
                for (String prefix:sClassPrefix) {
                    View view = reflectView(name, prefix, context, attributeSet);
​
                    if (view != null)
                    {
                        return view;
                    }
                }
​
            }
            return null;
        }
​
        private View reflectView(String name, String prefix, Context context, AttributeSet attrs){
​
            try {
                return inflater.createView(name, prefix, attrs);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        }
    }

5.提供一个ParallaxContainer类来控制所有的fragment的加载和动画

导入ftagment数组关键代码:
    public void setUp(int... childIds){
        //fragments数组
        fragments = new ArrayList<ParallaxFragment>();
​
        for (int i = 0; i < childIds.length; i++) {
            ParallaxFragment fragment = new ParallaxFragment();
​
            //Fragment中需要加载的布局文件id
            Bundle args = new Bundle();
            args.putInt("layoutId", childIds[i]);
            fragment.setArguments(args);
            fragments.add(fragment);
        }
​
        ViewPager vp = new ViewPager(getContext());
        vp.setId(R.id.parallax_pager);
​
        //实例化适配器
        SplashActivity activity = (SplashActivity) getContext();
        adapter = new ParallaxPagerAdapter(activity.getSupportFragmentManager(), fragments);
//        vp.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        vp.setAdapter(adapter);
        vp.setOnPageChangeListener(this);
        addView(vp, 0);
    }
    
    //控制动画关键代码
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
​
        int containerWidth = getWidth();
​
        ParallaxFragment outFragment = null;
        try {
            outFragment = fragments.get(position - 1);
        } catch (Exception e) {}
​
        ParallaxFragment inFragment = null;
        try {
            inFragment = fragments.get(position);
        } catch (Exception e) {}
​
        if (outFragment != null){
            List<View> outViews = outFragment.getParallaxViews();
            if (outViews != null){
                for (View view:outViews) {
                    ParallaxViewTag tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag);
                    if (tag == null){
                        continue;
                    }
​
                    ViewHelper.setTranslationX(view, (containerWidth - positionOffsetPixels) * tag.xIn);
                    ViewHelper.setTranslationY(view, (containerWidth - positionOffsetPixels) * tag.yIn);
                }
            }
​
        }
​
        if (inFragment != null){
            List<View> inViews = inFragment.getParallaxViews();
            if (inViews != null){
                for (View view:inViews) {
                    ParallaxViewTag tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag);
                    if (tag == null){
                        continue;
                    }
​
                    //仔细观察退出的fragment中view从原始位置开始向上移动,translationY应为负数
                    ViewHelper.setTranslationY(view, 0 - positionOffsetPixels * tag.yOut);
                    ViewHelper.setTranslationX(view, 0 - positionOffsetPixels * tag.xOut);
                }
            }
​
        }
​
    }

通过在系统控件中加入自定义属性来实现平行动画,扩展性好,启动页的UI不管怎么变化,我们只需要的简单的几步就能实现所需要的效果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
<h3>回答1:</h3><br/>要使用Android Studio仿小红书应用程序,您需要具备以下技能和知识: 1. Java编程语言:Java是Android应用程序的主要编程语言,您需要掌握Java语言的基础知识和高级特性。 2. Android SDK:Android SDK是开发Android应用程序所需的工具和库。您需要掌握使用Android SDK构建应用程序的技能,包括使用XML和Java编写应用程序布局、编写代码逻辑和处理用户输入。 3. Android Studio:Android Studio是用于开发Android应用程序的IDE。您需要了解如何使用Android Studio创建新项目、编写代码、调试应用程序并生成APK文件。 4. 数据库设计:小红书应用程序涉及到大量的用户生成内容,因此您需要掌握基本的数据库设计知识,如使用SQLite或其他数据库管理系统来存储和管理数据。 5. 用户界面设计:小红书应用程序具有独特的用户界面设计,包括动画效果、卡片式布局和精美的图像设计。您需要了解如何使用XML和Java实现这些界面元素。 6. 前端技术:小红书应用程序的前端技术包括HTML、CSS和JavaScript。您需要了解这些技术以便构建小红书应用程序的Web视图。 7. 后端技术:小红书应用程序还涉及到后端技术,如REST API和服务器端编程。您需要了解如何使用这些技术来实现小红书应用程序的服务器端功能。 以上是开发仿小红书应用程序所需的技能和知识。需要不断学习和实践,才能成为一个优秀的Android开发者。 <h3>回答2:</h3><br/>说到仿小红书,我们首先想到的是什么?模仿小红书APP的UI设计、数据存储方式以及交互方式,那么我们应该如何在Android Studio中实现仿小红书的APP呢? 1.设计界面 首先你需要了解到小红书UI设计,然后在Android Studio中根据设计稿使用XML实现iOS版小红书UI界面。也可以使用一些实现富文本、瀑布流等效果的第三方库,它们会更方便你的实现。 2.数据存储 小红书的数据量非常大,它的数据存储一定非常复杂。你可以使用SQLite数据库来实现文本存储,使用媒体数据库来实现图片和视频的存储。这些数据库比较常用,使用它们可以更方便的将数据存储到本地。 3.交互 小红书的交互特征主要是采用物理滑动的方式来实现浏览偏好的美妆范围的功能。所以如何对用户输入进行操作也是本APP的一个非常重要的特征。我们可以使用通用的手势库来实现诸如下滑刷新、上滑查看更多、详情面下拉放大等常用的交互特征。 总之,仿小红书APP并不容易,但是在Android Studio里,我们可以通过界面美观、数据存储、交互方式来实现。当然,这需要花费大量的时间和精力,你也可以选择一些优秀的开源APP进行学习借鉴。 <h3>回答3:</h3><br/>Android Studio是一个非常流行的Android应用程序开发工具,能够帮助程序员快速开发各种类型的Android应用程序。而仿小红书的应用程序,则需要充分利用Android Studio提供的各种工具和功能,以便实现类似小红书的功能和用户体验。 首先,开发人员需要确保应用程序实现了类似小红书的基本功能,例如用户注册、登录、创建账号和发布信息等功能。此外,用户可以通过浏览、点赞、收藏、评论和分享等交互方式来与其他用户互动。 其次,Android Studio提供了许多有用的布局和UI控件,例如RecyclerView、CardView、BottomNavigationView和Material Design组件等。这些布局和控件可用于构建美观、易于使用和响应性强的用户界面。 第三,数据存储和管理是构建类似小红书的应用程序的另一个关键方面。 Android Studio支持使用SQLite、Room和Firebase等数据存储方案,这些方案可以使开发人员轻松地管理和存储应用程序的数据。 最后,开发人员还需要考虑应用程序的性能和安全性。在构建应用程序时,必须考虑到内存使用率、CPU利用率和网络通信速度等因素。此外,应用程序还需要采取必要的安全措施,例如数据加密和用户身份验证,以保护用户和应用程序的敏感信息。 总之,使用Android Studio仿小红书需要开发人员充分利用该工具的各种功能和工具,并在数据管理、用户体验、性能和安全性方面进行适当的考虑。通过正确地使用这些工具和技术,开发人员可以构建出功能完善的类似小红书的应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值