如今的很多app都在首页有一个可以滑动的导航,像美团App在首页上也有,如图:
还有很多的app也有,比如京东、饿了么等等,这个功能实现起来其实也不难,有RecycleView就可以,因为RecycleView有一个网格的布局,但是为了使用方便,还有就是以后其他的项目也需要用到,所以我把这个功能做了一个封装,全都封装在一个.java类里面,使用的时候就只需要在布局中引用该控件,同时在Activity或是Fragment中添加几行代码就可以使用整个功能。
这里将分别使用 java代码 跟 kotlin代码 实现这个功能
我封装的类名为EntryListView.java,将这个类放到自己的项目中。
一。先看使用方式
在布局中引用该控件
在布局对应activity或是Fragment中添加如下代码
mEntryList是我封装的EntryListView.java这个类的对象,第一行代码是设置行数跟列数,第二行代码中的listEntry是一个List对象,里面存放着每个item的内容,比如可以这样写,其中Entry.java是存放信息的实体类
好了这样就可以实现整个功能了,给大家看一下我自己这边的效果图
二。简单说一下我封装的EntryListView.java里面实现的原理,我这里使用的是ViewPager来实现滑动切换的,ViewPager的每一页都是一个GridView,这个大家都知道,他就是网格布局,然后网格布局里面的每一个item,就是一个图标跟一个标题,整个过程都是动态创建布局,没有引用到布局文件,只有这样移植性才比较强,只需要把我的这个EntryListView.java引用到自己的项目中,通过上面的方法是使用,就可以了。
下面是大家关系的问题,那就是源码啦,下面就是源码,里面注释很清晰,看了都懂,直接拿走不谢,觉得好的话多关注点个喜欢就行(微笑)
java代码
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import qin.zszc.com.basiclib.R;
import qin.zszc.com.basiclib.support.imageloader.ImageLoader;
import qin.zszc.com.basiclib.widget.custom.entrynavigation.model.Entry;
/**
* Created by xcfor on 2018/7/11.
* 首页导航栏
*/
public class EntryListView extends FrameLayout implements ViewPager.OnPageChangeListener {
private final int IND_WIDTH_SIZE = 6; //指示器默认宽的长度
private Context mContext;
private ViewPager mViewPager;
private PagerAdapter mAdapter;
private MyGridViewAdapter mMyGridViewAdpter;
private LinearLayout mIndecatorLayout;
private int mColumNum = 5;//多少列
private int mRowNum = 2;//多少行
private int mPageSize = 8;//每页多少个
private int mCount;//滑动页面的数量
private int mPrePosition = 0; //历史页pos
private int mIndicatorWidth;//指示器宽度
private int mIndicatorHight = 3;//指示器高度
private int mCurrentPos = 0;//当前页pos
private int mIconSize = 40; //item中图片的宽高
private int mTextSize = 11; //item中字体的大小
private List<List<Entry>> mResultList;//分页后的数据列表
private List<Entry> mLists;//获取到的数据列表
private List<View> mItemView = new ArrayList<>();//存放gridView的列表
public EntryListView(@NonNull Context mContext) {
super(mContext);
this.mContext = mContext;
}
public EntryListView(@NonNull Context mContext, @Nullable AttributeSet attrs) {
super(mContext, attrs);
this.mContext = mContext;
}
public EntryListView(@NonNull Context mContext, @Nullable AttributeSet attrs, int defStyleAttr) {
super(mContext, attrs, defStyleAttr);
this.mContext = mContext;
}
public void setPageSize(int rowNum,int colNum){
mRowNum=rowNum;
mColumNum=colNum;
mPageSize=rowNum*colNum;
}
public void init() {
if (mAdapter == null) {
return;
}
mViewPager.setAdapter(mAdapter);
//切换画面
mViewPager.setPageTransformer(true, new EntryListView.MyPageTransformer());
mIndecatorLayout.removeAllViews();
//向线性布局中添加小圆点指示器
View line;
LinearLayout.LayoutParams params;
for (int i = 0; i < mCount; i++) {
line = new View(mContext);
line.setId(i);
params = new LinearLayout.LayoutParams(dip2px(mIndicatorWidth), dip2px(mIndicatorHight));
params.setMargins(dip2px(3), 0, dip2px(3), 0);
line.setLayoutParams(params);
line.setBackgroundResource(R.drawable.basiclib_line_bg_selector);
line.setEnabled(false);//默认设为非选中
mIndecatorLayout.setGravity(Gravity.CENTER_HORIZONTAL);
mIndecatorLayout.addView(line);
}
mIndecatorLayout.getChildAt(0).setEnabled(true);
mViewPager.setCurrentItem(0);
}
/**
* 获取数据
* @param mLists
*/
public void setData(List<Entry> mLists) {
if (mLists.isEmpty()) {
return;
}
this.mLists = mLists;
mCount = pageNum();
cutOutList(mLists, mPageSize);
if (mAdapter == null) {
mAdapter = new EntryListView.MyEntryAdapter(mItemView);
}
init();
}
/**
* 根据手机的分辨率从 dip 的单位 转成为 px(像素)
*/
private int dip2px(float dpValue) {
final float scale = mContext.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 按照size的大小截取list
*
* @param lists
* @param size
* @return
*/
public List<List<Entry>> cutOutList(List<Entry> lists, int size) {
mResultList = new ArrayList<>();
int arrSize = lists.size() % size == 0 ? lists.size() / size : lists.size() / size + 1;
for (int i = 0; i < arrSize; i++) {
List<Entry> sub = new ArrayList<>();
for (int j = i * size; j <= size * (i + 1) - 1; j++) {
if (j <= lists.size() - 1) {
sub.add(lists.get(j));
}
}
mResultList.add(sub);
}
return mResultList;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//初始化Viewpager(应用入口列表)
mIndicatorWidth = dip2px(IND_WIDTH_SIZE);
LinearLayout llContent = new LinearLayout(mContext);
llContent.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
mViewPager = new customViewPager(mContext);
mViewPager.setLayoutParams(params);
llContent.addView(mViewPager);
//初始化指示器容器
mIndecatorLayout = new LinearLayout(mContext);
params.bottomMargin = 28;
mIndecatorLayout.setLayoutParams(params);
mIndecatorLayout.setOrientation(LinearLayout.HORIZONTAL);
llContent.addView(mIndecatorLayout);
mViewPager.addOnPageChangeListener(this);
addView(llContent);
}
/**
* 计算出页面的数量
* @return
*/
public int pageNum() {
if (mLists.size() % mPageSize != 0 && mLists.size() / mPageSize < 1) {
mCount = 1;
} else if (mLists.size() % mPageSize != 0 && mLists.size() / mPageSize >= 1) {
mCount = mLists.size() / mPageSize + 1;
} else {
mCount = mLists.size() / mPageSize;
}
return mCount;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
mCurrentPos = position;
mIndecatorLayout.getChildAt(mPrePosition % mCount).setEnabled(false);
mIndecatorLayout.getChildAt(mCurrentPos % mCount).setEnabled(true);//设置true放后面,防止初始化时两个pos都为0时。没有默认选中
mPrePosition = mCurrentPos;
}
@Override
public void onPageScrollStateChanged(int state) {
}
/**
* 导航Viewpager的适配器,向ViewPager的每一个页中添加一个GridView
*/
class MyEntryAdapter extends PagerAdapter {
List<View> listItem;//前面加类型
public MyEntryAdapter(List<View> itemView) {
this.listItem = itemView;
}
@Override
public int getCount() {
return mCount;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
//向Viewpager每一页中添加一个gridView布局
for (int i = 0; i < mCount; i++) {
GridView gridView = new CustomGridView(mContext);
gridView.setNumColumns(mColumNum);
gridView.setGravity(Gravity.CENTER);
gridView.setPadding(2, 2, 2, 0);
listItem.add(gridView);
mMyGridViewAdpter = new MyGridViewAdapter(mContext, mResultList.get(i), 0, mPageSize);
gridView.setAdapter(mMyGridViewAdpter);
}
container.addView(listItem.get(position));
return listItem.get(position);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
/**
* 导航gridView的适配器,向gridView的每一项中添加信息
*/
class MyGridViewAdapter extends BaseAdapter {
private Context context;
private List<Entry> lists;
private int index;//页数小标,标示第几页,从0开始
private int mPargerSize;//每页显示的最大的数量
public MyGridViewAdapter(Context context, List<Entry> lists, int index, int mPargerSize) {
this.context = context;
this.lists = lists;
this.index = index;
this.mPargerSize = mPargerSize;
}
@Override
public int getCount() {
return lists.size() > (index + 1) * mPargerSize ?
mPargerSize : (lists.size() - index * mPargerSize);
}
@Override
public Object getItem(int i) {
return lists.get(i + index * mPargerSize);
}
@Override
public long getItemId(int i) {
return i + index * mPargerSize;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
return addGridViewChildLayout(view, i);
}
/**
* 动态创建gridView的子布局
*
* @param view
*/
public View addGridViewChildLayout(View view, int pos) {
LinearLayout ll = new LinearLayout(context);
GridView.LayoutParams llparams = new GridView.LayoutParams(GridView.LayoutParams.MATCH_PARENT, GridView.LayoutParams.MATCH_PARENT);
ll.setOrientation(LinearLayout.VERTICAL);
ll.setLayoutParams(llparams);
ll.setPadding(0, dip2px(10), 0, 0);
ll.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams img_params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
LinearLayout.LayoutParams tv_params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
ImageView img = new ImageView(context);
img.setScaleType(ImageView.ScaleType.FIT_XY);
img_params.gravity = Gravity.CENTER;
img_params.width = dip2px(mIconSize);
img_params.height = dip2px(mIconSize);
img.setLayoutParams(img_params);
ll.addView(img);
TextView textView = new TextView(context);
textView.setTextSize(mTextSize);
textView.setTextColor(getResources().getColor(R.color.basiclib_colorBack));
textView.setPadding(0, 20, 0, 0);
textView.setLayoutParams(tv_params);
ll.addView(textView);
view = ll;
ImageLoader.display(lists.get(pos).getEntryIocn(),img,R.mipmap.basicres_picture_default,R.mipmap.basicres_picture_default);
textView.setText(lists.get(pos).getEntryName());
return view;
}
}
/**
* 定义viewpager滑动效果
*/
private class MyPageTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View page, float position) {
final float normalizedposition = Math.abs(Math.abs(position) - 1);
page.setAlpha(normalizedposition);
}
}
/**
* 自定义Viewpager,计算viewPager高度
*/
private class customViewPager extends ViewPager {
public customViewPager(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec,
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if (h > height)
height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
/**
* 自定义GridView,计算GridView高度
*/
private class CustomGridView extends GridView {
public CustomGridView(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
}
kotlin代码
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager
import com.bumptech.glide.Glide
import com.example.testproject.R
import com.example.testproject.bean.Entry
class EntryListView : FrameLayout, ViewPager.OnPageChangeListener {
private val IND_WIDTH_SIZE = 6f //指示器默认宽的长度
private var mContext: Context? = null
private var mViewPager: ViewPager? = null
private var mAdapter: PagerAdapter? = null
private var mMyGridViewAdapter: MyGridViewAdapter? = null
private var mIndecatorLayout: LinearLayout? = null
private var mColumNum = 5 //多少列
private var mRowNum = 2 //多少行
private var mPageSize = 8 //每页多少个
private var mCount = 0 //滑动页面的数量
private var mPrePosition = 0 //历史页pos
private var mIndicatiorWidth = 0.0f //指示器宽度
private var mIndicatiorHight = 3f //指示器高度
private var mCurrentPos = 0 //当前也pos
private var mIconSize = 40f //item中图片的宽高
private var mTextSize = 11f //item中字体的大小
private var mResultList: MutableList<List<Entry>>? = null //分页后的数据列表
private var mLists: List<Entry>? = null //获取到的数据列表
private var mItemView: MutableList<View> = ArrayList() //存放gridView的列表
constructor(context: Context) : super(context) {
this.mContext = context
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
this.mContext = context
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
/**
* 设置几行几列
*/
fun setPageSize(rowNum: Int, colNum: Int) {
mRowNum = rowNum
mColumNum = colNum
mPageSize = rowNum * colNum
}
/**
* 初始化指示器
*/
private fun init() {
if (mAdapter == null) return
mViewPager?.adapter = mAdapter
//切换画面
mViewPager?.setPageTransformer(true, MyPageTransformer())
mIndecatorLayout?.removeAllViews()
//向线性布局中添加小圆点指示器
var line: View?
var params: LinearLayout.LayoutParams?
for (item in 0 until mCount) {
line = View(mContext)
line.id = item
params = LinearLayout.LayoutParams(dip2px(mIndicatiorWidth), dip2px(mIndicatiorHight))
params.setMargins(dip2px(3f), 0, dip2px(3f), 0)
line.layoutParams = params
line.setBackgroundResource(R.mipmap.ic_launcher)
line.isEnabled = false
mIndecatorLayout?.gravity = Gravity.CENTER_HORIZONTAL
mIndecatorLayout?.addView(line)
}
mIndecatorLayout?.getChildAt(0)?.isEnabled = true
mViewPager?.currentItem = 0
}
/**
* 获取数据
*/
fun setData(mLists: MutableList<Entry>) {
if (mLists.isEmpty()) return
this.mLists = mLists
mCount = pageNum()
cutOutList(mLists, mPageSize)
if (mAdapter == null)
mAdapter = MyEntryAdapter(mItemView)
init()
}
/**
* 按照size的大小截取list
*/
private fun cutOutList(lists: List<Entry>, size: Int): List<List<Entry>> {
mResultList = ArrayList()
val arrSize: Int = if (lists.size % size == 0) {
lists.size / size
} else lists.size / size + 1
for (item in 0 until arrSize) {
val sub = ArrayList<Entry>()
for (i in (item * size) until size * (item + 1)) {
if (i <= lists.size - 1) {
sub.add(lists[i])
}
}
mResultList!!.add(sub)
}
return mResultList as ArrayList<List<Entry>>
}
override fun onFinishInflate() {
super.onFinishInflate()
//初始化ViewPager (入口列表)
mIndicatiorWidth = dip2px(IND_WIDTH_SIZE).toFloat()
val llContent = LinearLayout(mContext)
llContent.orientation = LinearLayout.VERTICAL
val params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
mViewPager = CustomViewPager(mContext!!)
mViewPager?.layoutParams = params
llContent.addView(mViewPager)
//初始化指示器容器
mIndecatorLayout = LinearLayout(mContext)
params.bottomMargin = 28
mIndecatorLayout?.layoutParams = params
mIndecatorLayout?.orientation = LinearLayout.HORIZONTAL
llContent.addView(mIndecatorLayout)
mViewPager?.addOnPageChangeListener(this)
addView(llContent)
}
/**
* 计算出页面的数量
*/
private fun pageNum(): Int {
mCount = if (mLists?.size!! % mPageSize != 0 && mLists?.size!! / mPageSize < 1)
1
else if (mLists?.size!! % mPageSize != 0 && mLists?.size!! / mPageSize >= 1)
mLists?.size!! / mPageSize + 1
else
mLists?.size!! / mPageSize
return mCount
}
override fun onPageScrollStateChanged(state: Int) {
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
}
override fun onPageSelected(position: Int) {
mCurrentPos = position
mIndecatorLayout?.getChildAt(mPrePosition % mCount)?.isEnabled = false
mIndecatorLayout?.getChildAt(mCurrentPos % mCount)?.isEnabled = true
mPrePosition = mCurrentPos
}
/**
* 根据手机的分辨率从dip的单位转为px(像素)
*/
fun dip2px(dpValue: Float): Int {
val scale: Float = mContext!!.resources.displayMetrics.density
return (dpValue * scale + 0.5f).toInt()
}
/**
* 导航ViewPager的适配器,向ViewPager的每一个页中添加一个GridView
*/
inner class MyEntryAdapter(itemView: MutableList<View>) : PagerAdapter() {
private var listItem: MutableList<View>? = itemView
override fun isViewFromObject(view: View, `object`: Any): Boolean {
return view == `object`
}
override fun getCount(): Int {
return mCount
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
container.removeView(`object` as View?)
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
//向viewpager每一页中添加一个gridView布局
for (item in 0 until mCount) {
val gridView = CustomGridView(mContext!!)
gridView.numColumns = mColumNum
gridView.gravity = Gravity.CENTER
gridView.setPadding(2, 2, 2, 0)
listItem?.add(gridView)
mMyGridViewAdapter = MyGridViewAdapter(mContext, mResultList?.get(item), 0, mPageSize)
gridView.adapter = mMyGridViewAdapter
}
container.addView(listItem?.get(position))
return listItem?.get(position)!!
}
}
/**
* 导航gridView的适配器,向girdView的每一项中添加信息
* index参数 页数下标,表示第几页,从0开始
* mPagerSize参数 每页显示的最大的数量
*/
inner class MyGridViewAdapter(var context: Context?, var lists: List<Entry>?, var index: Int, var mPagerSize: Int) : BaseAdapter() {
override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
return addGridViewChildLayout(p1, p0)
}
override fun getItem(p0: Int): Any {
return lists?.get(p0 + index * mPagerSize)!!
}
override fun getItemId(p0: Int): Long {
return (p0 + index * mPagerSize).toLong()
}
override fun getCount(): Int {
return if ((lists?.size ?: 0) > (index + 1) * mPagerSize) {
mPagerSize
} else {
((lists?.size ?: 0).minus(index * mPagerSize))
}
}
/**
* 动态创建gridView的子布局
*/
private fun addGridViewChildLayout(view: View?, position: Int): View {
val any = LinearLayout(mContext)
val params: AbsListView.LayoutParams = GridView@ AbsListView.LayoutParams(GridView@ AbsListView.LayoutParams.MATCH_PARENT, GridView@ AbsListView.LayoutParams.MATCH_PARENT)
any.orientation = LinearLayout.VERTICAL
any.layoutParams = params
any.setPadding(0, this@EntryListView.dip2px(10f), 0, 0)
any.gravity = Gravity.CENTER
val imgParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
val tvParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
imgParams.gravity = Gravity.CENTER
imgParams.width = this@EntryListView.dip2px(mIconSize)
imgParams.height = this@EntryListView.dip2px(mIconSize)
val img = ImageView(context)
img.scaleType = ImageView.ScaleType.FIT_XY
img.layoutParams = imgParams
any.addView(img)
val textView = TextView(context)
textView.textSize = mTextSize
textView.setTextColor(resources.getColor(R.color.colorPrimary))
textView.setPadding(0, 20, 0, 0)
textView.layoutParams = tvParams
any.addView(textView)
Glide.with(context!!).load(lists?.get(position)?.icon).into(img)
textView.text = lists?.get(position)?.title
return view ?: any
}
}
/**
* 定义ViewPager滑动效果
*/
class MyPageTransformer : ViewPager.PageTransformer {
override fun transformPage(page: View, position: Float) {
val normalize = Math.abs(Math.abs(position) - 1)
page.alpha = normalize
}
}
/**
* 自定义ViewPager,计算ViewPager高度
*/
class CustomViewPager(context: Context) : ViewPager(context) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var height = 0
for (item in 0 until childCount) {
val child = getChildAt(item)
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))
val h = child.measuredHeight
if (h > height)
height = h
}
val expandSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
super.onMeasure(widthMeasureSpec, expandSpec)
}
}
/**
* 自定义GridView,计算GridView高度
*/
class CustomGridView(context: Context) : GridView(context) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val expandSpec = MeasureSpec.makeMeasureSpec(Int.MAX_VALUE.shr(2), MeasureSpec.AT_MOST)
super.onMeasure(widthMeasureSpec, expandSpec)
}
}
}