打造通用的下拉列表
2018-01-28 14:28
211 查看
这是一个通用的下拉列表效果,类型和价格展示的是一个textview,品牌和更多展示的是一个RecyclerView列表效果;这里是采用adapter设计模式实现的。
实现思路:
1、可以模仿ListView的BaseAdapter那样建立一个抽象的adapter,后面的适配将继承自该抽象类;
2、自定义布局容易,将tab栏,菜单内容、背影逐一添加到布局容器中;
3、实现下拉和收缩效果,并为其添加动画效果;
4、利用观察者模式实现点击菜单内容关闭,并进行结果回调;
先按照第一步新建一个抽象的adapter:
/** * Created by Administrator on 2018/1/22. * 筛选菜单的adapter */ public abstract class BaseMenuAdapter { //菜单内容点击关闭的观察者 private MenuObserver mObserver; //注册观察者 public void registerDataSetObserver(MenuObserver observer) { mObserver = observer; } // 注销观察者 public void unregisterDataSetObserver(MenuObserver observer) { mObserver = null; } /** * 关闭菜单 */ public void closeMenu(){ if(mObserver != null){ mObserver.closeMenu(); } } /** * 获取总共有多少条 * * @return */ public abstract int getCount(); /** * 获取tab内容 * * @param position * @param parent * @return */ public abstract View getTabView(int position, ViewGroup parent); /** * 获取当前菜单的内容 * * @param position * @param parent * @return */ public abstract View getMenuView(int position, ViewGroup parent); /** * 菜单打开 * * @param tabView */ public void menuOpen(View tabView) { } /** * 菜单关闭 * * @param tabView */ public void menuClose(View tabView) { } }接下来可以实现第二步和第三步,自定义布局容器并将tab、菜单内容、背景添加到布局容器中;
public class ListDataScreenView extends LinearLayout implements View.OnClickListener { //头部tab private LinearLayout mMenuTabView; private Context mContext; //中间菜单内容 private FrameLayout mMenuMiddleView; //阴影 private View mShadowView; //阴影的背景颜色 private int mShadowColor = 0x88888888; //创建菜单用来存放菜单内容 private FrameLayout mMenuContentView; //菜单的adapter private BaseMenuAdapter mAdapter; //内容菜单的高度 private int menuContainerHeight; //当前的打开的位置 private int mCurentPosition = -1; private long DURATIOIN_TIME = 350; //动画是否在执行 private boolean mAnimatorExecute; public ListDataScreenView(Context context) { this(context, null); } public ListDataScreenView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ListDataScreenView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContext = context; initLayout(); } /** * 初始化布局 */ private void initLayout() { setOrientation(VERTICAL); //1.1创建头部用来存放tab mMenuTabView = new LinearLayout(mContext); mMenuTabView.setBackgroundColor(Color.GREEN); mMenuTabView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); addView(mMenuTabView); //1.2创建FramLayout用来存放菜单内容布局+阴影 mMenuMiddleView = new FrameLayout(mContext); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0); params.weight = 1; mMenuMiddleView.setLayoutParams(params); addView(mMenuMiddleView); //创建阴影,可以不用设置LayoutParams 默认就是MATCH_PARENT mShadowView = new View(mContext); mShadowView.setBackgroundColor(mShadowColor); mShadowView.setAlpha(0f); mShadowView.setOnClickListener(this); mShadowView.setVisibility(GONE); mMenuMiddleView.addView(mShadowView); //创建菜单用来存放菜单内容 mMenuContentView = new FrameLayout(mContext); mMenuContentView.setBackgroundColor(Color.WHITE); mMenuMiddleView.addView(mMenuContentView); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = MeasureSpec.getSize(heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (menuContainerHeight == 0 && height > 0) { //内容的高度应该不是全部,应该是整个View的75% menuContainerHeight = (int) (height * 75f / 100); ViewGroup.LayoutParams params = mMenuContentView.getLayoutParams(); params.height = menuContainerHeight; mMenuContentView.setLayoutParams(params); //进来的时候阴影不显示 内容也是不显示的 mMenuContentView.setTranslationY(-menuContainerHeight); } } /** * 具体的观察者 */ private class AdapterDataSetObserver extends MenuObserver { @Override public void closeMenu() { //如果有注册就会收到通知 ListDataScreenView.this.closeMenu(); } } private AdapterDataSetObserver mMenuObserver; /** * 设置adapter * * @param adapter */ public void setAdapter(BaseMenuAdapter adapter) { if (adapter == null) { return; } if (mAdapter != null && mMenuObserver != null) { //取消订阅 mAdapter.unregisterDataSetObserver(mMenuObserver); } //注册一个观察者 this.mAdapter = adapter; mMenuObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mMenuObserver); //观察者 this.mAdapter = adapter; //获取有多少条 int count = mAdapter.getCount(); for (int i = 0; i < count; i++) { //获取菜单的tab View tabView = mAdapter.getTabView(i, mMenuTabView); mMenuTabView.addView(tabView); //宽度不是等宽,weight设置为1 LinearLayout.LayoutParams params = (LayoutParams) tabView.getLayoutParams(); params.weight = 1; tabView.setLayoutParams(params); //设置点击事件 setTabClick(tabView, i); //获取菜单内容 View menuView = mAdapter.getMenuView(i, mMenuContentView); //先隐藏,当点击的时候才显示 menuView.setVisibility(GONE); mMenuContentView.addView(menuView); } } /** * 点击事件处理 * * @param tabView * @param position */ private void setTabClick(final View tabView, final int position) { tabView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mCurentPosition == -1) { //没有打开 openMenu(position, tabView); } else { if (mCurentPosition == position) { //打开关闭 closeMenu(); } else { //切换显示 View currentMenu = mMenuContentView.getChildAt(mCurentPosition); currentMenu.setVisibility(GONE); mAdapter.menuClose(mMenuTabView.getChildAt(mCurentPosition)); mCurentPosition = position; currentMenu = mMenuContentView.getChildAt(mCurentPosition); currentMenu.setVisibility(VISIBLE); mAdapter.menuOpen(mMenuTabView.getChildAt(mCurentPosition)); } } } }); } /** * 关闭menu */ public void closeMenu() { if (mAnimatorExecute) { return; } //打开开启动画 位移 透明度 ObjectAnimator translationAnimator = ObjectAnimator.ofFloat(mMenuContentView, "translationY", 0, -menuContainerHeight); translationAnimator.setDuration(DURATIOIN_TIME); translationAnimator.start(); ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mShadowView, "alpha", 1f, 0); alphaAnimator.setDuration(DURATIOIN_TIME); //要等关闭动画执行完才能去隐藏菜单 alphaAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); View menuView = mMenuContentView.getChildAt(mCurentPosition); menuView.setVisibility(GONE); mCurentPosition = -1; mShadowView.setVisibility(GONE); mAnimatorExecute = false; } @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); mAnimatorExecute = true; mAdapter.menuClose(mMenuTabView.getChildAt(mCurentPosition)); } }); alphaAnimator.start(); } /** * 打开menu * * @param position */ public void openMenu(final int position, final View tabView) { if (mAnimatorExecute) { return; } mShadowView.setVisibility(VISIBLE); //获取当前位置,去显示当前菜单 View menuView = mMenuContentView.getChildAt(position); menuView.setVisibility(VISIBLE); //打开开启动画 位移 透明度 ObjectAnimator translationAnimator = ObjectAnimator.ofFloat(mMenuContentView, "translationY", -menuContainerHeight, 0); translationAnimator.setDuration(DURATIOIN_TIME); translationAnimator.start(); ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mShadowView, "alpha", 0, 1f); alphaAnimator.setDuration(DURATIOIN_TIME); alphaAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); mAnimatorExecute = true; mAdapter.menuOpen(tabView); } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mCurentPosition = position; mAnimatorExecute = false; } }); alphaAnimator.start(); } @Override public void onClick(View v) { closeMenu(); } }收缩和展开下拉的动画也就直接在里面实现了;接下来就是监听菜单内容的点击及回调,还有就是当点击菜单内容后,菜单内容也要关闭掉;点击动作是ListMenuAdapter发生的,关闭菜单内容是在ListDataScreenView中发生的,是在两个不同的类中,这里实现的方式有两种:
第一种:在时候的时候将ListDataScreenView作为参数通过ListMenuAdapter的构造方法传入,然后触发点击的时候调用ListDataScreenView里面的关闭菜单内容方法,进行关闭;
第二种:模仿ListView那样使用观察者方式实现菜单内容的关闭;
这里采用的是第二种方式,建立一个抽象的观察者;
/** * Created by Administrator on 2018/1/25. * 菜单内容关闭的观察者 */ public abstract class MenuObserver { public abstract void closeMenu(); }
在BaseMenuAdapter中提供注册观察者(registerDataSetObserver(MenuObserver observer))和注销观察者(unregisterDataSetObserver(MenuObserver observer))在setAdapter中进行注册和注销的调用,这样就实现了,直接使用就可以了;
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.listviewchoice.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!内容"
android:layout_centerInParent="true"/>
<com.listviewchoice.ListDataScreenView
android:id="@+id/list_data_screen_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
public class MainActivity extends AppCompatActivity { private ListDataScreenView screenView; private ListMenuAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); List<String> list=getData(); screenView = (ListDataScreenView) findViewById(R.id.list_data_screen_view); adapter = new ListMenuAdapter(this,list); screenView.setAdapter(adapter); adapter.setListener(new ListMenuAdapter.ListMenuListener() { @Override public void listMenuClick(List<String> mItems, int position) { Toast.makeText(MainActivity.this,mItems.get(position),Toast.LENGTH_LONG).show(); } }); } public List<String> getData() { List<String> data=new ArrayList<>(); data.add("类型"); data.add("品牌"); data.add("价格"); data.add("更多"); return data; } }
在建立ListDataScreenView的适配器的时候需要注意getMenuView(final int position, ViewGroup parent)方法,需要根据自己的需要去实例化布局文件,并将其添加进来;
public class ListMenuAdapter extends BaseMenuAdapter {
private List<String> mItems;
private Context mContext;
private ListMenuListener listener;
public ListMenuAdapter( Context mContext,List<String> mItems) {
this.mItems = mItems;
this.mContext = mContext;
}
@Override
public int getCount() {
return mItems.size();
}
@Override
public View getTabView(final int position, ViewGroup parent) {
final View tabView = LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_tab, parent, false);
TextView tabViewTv = (TextView) tabView.findViewById(R.id.tab_view_tv);
tabViewTv.setTextColor(Color.BLACK);
tabViewTv.setText(mItems.get(position));
return tabView;
}
@Override
public View getMenuView(final int position, ViewGroup parent) {
String mItem = mItems.get(position);
View menuView = null;
if(mItem.equals("品牌")||mItem.equals("更多")){
menuView = LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_menu_list, parent, false);
final List<String> data = getData(mItem);
RecyclerView recyclerView = (RecyclerView) menuView.findViewById(R.id.recycler_view);
//设置布局管理器
recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
ListContentAdapter contentAdapter=new ListContentAdapter(mContext,data);
//设置adapter
recyclerView.setAdapter(contentAdapter);
contentAdapter.setListener(new ListContentAdapter.OnItemClicListener() {
@Override
public void listMenuClick(List<String> list, int position) {
//观察者
closeMenu();
if (listener != null) {
listener.listMenuClick(data, position);
}
}
});
}else{
menuView = LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_menu, parent, false);
TextView menuViewTv = (TextView) menuView.findViewById(R.id.menu_view_tv);
menuViewTv.setText(mItem);
//点击事件
menuViewTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//观察者
closeMenu();
if (listener != null) {
listener.listMenuClick(mItems, position);
}
}
});
}
return menuView;
}
private List<String> getData(String mItem) {
List<String> list=new ArrayList<>();
if(mItem.equals("品牌")){
for (int i=0;i<20;i++){
list.add("品牌"+i);
}
}else{
for (int i=0;i<20;i++){
list.add("更多"+i);
}
}
return list;
}
@Override
public void menuOpen(View tabView) {
super.menuOpen(tabView);
TextView tabTv = (TextView) tabView;
tabTv.setTextColor(Color.RED);
}
@Override
public void menuClose(View tabView) {
super.menuClose(tabView);
TextView tabTv = (TextView) tabView;
tabTv.setTextColor(Color.BLACK);
}
public void setListener(ListMenuListener listener) {
this.listener = listener;
}
interface ListMenuListener {
void listMenuClick(List<String> list, int position);
}
}下面这个的话是RecyclerView的适配器;
public class ListContentAdapter extends RecyclerView.Adapter<ListContentAdapter.ContentHolder>{
private Context context;
private List<String> datas;
private OnItemClicListener listener;
public ListContentAdapter(Context context, List<String> datas) {
this.context = context;
this.datas = datas;
}
@Override
public ContentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
ContentHolder holder=new ContentHolder(view);
return holder;
}
@Override
public void onBindViewHolder(ContentHolder holder, final int position) {
holder.item.setText(datas.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(listener!=null){
listener.listMenuClick(datas,position);
}
}
});
}
@Override
public int getItemCount() {
return datas.size();
}
class ContentHolder extends RecyclerView.ViewHolder{
TextView item;
public ContentHolder(View itemView) {
super(itemView);
item= (TextView) itemView.findViewById(R.id.item);
}
}
public void setListener(OnItemClicListener listener) {
this.listener = listener;
}
interface OnItemClicListener{
void listMenuClick(List<String> list, int position);
}
}这样子整个的大致效果就实现了,下面是源码的链接,可以根据项目的需要对源码进行修改。
源码地址:
https://pan.baidu.com/s/1nwI0GvZ
相关文章推荐
- 尝试用React写几个通用组件 - 带搜索功能的下拉列表,开关切换按钮,弹出框
- jquery+json 通用三级联动下拉列表效果代码
- jquery+json 通用三级联动下拉列表
- 通用三级联动下拉列表
- 打造完美SWT日期选择控件:(四)下拉列表的整体设计
- 通用四级联动下拉列表
- 【项目分析】通用的枚举类型的下拉列表绑定(3)
- js操作select下拉列表的一些通用代码
- js操作select下拉列表的一些通用代码
- js操作select下拉列表的一些通用代码
- ASP.NET.MVC--注解和反射将Enum转换List<SelectListItem>为@Html.DropDownListFor()下拉列表的通用方法
- js操作select下拉列表的一些通用代码
- jquery+json 通用三级联动下拉列表
- PopupWindow+ReycyclerView打造优雅的下拉列表
- 在web上打造可输入下拉列表
- 打造完美SWT日期选择控件:(三)看上去像下拉列表
- 下拉列表---demo---bai
- Struts2与下拉列表绑定的取值问题
- Qt之实现下拉列表菜单
- 带checkbox的下拉列表 很好看