1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Android仿小紅書啟動(dòng)頁平行動(dòng)畫

        共 6863字,需瀏覽 14分鐘

         ·

        2021-10-24 14:33

        其實(shí)之前在很多APP的引導(dǎo)頁上都看到過這個(gè)效果,通過速度不同給人帶來視覺差,感覺很炫酷。


        通過網(wǎng)易云的課做了一個(gè)demo,記錄一下。


        先上圖:


        首先來梳理一下實(shí)現(xiàn)的思路,其實(shí)就是viewpager+fragment,只不過這個(gè)fragment里的控件,會(huì)根據(jù)viewpager的滑動(dòng)產(chǎn)生不同的速度的位移。從架構(gòu)的角度來考慮的話,我們要做到可以隨時(shí)添加fragment,同時(shí)可以隨時(shí)添加fragment里的控件,控件的速度可以設(shè)置。所以要給系統(tǒng)控件來添加自定義屬性。


        attrs文件

        //進(jìn)入的時(shí)候透明度//出去的時(shí)候透明度//進(jìn)入的時(shí)候x方向的速度//出去的時(shí)候x方向的速度//進(jìn)入的時(shí)候Y方向的速度出去的時(shí)候Y方向的速度


        然后是布局文件其中的控件,添加我們自定義的屬性
            android:id="@+id/iv_0"    android:layout_width="103dp"    android:layout_height="19dp"    android:layout_centerInParent="true"    android:src="@drawable/intro1_item_0"    app:x_in="1.2"    app:x_out="1.2" />


        自定義FrameLayout


        為了方便管理fragment和viewpager,我們自定義一個(gè)frameLayout,實(shí)現(xiàn)Viewpager的OnPageChangeListener,提供一個(gè)setUp方法,來添加布局文件,從而新建數(shù)量相同的fragment。
        public void setUp(int... childIds) {
        //fragment集合 fragments = new ArrayList(); for (int i = 0; i < childIds.length; i++) { ParallaxFragment fragment = new ParallaxFragment(); //通過bundle傳遞參數(shù)給fragment Bundle bundle = new Bundle(); bundle.putInt("layoutId", childIds[i]); fragment.setArguments(bundle); fragments.add(fragment); }

        ViewPager vp = new ViewPager(getContext()); vp.setId(R.id.parallax_pager);//從value里的ids拿的
        SplashActivity activity = (SplashActivity) getContext(); parallaxPagerAdapter = new ParallaxPagerAdapter(activity.getSupportFragmentManager(), fragments); vp.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); vp.setAdapter(parallaxPagerAdapter); vp.setOnPageChangeListener(this); addView(vp,0);}


        自定義Fragment

        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        //獲取到布局文件id int rootViewId = getArguments().getInt("layoutId"); //自定義LayoutInflater來處理里面的控件 ParallaxLayoutInflater parallaxLayoutInflater = new ParallaxLayoutInflater(inflater,getActivity(),this); View rootView = parallaxLayoutInflater.inflate(rootViewId, null);
        return rootView;}


        自定義LayoutInflater


        對于我們自定義的fragment ,我們同樣需要通過自定義layoutInflater來對控件的布局進(jìn)行管理,自定義的layoutInflater里最重要的就是要實(shí)現(xiàn)setFactory2這個(gè)方法,自定義一個(gè)Factory,通過里面的onCreateView方法,來獲取控件的自定義屬性,再把所有的自定義屬性都通過ParallaxTag添加到View的tag里。這里要注意區(qū)分是系統(tǒng)控件還是自定義控件,也要考慮兼容自定義控件。
        class ParallaxFactory implements Factory2 {
        private LayoutInflater layoutInflater; //系統(tǒng)控件的前綴 private String[] sClassPrefix = { "android.widget.", "android.view." }; //系統(tǒng)控件自定義的屬性 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};

        public ParallaxFactory(LayoutInflater layoutInflater) { this.layoutInflater = layoutInflater; }
        /** * 反射機(jī)制 * * @param parent 頂級容器 * @param name 控件名字(像RelativeLayout,ImageView)如果是自定義控件的話 返回的是全路徑名字 * @param context * @param attrs 控件的屬性(width,height) * @return */ @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
        View view = null;
        view = createMyView(name, context, attrs);
        if (view != null) {
        //attrs 控件的所有屬性,attrIds 是想要獲取的控件的屬性 TypedArray array = context.obtainStyledAttributes(attrs, attrIds); if (array != null && array.length() > 0) { ParallaxViewTag viewTag = new ParallaxViewTag(); viewTag.alphaIn = array.getFloat(0, 0f); viewTag.alphaOut = array.getFloat(1, 0f); viewTag.xIn = array.getFloat(2, 0f); viewTag.xOut = array.getFloat(3, 0f); viewTag.yIn = array.getFloat(4, 0f); viewTag.yOut = array.getFloat(5, 0f); view.setTag(R.id.parallax_view_tag, viewTag);
        } fragment.getParallaxViews().add(view); array.recycle(); }
        return view; }
        /** * 創(chuàng)建view * @param name * @param context * @param attrs * @return */ private View createMyView(String name, Context context, AttributeSet attrs) {
        //如果是系統(tǒng)的控件,不會(huì)有. 如果是自定義控件的話 含有. if (name.contains(".")) { return reflectView(name, null, attrs); } else { //循環(huán)兩個(gè)包 for (String prefix : sClassPrefix) { View view = reflectView(name, prefix, attrs); if (view != null) { return view; } }
        }
        return null; }
        /** * @param name 控件名字 * @param prefix 控件前綴 * @param attrs 控件屬性 * @return */ private View reflectView(String name, String prefix, AttributeSet attrs) { try { //通過系統(tǒng)的layoutInflater創(chuàng)建視圖,讀取系統(tǒng)屬性 return layoutInflater.createView(name, prefix, attrs); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; }
        @Override public View onCreateView(String name, Context context, AttributeSet attrs) { return null; }}


        最后我們就要實(shí)現(xiàn),根據(jù)viewpager的滑動(dòng)來控制Fragment里不同控件的不同移動(dòng)速度,這里要區(qū)分fragment是進(jìn)入狀態(tài)還是退出狀態(tài)
        @Override    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {        //動(dòng)畫操作
        int containerWidth = getWidth();
        //獲取到退出 ParallaxFragment outFragment = null; try { outFragment = fragments.get(position - 1); } catch (Exception e) { e.printStackTrace(); }
        //獲取到進(jìn)入的頁面 ParallaxFragment inFragment = null; try { inFragment = fragments.get(position); } catch (Exception e) { }

        if (outFragment != null) { //獲取Fragment上所有的視圖,實(shí)現(xiàn)動(dòng)畫效果 List inViews = outFragment.getParallaxViews();// 動(dòng)畫 if (inViews != null) { for (View view : inViews) { 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 outViews = inFragment.getParallaxViews(); if (outViews != null) { for (View view : outViews) { ParallaxViewTag tag = (ParallaxViewTag) view.getTag(R.id.parallax_view_tag); if (tag == null) { continue; } //仔細(xì)觀察退出的fragment中view從原始位置開始向上移動(dòng),translationY應(yīng)為負(fù)數(shù) ViewHelper.setTranslationY(view, 0 - positionOffsetPixels * tag.yOut); ViewHelper.setTranslationX(view, 0 - positionOffsetPixels * tag.xOut); } }????????} }


        最后總結(jié)一下的話,其實(shí)就是對于自定義控件的實(shí)現(xiàn),要做到熟練,考慮實(shí)現(xiàn)過程的時(shí)候,要盡量做到可擴(kuò)展性,而不是單獨(dú)實(shí)現(xiàn)功能就可以。

        源碼地址:
        https://github.com/wangxueshen/NetEaseDemo

        到這里就結(jié)束啦。
        瀏覽 111
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产交换配乱淫视频a免费 | 国产91亚洲成人精品观看 | 国产理论片午午午伦夜理片2021 | 五级黄高潮片90分钟 | 正在播放国产视频 | 在线观看三级 | 亚洲黄在线观看 | 久久久久久午夜 | 亚洲AV无码专区在线 | gogo西西人体做爰大胆视频图 |