用过美团和饿了么的app的童鞋应该清楚这一功能。首页菜单可以分页切换,类似我们的banner广告切换效果,只不过只能手动切换。所以整个分页效果,我们可以采用Viewpager实现,里面的菜单项我们则可以采用RecyclerView实现,动态改变里面的菜单项,所以今天我们这个首页分页菜单效果,可以决定采用ViewPager+RecyclerView实现,效果如下:
转载自:https://github.com/xiaohaibin/MeiTuanCategary
1.首页布局文件,代码如下,其中IndicatorView是自己自定义的小圆点指示器控件,也可以使用ViewPagerIndicator库来实现。
2.由于我们分页效果是以ViewPager实现的,所以我们要创建一个ViewPager的适配器,CagegoryViewPagerAdapter.Class
public class CagegoryViewPagerAdapter extends PagerAdapter {
private List mViewList;
public CagegoryViewPagerAdapter(List 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.从上面的适配器我们可以看到泛型为View,也就是每一个ViewPager页面其实都是一个View实例,并且这个View的布局也很简单,仅仅包含一个RecycleView而已,代码如下:
4.接下来就是RecyclerView的菜单项的布局文件
// 此处View的作用是菜单点击时的背景变化效果
selector_trans_divider.xml如下:
5.由于我们的菜单项有一个icon和名称name,为了方便管理,我们可以创建一个菜单项实体类ModelHomeEntrance.class
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.创建一个RecyclerView的菜单项列表适配器,EntranceAdapter.Class
public class EntranceAdapter extends RecyclerView.Adapter {
private List mDatas;
/**
* 页数下标,从0开始(通俗讲第几页)
*/
private int mIndex;
/**
* 每页显示最大条目个数
*/
private int mPageSize;
private Context mContext;
private final LayoutInflater mLayoutInflater;
private List homeEntrances;
public EntranceAdapter(Context context, List 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) {
/**
* 在给View绑定显示的数据时,计算正确的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: 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.最后就是我们的MainActivity的代码实现了,我们整体的思路其实就是需要根据首页菜单项的数据源进行分页显示,首页确定单页菜单显示数量,总数除以单页显示数量取整就是显示页数,我们再根据页数来创建RecyclerView将其添加到ViewPager的适配器中,下面就让我们一起来看看具体是如何的。
public class MainActivity extends AppCompatActivity {
public static final int HOME_ENTRANCE_PAGE_SIZE = 10;//首页菜单单页显示数量
private ViewPager entranceViewPager;
private LinearLayout homeEntranceLayout;
private List 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("美食", R.mipmap.ic_category_0));
homeEntrances.add(new ModelHomeEntrance("电影", R.mipmap.ic_category_1));
homeEntrances.add(new ModelHomeEntrance("酒店住宿", R.mipmap.ic_category_2));
homeEntrances.add(new ModelHomeEntrance("生活服务", R.mipmap.ic_category_3));
homeEntrances.add(new ModelHomeEntrance("KTV", R.mipmap.ic_category_4));
homeEntrances.add(new ModelHomeEntrance("旅游", R.mipmap.ic_category_5));
homeEntrances.add(new ModelHomeEntrance("学习培训", R.mipmap.ic_category_6));
homeEntrances.add(new ModelHomeEntrance("汽车服务", R.mipmap.ic_category_7));
homeEntrances.add(new ModelHomeEntrance("摄影写真", R.mipmap.ic_category_8));
homeEntrances.add(new ModelHomeEntrance("休闲娱乐", R.mipmap.ic_category_10));
homeEntrances.add(new ModelHomeEntrance("丽人", R.mipmap.ic_category_11));
homeEntrances.add(new ModelHomeEntrance("运动健身", R.mipmap.ic_category_12));
homeEntrances.add(new ModelHomeEntrance("大保健", R.mipmap.ic_category_13));
homeEntrances.add(new ModelHomeEntrance("团购", R.mipmap.ic_category_14));
homeEntrances.add(new ModelHomeEntrance("景点", R.mipmap.ic_category_16));
homeEntrances.add(new ModelHomeEntrance("全部分类", R.mipmap.ic_category_15));
}
private void init() {
LinearLayout.LayoutParams layoutParams12 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) ((float) ScreenUtil.getScreenWidth() / 2.0f));
//首页菜单分页
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);
//将RecyclerView放至ViewPager中:
int pageSize = HOME_ENTRANCE_PAGE_SIZE;
//一共的页数等于 总数/每页数量,并取整。
int pageCount = (int) Math.ceil(homeEntrances.size() * 1.0 / pageSize);
List viewList = new ArrayList();
for (int index = 0; index < pageCount; index++) {
//每个页面都是inflate出一个新实例
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.封装的指示器类:
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);
}
}
自定义控件的自定义属性,在values目录下创建attrs.xml文件,内容如下:
9.工具类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.工具类的使用:首先在MyApplication中做初始化:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ScreenUtil.init(this);
}
}