<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android仿小紅書啟動頁平行動畫

          共 6863字,需瀏覽 14分鐘

           ·

          2021-10-24 14:33

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


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


          先上圖:


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


          attrs文件

          //進(jìn)入的時候透明度//出去的時候透明度//進(jìn)入的時候x方向的速度//出去的時候x方向的速度//進(jìn)入的時候Y方向的速度出去的時候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,我們自定義一個frameLayout,實現(xiàn)Viewpager的OnPageChangeListener,提供一個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里最重要的就是要實現(xiàn)setFactory2這個方法,自定義一個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)的控件,不會有. 如果是自定義控件的話 含有. if (name.contains(".")) { return reflectView(name, null, attrs); } else { //循環(huán)兩個包 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; }}


          最后我們就要實現(xiàn),根據(jù)viewpager的滑動來控制Fragment里不同控件的不同移動速度,這里要區(qū)分fragment是進(jìn)入狀態(tài)還是退出狀態(tài)
          @Override    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {        //動畫操作
          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上所有的視圖,實現(xiàn)動畫效果 List inViews = outFragment.getParallaxViews();// 動畫 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從原始位置開始向上移動,translationY應(yīng)為負(fù)數(shù) ViewHelper.setTranslationY(view, 0 - positionOffsetPixels * tag.yOut); ViewHelper.setTranslationX(view, 0 - positionOffsetPixels * tag.xOut); } }????????} }


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

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

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

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  午夜欧美成人电影 | 欧美mv日韩mv国产 | 一级片在线直播 | 亚洲精品成人片在线播放波多野吉 | 黄色美女特极A毛片 |