ViewPager+RecycleView for Android Project Implementation of Home Paging Navigation Menu

Keywords: Mobile Android encoding xml github

Design sketch

Children's shoes that have used beauty troupes and hungry app s should be aware of this function. Home menu can be paginated switching, similar to our banner advertising switching effect, but can only be manually switched. So the whole paging effect can be achieved by Viewpager, and the menu items in it can be realized by Recycler View, which can dynamically change the menu items in it. So today, we can decide to use ViewPager + Recycler View to achieve the effect as follows:

Reprinted from: https://github.com/xiaohaibin/MeiTuanCategary

Begin to achieve

1. Home page layout file, code as follows, where IndicatorView is a small dot indicator encapsulated by itself, can also be implemented using the ViewPager Indicator library.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:id="@+id/home_entrance"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="vertical">

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

    <com.stx.xhb.meituancategorydemo.widget.IndicatorView
        android:id="@+id/main_home_entrance_indicator"
        android:layout_width="match_parent"
        android:layout_height="32dp"
        android:layout_marginLeft="16dp"
        android:layout_gravity="bottom"
        android:layout_marginRight="16dp"
        app:gravity="0"
        app:indicatorColor="#668b8989"
        app:indicatorColorSelected="#FF5722"
        app:indicatorWidth="6"/>

</LinearLayout>

2. Since our paging effect is implemented with ViewPager, we will create an adapter for ViewPager, CagegoryViewPager Adapter. Class.

public class CagegoryViewPagerAdapter extends PagerAdapter {

    private List<View> mViewList;
    public CagegoryViewPagerAdapter(List<View> mViewList) {
        this.mViewList = mViewList;
    }

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

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

    @Override
    public int getCount() {
        if (mViewList == null)
            return 0;
        return mViewList.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }
}

3. From the adapter above, we can see that the generic form is View, that is, every ViewPager page is actually a View instance, and the layout of the View is very simple, only contains a RecycleView, the code is as follows:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView 
    xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
	android:layout_height="wrap_content"/>

4. Next is the layout file for RecyclerView's menu items.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:padding="6dp">

        <ImageView
            android:id="@+id/entrance_image"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_margin="2dp"
            android:layout_weight="1"
            android:scaleType="fitCenter"/>

        <TextView
            android:id="@+id/entrance_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="2dp"
            android:singleLine="true"
            android:textColor="#80000000"
            android:textSize="12dp"/>
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/selector_trans_divider"/>

</FrameLayout>

5. Since our menu item has an icon and a name, we can create a menu item entity class ModelHomeEntrance.class for easy management.

public class ModelHomeEntrance {
    private String name = "";
    private int image;

    public ModelHomeEntrance(String name, int image) {
        this.image = image;
        this.name = name;
    }


    public int getImage() {
        return image;
    }

    public String getName() {
        return name;
    }
}

6. Create a RecyclerView menu item list adapter, EntranceAdapter.Class

public class EntranceAdapter extends RecyclerView.Adapter<EntranceAdapter.EntranceViewHolder> {

    private List<ModelHomeEntrance> mDatas;

    /**
     * Page subscripts, starting from 0 (popular on which page)
     */
    private int mIndex;

    /**
     * Maximum number of items displayed per page
     */
    private int mPageSize;

    private Context mContext;

    private final LayoutInflater mLayoutInflater;

    private List<ModelHomeEntrance> homeEntrances;

    public EntranceAdapter(Context context, List<ModelHomeEntrance> datas, int index, int pageSize) {
        this.mContext = context;
        this.homeEntrances = datas;
        mPageSize = pageSize;
        mDatas = datas;
        mIndex = index;
        mLayoutInflater = LayoutInflater.from(context);

    }

    @Override
    public EntranceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new EntranceViewHolder(mLayoutInflater.inflate(R.layout.item_home_entrance, null));
    }

    @Override
    public void onBindViewHolder(EntranceViewHolder holder, final int position) {
        /**
         * When binding data to View, calculate the correct position = position + mIndex * mPageSize.
         */
        final int pos = position + mIndex * mPageSize;
        holder.entranceNameTextView.setText(homeEntrances.get(pos).getName());
        holder.entranceIconImageView.setImageResource(homeEntrances.get(pos).getImage());
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ModelHomeEntrance entrance = homeEntrances.get(pos);
                // TODO: Click Event 2017/5/24
            }
        });
    }

    @Override
    public int getItemCount() {
        return mDatas.size() > (mIndex + 1) * mPageSize ? mPageSize : (mDatas.size() - mIndex * mPageSize);
    }

    @Override
    public long getItemId(int position) {
        return position + mIndex * mPageSize;
    }

    class EntranceViewHolder extends RecyclerView.ViewHolder {

        private TextView entranceNameTextView;
        private ImageView entranceIconImageView;

        public EntranceViewHolder(View itemView) {
            super(itemView);
            entranceIconImageView = (ImageView) itemView.findViewById(R.id.entrance_image);
            entranceNameTextView = (TextView) itemView.findViewById(R.id.entrance_name);
            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) ((float) ScreenUtil.getScreenWidth() / 4.0f));
            itemView.setLayoutParams(layoutParams);
        }
    }
}

7. Finally, our MainActivity code is implemented. Our overall idea is that we need to paginate the display according to the data source of the home menu item. The home page determines the number of single-page menu display. The total number divided by the number of single-page display is the number of display pages. We then create RecyclerView according to the number of pages to add it to the adapter of ViewPager. Let's do the following. Let's see how it works.

public class MainActivity extends AppCompatActivity {
    public static final int HOME_ENTRANCE_PAGE_SIZE = 10;//Home menu single page display number
    private ViewPager entranceViewPager;
    private LinearLayout homeEntranceLayout;
    private List<ModelHomeEntrance> homeEntrances;
    private IndicatorView entranceIndicatorView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();
        init();
    }


    private void initView() {
        homeEntranceLayout = (LinearLayout) findViewById(R.id.home_entrance);
        entranceViewPager = (ViewPager) findViewById(R.id.main_home_entrance_vp);
        entranceIndicatorView = (IndicatorView) findViewById(R.id.main_home_entrance_indicator);
    }


    private void initData() {
        homeEntrances = new ArrayList<>();
        homeEntrances.add(new ModelHomeEntrance("Delicious food", R.mipmap.ic_category_0));
        homeEntrances.add(new ModelHomeEntrance("Film", R.mipmap.ic_category_1));
        homeEntrances.add(new ModelHomeEntrance("Hotel accommodation", R.mipmap.ic_category_2));
        homeEntrances.add(new ModelHomeEntrance("Life service", R.mipmap.ic_category_3));
        homeEntrances.add(new ModelHomeEntrance("KTV", R.mipmap.ic_category_4));
        homeEntrances.add(new ModelHomeEntrance("Tourism", R.mipmap.ic_category_5));
        homeEntrances.add(new ModelHomeEntrance("Learning and training", R.mipmap.ic_category_6));
        homeEntrances.add(new ModelHomeEntrance("Automobile service", R.mipmap.ic_category_7));
        homeEntrances.add(new ModelHomeEntrance("Photographer", R.mipmap.ic_category_8));
        homeEntrances.add(new ModelHomeEntrance("Recreation & Entertainment", R.mipmap.ic_category_10));
        homeEntrances.add(new ModelHomeEntrance("beauty", R.mipmap.ic_category_11));
        homeEntrances.add(new ModelHomeEntrance("Sports fitness", R.mipmap.ic_category_12));
        homeEntrances.add(new ModelHomeEntrance("Big health care", R.mipmap.ic_category_13));
        homeEntrances.add(new ModelHomeEntrance("Group buying", R.mipmap.ic_category_14));
        homeEntrances.add(new ModelHomeEntrance("Scenic spot", R.mipmap.ic_category_16));
        homeEntrances.add(new ModelHomeEntrance("All categories", R.mipmap.ic_category_15));
    }

    private void init() {
        LinearLayout.LayoutParams layoutParams12 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) ((float) ScreenUtil.getScreenWidth() / 2.0f));

        //Home menu Pagination
        FrameLayout.LayoutParams entrancelayoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, (int) ((float) ScreenUtil.getScreenWidth() / 2.0f + 70));
        homeEntranceLayout.setLayoutParams(entrancelayoutParams);
        entranceViewPager.setLayoutParams(layoutParams12);
        LayoutInflater inflater = LayoutInflater.from(this);
        //Place Recycler View in ViewPager:
        int pageSize = HOME_ENTRANCE_PAGE_SIZE;
        //The total number of pages is equal to the total number of pages per page and is rounded.
        int pageCount = (int) Math.ceil(homeEntrances.size() * 1.0 / pageSize);
        List<View> viewList = new ArrayList<View>();
        for (int index = 0; index < pageCount; index++) {
            //Each page is inflate with a new instance
            RecyclerView recyclerView = (RecyclerView) inflater.inflate(R.layout.item_home_entrance_vp, entranceViewPager, false);
            recyclerView.setLayoutParams(layoutParams12);
            recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.this, 5));
            EntranceAdapter entranceAdapter = new EntranceAdapter(MainActivity.this, homeEntrances, index, HOME_ENTRANCE_PAGE_SIZE);
            recyclerView.setAdapter(entranceAdapter);
            viewList.add(recyclerView);
        }
        CagegoryViewPagerAdapter adapter = new CagegoryViewPagerAdapter(viewList);
        entranceViewPager.setAdapter(adapter);
        entranceIndicatorView.setIndicatorCount(entranceViewPager.getAdapter().getCount());
        entranceIndicatorView.setCurrentIndicator(entranceViewPager.getCurrentItem());
        entranceViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                entranceIndicatorView.setCurrentIndicator(position);
            }
        });
    }
}

8. Packaged indicator classes:

public class IndicatorView extends View {

    private int indicatorColor = Color.rgb(0, 0, 0);
    private int indicatorColorSelected = Color.rgb(0, 0, 0);
    private int indicatorWidth = 0;
    private int gravity = 0;

    private int indicatorCount = 0;
    private int currentIndicator = 0;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0x12) {
                invalidate();
            }
        }
    };

    public IndicatorView(Context context) {
        super(context);
    }

    public IndicatorView(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (attrs != null) {
            TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.IndicatorView);
            indicatorColor = typedArray.getColor(R.styleable.IndicatorView_indicatorColor, Color.rgb(0, 0, 0));
            indicatorColorSelected = typedArray.getColor(R.styleable.IndicatorView_indicatorColorSelected, Color.rgb(0, 0, 0));
            indicatorWidth = ScreenUtil.dip2px(typedArray.getInt(R.styleable.IndicatorView_indicatorWidth, 0));
            gravity = typedArray.getInt(R.styleable.IndicatorView_gravity, 0);
            typedArray.recycle();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int viewWidth = getWidth();
        int viewHeight = getHeight();
        int totalWidth = indicatorWidth * (2 * indicatorCount - 1);

        Paint paint = new Paint();
        paint.setAntiAlias(true);

        if (indicatorCount > 0) {
            for (int i = 0; i < indicatorCount; i++) {
                if (i == currentIndicator) {
                    paint.setColor(indicatorColorSelected);
                } else {
                    paint.setColor(indicatorColor);
                }
                int left = (viewWidth - totalWidth) / 2 + (i * 2 * indicatorWidth);
                switch (gravity) {

                    case 0:
                        left = (viewWidth - totalWidth) / 2 + (i * 2 * indicatorWidth);
                        break;

                    case 1:
                        left = i * 2 * indicatorWidth;
                        break;

                    case 2:
                        left = viewWidth - totalWidth + (i * 2 * indicatorWidth);
                        break;

                }
                int top = (viewHeight - indicatorWidth) / 2;
                int right = left + indicatorWidth;
                int bottom = top + indicatorWidth;
                RectF rectF = new RectF(left, top, right, bottom);
                canvas.drawOval(rectF, paint);
            }
        }
    }

    public void setIndicatorCount(int indicatorCount) {
        this.indicatorCount = indicatorCount;
    }

    public void setCurrentIndicator(int currentIndicator) {
        this.currentIndicator = currentIndicator;
        handler.sendEmptyMessage(0x12);
    }
}

9. Tool class ScreenUtil

public class ScreenUtil {

    static double scale;
    static int screenWidth = 0, screenHeight = 0;

    public static void init(Context context) {
        scale = context.getResources().getDisplayMetrics().density;
        screenWidth = context.getResources().getDisplayMetrics().widthPixels;
        screenHeight = context.getResources().getDisplayMetrics().heightPixels;
    }

    public static int dip2px(float dipValue) {
        return (int) (dipValue * scale + 0.5f);
    }

    public static int px2dip(float pxValue) {
        return (int) (pxValue / scale + 0.5f);
    }

    public static int px2sp(float pxValue) {
        return (int) (pxValue / scale + 0.5f);
    }

    public static int getScreenHeight() {
        return screenHeight;
    }

    public static int getScreenWidth() {
        return screenWidth;
    }
}

10. Use of tool classes: First initialize in MyApplication:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        ScreenUtil.init(this);
    }
}

Posted by Bijan on Fri, 01 Feb 2019 09:54:15 -0800