首先我们可以模仿ListView中的adapter的设计模式,自己搭建一个BaseAdapter类(BaseMenuAdapter),相当于ListView中的BaseAdapter
public abstract class BaseMenuAdapter {
//获取总共多少条
public abstract int getCount();
//获取Tab的view
public abstract View getTabView(int position, ViewGroup parent);
//获取Menu的view
public abstract View getMenuView(int position, ViewGroup parent);
//提供接口给外部去更新文字
public abstract void openMenu(View tabView);
public abstract void closeMenu(View tabView);
}
ListScreenMenuAdapter自己定义一个适配器,相当于我们ListView中的自己定义的adapter
public class ListScreenMenuAdapter extends BaseMenuAdapter {
private String[] mItems = {"类型", "品牌", "价格", "更多"};
private Context mContext;
public ListScreenMenuAdapter(Context context) {
this.mContext = context;
}
@Override
public int getCount() {
return mItems.length;
}
//这里设置tabview的布局为TextView所以没有findviewById的形式,后期可以修改
//这里相当于自己的adapter继承BaseAdapter后实现的getView(),这里图方便也没有添加View contentView
@Override
public View getTabView(int position, ViewGroup parent) {
TextView tabView = (TextView) LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_tab, parent, false);
tabView.setText(mItems[position]);
tabView.setTextColor(Color.BLACK);
return tabView;
}
@Override
public View getMenuView(int position, ViewGroup parent) {
// 真正开发过程中,不同的位置显示的布局不一样
TextView menuView = (TextView) LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_menu, parent, false);
menuView.setText(mItems[position]);
return menuView;
}
@Override
public void openMenu(View tabView) {
TextView tv = (TextView) tabView;
tv.setTextColor(Color.parseColor("#FF4081"));
}
//关闭菜单,设置颜色为黑色
@Override
public void closeMenu(View tabView) {
TextView tv = (TextView) tabView;
tv.setTextColor(Color.BLACK);
}
}
**ListDataScreenView:自己定义View,实现布局,整体是一个线性布局,布局中添加一个Tab的布局+内容的FrameLayout布局,而FrameLayout中是LinearLayout+View(阴影),注意以下几个问题
// 菜单文字挤到一堆去了 ,菜单的 Tab 不见了(解决) ,宽度不是等宽,weight要是为1
**
// 内容的高度应该不是全部 应该是整个 View的 75%
// 进来的时候阴影不显示 ,内容也是不显示的(把它移上去)
// 内容还没有显示出来,打开的时候显示当前位置的菜单,关闭的时候隐藏,阴影点击应该要关闭菜单
// 动画在执行的情况下就不要在响应动画事件
// 打开和关闭 变化tab的显示 , 肯定不能把代码写到 ListDataScreen 里面来
// 当菜单是打开的状态 不要执行动画只要切换
public class ListDataScreenView extends LinearLayout implements View.OnClickListener {
private Context mContext;
// 1.1 创建头部用来存放 Tab
private LinearLayout mMenuTabView;
// 1.2 创建 FrameLayout 用来存放 = 阴影(View) + 菜单内容布局(FrameLayout)
private FrameLayout mMenuMiddleView;
// 阴影
private View mShadowView;
// 创建菜单用来存放菜单内容
private FrameLayout mMenuContainerView;
// 阴影的颜色
private int mShadowColor = 0x88888888;
private BaseMenuAdapter mAdapter;
private int mMenuContainerHeight;
private int DURATION_TIME = 350;
private int mCurrentPosition = -1;//当前位置
// 动画是否在执行
private boolean mAnimatorExecute;
public ListDataScreenView(Context context) {
this(context, null);
}
public ListDataScreenView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ListDataScreenView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
initLayout();
}
/**
* 初始化控件
*/
private void initLayout() {
// 1. 先创建一个 xml 布局 ,再加载,findViewById
// 2. 简单的效果用代码去创建
setOrientation(VERTICAL);
//创建头部用来存放Tab
mMenuTabView = new LinearLayout(mContext);
mMenuTabView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT
, LayoutParams.WRAP_CONTENT));
addView(mMenuTabView);
// 1.2 创建 FrameLayout 用来存放 = 阴影(View) + 菜单内容布局(FrameLayout)
mMenuMiddleView = new FrameLayout(mContext);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0);
params.weight = 1;
mMenuMiddleView.setLayoutParams(params);
addView(mMenuMiddleView);
// 创建阴影 可以不用设置 LayoutParams 默认就是 MATCH_PARENT ,MATCH_PARENT
mShadowView = new View(mContext);
mShadowView.setBackgroundColor(mShadowColor);
mShadowView.setVisibility(GONE);
mShadowView.setAlpha(0f);//默认是0
mMenuMiddleView.addView(mShadowView);
mShadowView.setOnClickListener(this);
// 创建菜单用来存放菜单内容
mMenuContainerView = new FrameLayout(mContext);
mMenuContainerView.setBackgroundColor(Color.WHITE);
mMenuMiddleView.addView(mMenuContainerView);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 内容的高度应该不是全部 应该是整个 View的 75%
//只需要测量一次就可以了
if (mMenuContainerHeight == 0 && heightSize > 0) {
ViewGroup.LayoutParams params = mMenuContainerView.getLayoutParams();
mMenuContainerHeight = (int) (heightSize * 75f / 100);
params.height = mMenuContainerHeight;
mMenuContainerView.setLayoutParams(params);
// 进来的时候阴影不显示 ,内容也是不显示的(把它移上去)
mMenuContainerView.setTranslationY(-mMenuContainerHeight);
}
}
public void setAdapter(BaseMenuAdapter adapter) {
if (adapter != null) {
this.mAdapter = adapter;
}
//获取一共多少条
int count = mAdapter.getCount();
for (int i = 0; i < count; i++) {
// 获取菜单的Tab
View tabView = mAdapter.getTabView(i, mMenuTabView);
LinearLayout.LayoutParams params = (LayoutParams) tabView.getLayoutParams();
params.weight = 1;
tabView.setLayoutParams(params);
mMenuTabView.addView(tabView);
//设置点击事件
setTabClick(tabView, i);
//获取内容的view
View menuView = mAdapter.getMenuView(i, mMenuContainerView);
menuView.setVisibility(GONE);//设置默认不可见
mMenuContainerView.addView(menuView);
}
// 挤到一堆去了 ,菜单的 Tab 不见了(解决) ,宽度不是等宽,weight要是为1
// 内容的高度应该不是全部 应该是整个 View的 75%
// 进来的时候阴影不显示 ,内容也是不显示的(把它移上去)
// 内容还没有显示出来,打开的时候显示当前位置的菜单,关闭的时候隐藏,阴影点击应该要关闭菜单
// 动画在执行的情况下就不要在响应动画事件
// 打开和关闭 变化tab的显示 , 肯定不能把代码写到 ListDataScreen 里面来
// 当菜单是打开的状态 不要执行动画只要切换
}
/**
* tabView的点击事件
*/
private void setTabClick(final View tabView, final int postition) {
tabView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mCurrentPosition == -1) {//没有打开
openMenu(tabView, postition);
} else {//已经打开
if (mCurrentPosition == postition) {//如果点击的是同一个位置则关闭
closeMenu();
} else {//切换显示yixia
View currentMenu = mMenuContainerView.getChildAt(mCurrentPosition);
currentMenu.setVisibility(GONE);
mAdapter.closeMenu(mMenuTabView.getChildAt(mCurrentPosition));
mCurrentPosition = postition;
currentMenu = mMenuContainerView.getChildAt(mCurrentPosition);
currentMenu.setVisibility(VISIBLE);
mAdapter.openMenu(mMenuTabView.getChildAt(mCurrentPosition));
}
}
}
});
}
private void closeMenu() {
if (mAnimatorExecute) {
return;
}
//设置阴影为可见
mShadowView.setVisibility(GONE);
// 获取当前位置显示当前菜单,菜单是加到了菜单容器
final View menuView = mMenuContainerView.getChildAt(mCurrentPosition);
menuView.setVisibility(GONE);
//位移动画
ObjectAnimator translationAnimation =
ObjectAnimator.ofFloat(mMenuContainerView, "translationY", 0, -mMenuContainerHeight);
translationAnimation.setDuration(DURATION_TIME);
translationAnimation.start();
//阴影的透明度变化
ObjectAnimator rotationAniamtion = ObjectAnimator.ofFloat(mShadowView, "alpha", 1f, 0f);
rotationAniamtion.setDuration(DURATION_TIME);
rotationAniamtion.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mCurrentPosition = -1;
mAnimatorExecute = false;
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
mAnimatorExecute = true;
mAdapter.closeMenu(mMenuTabView.getChildAt(mCurrentPosition));
}
});
rotationAniamtion.start();
}
/**
* 打开菜单
*/
private void openMenu(final View tabView, final int postition) {
if (mAnimatorExecute) {
return;
}
//设置阴影为可见
mShadowView.setVisibility(VISIBLE);
// 获取当前位置显示当前菜单,菜单是加到了菜单容器
final View menuView = mMenuContainerView.getChildAt(postition);
menuView.setVisibility(VISIBLE);
//位移动画
ObjectAnimator translationAnimation =
ObjectAnimator.ofFloat(mMenuContainerView, "translationY", -mMenuContainerHeight, 0);
translationAnimation.setDuration(DURATION_TIME);
translationAnimation.start();
//阴影的透明度变化
ObjectAnimator rotationAniamtion = ObjectAnimator.ofFloat(mShadowView, "alpha", 0f, 1f);
rotationAniamtion.setDuration(DURATION_TIME);
rotationAniamtion.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mCurrentPosition = postition;
mAnimatorExecute = false;
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
mAnimatorExecute = true;
mAdapter.openMenu(tabView);
}
});
rotationAniamtion.start();
}
//点击阴影部分,关闭菜单
@Override
public void onClick(View v) {
closeMenu();
}
}
TabView和Menu的布局
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tab_view_tv"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:gravity="center"
android:padding="10dp"
android:layout_height="wrap_content"
tools:context=".MainActivity"/>
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:gravity="center"
android:layout_height="match_parent"
tools:context=".MainActivity"/>
主布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="列表 内容"
android:layout_centerInParent="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:text="test"
android:onClick="click"
android:layout_alignParentBottom="true"
android:layout_height="wrap_content" />
<com.hbwj.a15_.ListDataScreenView
android:id="@+id/list_data_screen_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
此时想点击menu中的item然后点击关闭,这时可有三种方式:
- 1.1 方式一:可以写一个监听通过Activity去掉用 ListDataScreenView 的关闭方法
- 1.2 方式二:可以让Adapter去持有我们 ListDataScreenView 的对象
- 1.3 方式三:利用观察者设计模式(观察者 和 被观察者,注册订阅的概念) 场景 (今天这个,ListView的notifyDataChanged , RxJava等等)
今天主要讲第三种方式,参考的是listview中notifyChanged
第一步:MenuObserver观察者
public abstract class MenuObserver {
//关闭菜单
public abstract void closeMenu();
}
修改BaseMenuAdapter
//这里图方便就不用List方式
private MenuObserver mMenuObserver;
//模仿ListView中的源码
public void registerDataSetObserver(MenuObserver observer) {
mMenuObserver=observer;
}
public void unregisterDataSetObserver(MenuObserver observer) {
mMenuObserver=null;
}
public void closeMenu(){
mMenuObserver.closeMenu();
}
修改ListDataScreenView中的setAdapter
private class AdapterMenuObserver extends MenuObserver{
@Override
public void closeMenu() {
//关闭菜单
ListDataScreenView.this.closeMenu();
}
}
private AdapterMenuObserver mMenuObserver;
//模仿ListView中的adapter
public void setAdapter(BaseMenuAdapter adapter) {
if (mAdapter != null && mMenuObserver != null) {
mAdapter.unregisterDataSetObserver(mMenuObserver);
}
if (adapter != null) {
this.mAdapter = adapter;
}
mMenuObserver = new AdapterMenuObserver();
mAdapter.registerDataSetObserver(mMenuObserver);
//获取一共多少条
int count = mAdapter.getCount();
for (int i = 0; i < count; i++) {
// 获取菜单的Tab
View tabView = mAdapter.getTabView(i, mMenuTabView);
LinearLayout.LayoutParams params = (LayoutParams) tabView.getLayoutParams();
params.weight = 1;
tabView.setLayoutParams(params);
mMenuTabView.addView(tabView);
//设置点击事件
setTabClick(tabView, i);
//获取内容的view
View menuView = mAdapter.getMenuView(i, mMenuContainerView);
menuView.setVisibility(GONE);//设置默认不可见
mMenuContainerView.addView(menuView);
}
}
最后修改具体的ListScreenMenuAdapter
@Override
public View getMenuView(int position, ViewGroup parent) {
// 真正开发过程中,不同的位置显示的布局不一样
TextView menuView = (TextView) LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_menu, parent, false);
menuView.setText(mItems[position]);
menuView.setOnClickListener(new View.OnClickListener() {//点击关闭菜单
@Override
public void onClick(View v) {
//1.1 方式一:可以写一个监听通过Activity去掉用 ListDataScreenView 的关闭方法
//1.2 方式二:可以让Adapter去持有我们 ListDataScreenView 的对象
//1.3 方式三:利用观察者设计模式(观察者 和 被观察者,注册订阅的概念) 场景 (今天这个,ListView的notifyDataChanged , RxJava等等)
closeMenu();
//Toast.makeText(mContext, "关闭菜单", Toast.LENGTH_SHORT).show();
}
});
return menuView;
}