先看应用中的效果图:
demo的效果图:
这里是使用了仿美团的分类列表加上SlideBottomLayout来实现底部名片的自动弹出跟收起功能的。
实现流程:
1.添加SlideBottomLayout依赖
//自定义底部上拉控件
implementation 'com.github.qingmei2:SlideBottomLayout-Android:1.2.3'
//recyclerview
implementation 'com.android.support:recyclerview-v7:27.1.1'
2.布局文件
(1)MainActivity的布局文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.qingmei2.library.SlideBottomLayout
android:id="@+id/sbl_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:handler_height="35dp"
android:layout_gravity="bottom"
android:layout_marginLeft="-10dp"
android:layout_marginRight="-10dp"
android:visibility="gone"
>
<LinearLayout
android:id="@+id/ll_sbl_bottom"
android:layout_width="match_parent"
android:layout_height="120dp"
android:orientation="vertical"
android:layout_gravity="center_horizontal"
android:background="@mipmap/sbl_bottom_bg"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="底部栏"
android:textSize="14sp"
android:textColor="@color/colorAccent"
android:layout_gravity="center_horizontal"
android:layout_marginTop="14dp"
android:textStyle="bold"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="#FFFFFF"
android:layout_marginTop="5dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp">
<android.support.v4.view.ViewPager
android:id="@+id/recycle_home_entrance_vp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:id="@+id/recycle_ll_dot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="8dp"
android:gravity="center"
android:orientation="horizontal" />
</RelativeLayout>
</LinearLayout>
</com.qingmei2.library.SlideBottomLayout>
</FrameLayout>
(2)item_home_entrance的布局文件
<?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="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="5dp">
<ImageView
android:id="@+id/entrance_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dp"
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" />
</FrameLayout>
(3)item_home_entrance_vp的布局文件
<?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="match_parent" />
(4)dot 小圆点指示器的布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- 小圆点View -->
<View
android:id="@+id/v_dot"
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:background="@drawable/dot_normal"/>
</RelativeLayout>
(5)小圆点的正常态跟选中态的drawable文件
dot_normal:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="8dp" />
<solid android:color="#D4D3D3" />
</shape>
dot_selected:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorPrimary" />
<corners android:radius="8dp" />
</shape>
3.MainActvivty
package zy.xda.dibumingpian;
import android.os.Handler;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Toast;
import com.qingmei2.library.SlideBottomLayout;
import java.util.ArrayList;
import java.util.List;
import zy.xda.dibumingpian.adapter.EntranceAdapter;
import zy.xda.dibumingpian.model.ModelHomeEntrance;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
SlideBottomLayout sbl_bottom;
boolean isShow;
LinearLayout ll_sbl_bottom;
ViewPager entranceViewPager;
LinearLayout mLlDot;
public static final int HOME_ENTRANCE_PAGE_SIZE = 3;//首页菜单单页显示数量
private int pageCount;//总页数
private int curIndex = 0;//当前显示的事第几页
private List<ModelHomeEntrance> homeEntrances;
private LayoutInflater inflater;
EntranceAdapter entranceAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
entranceViewPager = findViewById(R.id.recycle_home_entrance_vp);
mLlDot = findViewById(R.id.recycle_ll_dot);
ll_sbl_bottom = findViewById(R.id.ll_sbl_bottom);
ll_sbl_bottom.setOnClickListener(this);
sbl_bottom = findViewById(R.id.sbl_bottom);
sbl_bottom.setVisibility(View.VISIBLE);
sbl_bottom.show();
showMingPian();
initData();
init();
}
public void showMingPian(){
Handler handler = new Handler();
Handler handler2 = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
sbl_bottom.show();
isShow = true;
}
}, 1000);
handler2.postDelayed(new Runnable() {
@Override
public void run() {
sbl_bottom.hide();
isShow = false;
}
}, 5000);
}
//假数据
private void initData() {
homeEntrances = new ArrayList<>();
homeEntrances.add(new ModelHomeEntrance("美食", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("电影", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("酒店住宿", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("生活服务", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("KTV", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("旅游", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("学习培训", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("汽车服务", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("摄影写真", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("休闲娱乐", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("丽人", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("运动健身", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("大保健", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("团购", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("景点", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("全部分类", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("美食", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("电影", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("酒店住宿", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("生活服务", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("KTV", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("旅游", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("学习培训", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("汽车服务", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("摄影写真", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("休闲娱乐", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("丽人", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("运动健身", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("大保健", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("团购", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("景点", R.mipmap.ic_launcher));
homeEntrances.add(new ModelHomeEntrance("全部分类", R.mipmap.ic_launcher));
}
private void init() {
//首页菜单分页
inflater = LayoutInflater.from(this);
//将RecyclerView放至ViewPager中:
int pageSize = HOME_ENTRANCE_PAGE_SIZE;
//一共的页数等于 总数/每页数量,并取整。
pageCount = (int) Math.ceil(homeEntrances.size() * 1.0 / pageSize);
List<View> viewList = new ArrayList<View>();
for (int index = 0; index < pageCount; index++) {
//每个页面都是inflate出一个新实例
RecyclerView recyclerView = (RecyclerView) inflater.inflate(R.layout.item_home_entrance_vp, entranceViewPager, false);
recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.this, 3){
@Override
public boolean canScrollVertically() {
return false;
}
});
entranceAdapter = new EntranceAdapter(MainActivity.this, homeEntrances, index, HOME_ENTRANCE_PAGE_SIZE);
recyclerView.setAdapter(entranceAdapter);
viewList.add(recyclerView);
entranceAdapter.setOnItemClickListener(new EntranceAdapter.OnItemClick(){
@Override
public void onItemListener(int position){
Toast.makeText(MainActivity.this,homeEntrances.get(position).getName(),Toast.LENGTH_SHORT).show();
}
});
}
CagegoryViewPagerAdapter adapter = new CagegoryViewPagerAdapter(viewList);
entranceViewPager.setAdapter(adapter);
//设置小圆点
setOvalLayout();
}
private void setOvalLayout() {
for (int i = 0; i < pageCount; i++) {
mLlDot.addView(inflater.inflate(R.layout.dot, null));
}
//默认显示第一页
mLlDot.getChildAt(0).findViewById(R.id.v_dot).setBackgroundResource(R.drawable.dot_selected);
entranceViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
//取消选中
mLlDot.getChildAt(curIndex).findViewById(R.id.v_dot).setBackgroundResource(R.drawable.dot_normal);
//选中
mLlDot.getChildAt(position).findViewById(R.id.v_dot).setBackgroundResource(R.drawable.dot_selected);
curIndex = position;
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.ll_sbl_bottom:
if(isShow){
sbl_bottom.hide();
isShow = false;
}else{
isShow = true;
sbl_bottom.show();
}
break;
}
}
}
4.仿美团分类菜单的Adapter
package zy.xda.dibumingpian.adapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
import zy.xda.dibumingpian.R;
import zy.xda.dibumingpian.model.ModelHomeEntrance;
/**
* Created by haoxuhong on 2019/1/14.
*
* @description:
*/
public class EntranceAdapter extends RecyclerView.Adapter<EntranceAdapter.EntranceViewHolder> {
private List<ModelHomeEntrance> mDatas;
/**
* 页数下标,从0开始(通俗讲第几页)
*/
private int mIndex;
/**
* 每页显示最大条目个数
*/
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) {
/**
* 在给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());
if(mOnItemClick!=null){
holder.itemView.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
mOnItemClick.onItemListener(pos);
}
});
}
}
@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) mContext.getResources().getDisplayMetrics().widthPixels / 4.0f));
itemView.setLayoutParams(layoutParams);
}
}
public OnItemClick mOnItemClick;
public void setOnItemClickListener(OnItemClick onItemClick){
this.mOnItemClick = onItemClick;
}
public interface OnItemClick{
void onItemListener(int position);
}
}
5.指示器小圆点的Adapter
package zy.xda.dibumingpian;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
/**
* Created by haoxuhong on 2019/1/14.
*
* @description: 首页分类ViewPager适配器
*/
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;
}
}
6.菜单的model类:
package zy.xda.dibumingpian.model;
/**
* Created by haoxuhong on 2019/1/14.
*
* @description: 菜单项实体类
*/
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;
}
}
代码完成,demo效果图:
demo源码:https://download.csdn.net/download/qq_38306233/12685220
加个自动滑动的viewpager:
import android.content.Context;
import android.os.Handler;
import android.os.Message;
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.View;
import android.view.ViewGroup;
import java.util.List;
public class ScrollerViewpager extends ViewPager {
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 100002)
setCurrentItem(getCurrentItem() + 1);
}
};
//滚动的页面
private List<View> views;
//滚动的页面数量
private int count;
//自动滚动信号
private final int SCROLL = 100002;
//创建一个滚动线程
private Thread thread;
//间隔时间
private int SPACE;
//目前线程状态
private boolean NORMAL = true;
public ScrollerViewpager(@NonNull Context context) {
super(context);
}
public ScrollerViewpager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
/**
* 功能:初始化
* @param views 视图组
* @param space 间隔时间
* @param listener 监听事件
*/
public void init(List<View> views, final int space, final OnViewpagerChangeListener listener){
this.views = views;
this.count = views.size();
this.SPACE = space;
setAdapter(new MyAdapter());
setCurrentItem(Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % views.size());
thread = new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
NORMAL = true;
Thread.sleep(SPACE * 1000);
handler.sendEmptyMessage(SCROLL);
} catch (InterruptedException e) {
try {
NORMAL = false;
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e1) { }
}
}
}
});
thread.start();
addOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
listener.onChange(position % count);
}
@Override
public void onPageScrollStateChanged(int state) {
if ((!NORMAL && state != 1) || (NORMAL && state == 1)){
thread.interrupt();
}
}
});
}
public class MyAdapter extends PagerAdapter {
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
int temp = position % count;
View view = views.get(temp);
if (view.getParent() == container) {
container.removeView(view);
}
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
@Override
public float getPageWidth(int position) {
// return 0.9f; 设置显示控件的90%宽度
return 1.0f;
}
}
}
使用:使用ScrollerViewpager替换原来的Viewpager,并在 init()方法中初始化ScrollerViewPager:
entranceViewPager.init(viewList, 5, new OnViewpagerChangeListener() {
@Override
public void onChange(int currentPage) {
}
});
/**
* 通过反射来修改 ViewPager的mScroller属性
* 修改viewpager的滑动速度
*/
try {
Class clazz=Class.forName("android.support.v4.view.ViewPager");
Field f=clazz.getDeclaredField("mScroller");
FixedSpeedScroller fixedSpeedScroller=new FixedSpeedScroller(getActivity(),new LinearOutSlowInInterpolator());
fixedSpeedScroller.setmDuration(1200);//设置滑动速度 1.2s划完
f.setAccessible(true);
f.set(klineViewPager,fixedSpeedScroller);
} catch (Exception e) {
e.printStackTrace();
}