1. 程式人生 > >利用view pager和recycler view兩種控制元件寫出有圓點指示器的引導頁

利用view pager和recycler view兩種控制元件寫出有圓點指示器的引導頁

最終效果:

(viewpager方式和recyclerview方式實現效果相同,但是recyclerview沒有轉場動畫)。


  • view pager

viewpager寫引導頁是比較簡單常見的,思路:

1.沉浸式狀態列。

2.建立viewpager並將圖片資料通過adapter設定給viewpager

3.圓點指示器

4.設定切換動畫

1.沉浸式狀態列之前有了解過,在沒有用residemenu等第三方庫的時候,在Android4.4以上通過一下方法能夠成功。

/**
 * 透明化狀態列
 */
public static void TranslucentStatusBar(Activity activity) {
    activity.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Window window = activity.getWindow();
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
                | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(Color.TRANSPARENT);
        window.setNavigationBarColor(Color.TRANSPARENT);
    }
}

2.建立viewpager等程式碼

<android.support.v4.view.ViewPager
    android:id="@+id/view_pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

viewPager = (ViewPager) findViewById(R.id.view_pager);

private List<View> mList = {…};
for (int i = 0; i < mImages.length; i++) {
    ImageView imageView = new ImageView(this);
    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    imageView.setImageResource(mImages[i]);
    mList.add(imageView);
}

PagerAdapter pagerAdapter = new PagerAdapter() {

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0 == arg1;
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView(mList.get(position));
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        container.addView(mList.get(position));
        return mList.get(position);
    }
};

viewPager.setAdapter(pagerAdapter);

3.建立圓點指示器之前沒有寫過,通過查詢資料發現可通過 linearlayout.addView方式新增小圓點群,通過position判斷當前圓點是否被選中 ,從而顯示不同的drawable

程式碼如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item 
	android:drawable="@drawable/ic_select" 
	android:state_selected="true" />
    <item 
	android:drawable="@drawable/ic_unselect" 
	android:state_selected="false"/>
</selector>

<LinearLayout
    android:id="@+id/point_group"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true"
    android:layout_marginBottom="72dp"
    android:orientation=“horizontal”></LinearLayout>


pointGroup = (LinearLayout) findViewById(R.id.point_group);

for (int i = 0; i < mImages.length; i++) {
    ImageView pointImage = new ImageView(this);
    pointImage.setImageResource(R.drawable.pointgroup);
    int PointSize = getResources().getDimensionPixelSize(R.dimen.point_size);
    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(PointSize, PointSize);

    if (i > 0) {
        params.leftMargin = getResources().getDimensionPixelSize(R.dimen.point_margin);
        pointImage.setSelected(false);
    } else {
        pointImage.setSelected(true);
    }
    pointImage.setLayoutParams(params);
    pointGroup.addView(pointImage);
}

首先設定R.drawable.point_group.根據select的值顯示不同的drawable

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        int lastPosition;

        @Override
        public void onPageSelected(int position) {
            pointGroup.getChildAt(position).setSelected(true);
            pointGroup.getChildAt(lastPosition).setSelected(false);
            lastPosition = position;
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });

}

然後設定linearlayout

然後設定viewpager的滑動監聽器pagechangelistener, 當前位置設定為被選擇,當前位置的前一個位置設為不被選擇,當前位置後一個位置(如果有的話)還沒有被設定,所以為未選擇。

4.設定切換動畫,通過viewpagersetpagetransformer方法,判斷當前位置是否顯示,設定透明度偏移量等等。

viewPager.setPageTransformer(true, new ViewPager.PageTransformer() {
    private static final float MIN_SCALE = 0.75f;

    @Override
    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();

        if (position < -1) { // [-Infinity,-1)
            view.setAlpha(0);

        } else if (position <= 0) { // [-1,0]
            view.setAlpha(1);
            view.setTranslationX(0);
            view.setScaleX(1);
            view.setScaleY(1);

        } else if (position <= 1) { // (0,1]
            view.setAlpha(1 - position);
            view.setTranslationX(pageWidth * -position);
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

        } else { // (1,+Infinity]
            view.setAlpha(0);
        }
    }
});

到這裡,viewpager的引導頁就基本完成了,也可以在最後一頁去掉圓點指示器,加上立即體驗按鈕,只需在onpagechangelistener中判斷位置是否是最後一頁,然後對控制元件setvisibility就可以了。

  • recycler view

利用recyclerview來做就不是那麼容易了,思路:

1.沉浸式狀態列。

2.建立recyclerviewadapter和資料繫結,並設定為橫向。

3.讓每一個item自動滑動到螢幕中央,且每次只能滑動一頁item

private void initPoint() {
    //指示器部分
    layout = (RelativeLayout) findViewById(R.id.layout_view);
    pointRecyclerView = new RecyclerView(RSplashActivity.this);
    LinearLayoutManager indicatorLayoutManager = new LinearLayoutManager(RSplashActivity.this
            , LinearLayoutManager.HORIZONTAL, false);
    pointRecyclerView.setLayoutManager(indicatorLayoutManager);
    pointAdapter = new PointAdapter();
    pointRecyclerView.setAdapter(pointAdapter);
    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT);
    params.addRule(RelativeLayout.CENTER_HORIZONTAL);
    params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
    params.bottomMargin = dp2px(72);
    layout.addView(pointRecyclerView, params);
}

public class PointAdapter extends RecyclerView.Adapter {

    int currentPosition = 0;

    public void setPosition(int position) {
        this.currentPosition = position;
    }

    @SuppressLint("ResourceAsColor")
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ImageView imageView = new ImageView(RSplashActivity.this);
        RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        lp.setMargins(dp2px(7), dp2px(7), dp2px(7), dp2px(7));
        imageView.setLayoutParams(lp);
        return new RecyclerView.ViewHolder(imageView) {
        };

    }


    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        ImageView bannerPoint = (ImageView) holder.itemView;
        bannerPoint.setImageResource(currentPosition == position ?
                R.drawable.ic_select : R.drawable.ic_unselect);
    }

    @Override
    public int getItemCount() {
        return mImages.length;
    }
}

protected synchronized void refreshIndicator() {
    pointAdapter.setPosition(currentIndex % 3);
    pointAdapter.notifyDataSetChanged();

}

protected int dp2px(int dp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
            Resources.getSystem().getDisplayMetrics());
}

4.圓點指示器的設定。

1.沉浸式狀態列同viewpager

2.比較容易,程式碼省略。

3.利用snaphelp來解決問題

final PagerSnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);

4.圓點指示器這一步比較難,主要是監聽item的改變並根據位置設定圓點是否選中。當時的思路有以下幾種:

  • 通過recyclerview中的onfing能夠監聽滑動位移xy,將x與測試機螢幕寬度做對比,若成倍數則表示剛好滑動到某個item,則可根據倍數判斷在哪一個位置,從而設定圓點。但是測試得知onfing中獲得的x並不與螢幕畫素點成倍數,故此方法不可行。
  • 通過snaphelperfindtargetsnapposition來獲取當前item位置,通過recyclerviewonscrollchangelistener來監聽,但通過輸出發現此方法也不能準確輸出當前position
  • 通過linearlayoutmanager中監聽方法,但搜尋方法並沒有類似方法。
  • recyclerviewitem中寫好圓點指示器,在adapter通過onbindviewholder中的position來設定圓點指示器,此方法可行,但滑動頁面是圓點指示器也會隨之滑動,故不符合頁面效果。

最後在github上某個demo中看到可行方法並應用到自己demo中,思路是在onscrolled中通過linearlayoutmanager中的方法計算獲得當前position,並在onbindviewholder中動態建立圓點指示器。以下是程式碼:

就是整體把圓點指示器當作recyclerview來做,利用adpater的特性,根據currentIndex的改變來動態更新指示器的drawable。到此,recyclerview的引導頁也實現了。

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        int firstReal = linearLayoutManager.findFirstVisibleItemPosition();
        View viewFirst = linearLayoutManager.findViewByPosition(firstReal);
        if (width != 0 && viewFirst != null) {
            float right = viewFirst.getRight();
            float ratio = right / width;
            if (ratio > 0.8) {
                if (currentIndex != firstReal) {
                    currentIndex = firstReal;
                    refreshIndicator();
                }
            } else if (ratio < 0.2) {
                if (currentIndex != firstReal + 1) {
                    currentIndex = firstReal + 1;
                    refreshIndicator();
                }
            }
        }
    }
});

完整程式碼地址:http://download.csdn.net/download/denglixuan1996/10264986